I apologize if my title sounds so confusing, as I don't know how to exactly word it. Basically, this is what I'm trying to do.
For example, I have the following array:
var sourceArray = [
{ question: "Question 1", inputType: "radio", answer: "Answer 1", id: "1" },
{ question: "Question 1", inputType: "radio", answer: "Answer 2", id: "2" },
{ question: "Question 1", inputType: "radio", answer: "Answer 3", id: "3" },
{ question: "Question 2", inputType: "radio", answer: "Answer 1", id: "4" },
{ question: "Question 2", inputType: "radio", answer: "Answer 2", id: "5" },
{ question: "Question 2", inputType: "radio", answer: "Answer 3", id: "6" }
]
I want to restructure this so that it'll look like this:
var newArray = [
{
question: "Question 1", inputType: "radio",
choices: [{answer: "Answer 1", id: "1"},
{answer: "Answer 2", id: "2"},
{answer: "Answer 3", id: "3"}
]},
{
question: "Question 2", inputType: "radio",
choices: [{answer: "Answer 1", id: "4"},
{answer: "Answer 2", id: "5"},
{answer: "Answer 3", id: "6"}
]}
]
Answers are grouped by question, so if the next question in the sourceArray is the same with the current, it will push the answers into the choices array.
Is this possible in AngularJS using angular.forEach?
Any help will be appreciated.
Thank you as always for your responses.
Here you go. See the working example below:
// Add a hepler method in the Array to find the value
Array.prototype.find = function(key, value) {
var index = -1;
angular.forEach(this, function(item, i) {
if (item[key] === value) {
index = i;
}
});
return this[index];
};
var app = angular.module("sa", []);
app.controller("FooController", function($scope) {
$scope.sourceArray = [{
question: "Question 1",
inputType: "radio",
answer: "Answer 1",
id: "1"
}, {
question: "Question 1",
inputType: "radio",
answer: "Answer 2",
id: "2"
}, {
question: "Question 1",
inputType: "radio",
answer: "Answer 3",
id: "3"
}, {
question: "Question 2",
inputType: "radio",
answer: "Answer 1",
id: "4"
}, {
question: "Question 2",
inputType: "radio",
answer: "Answer 2",
id: "5"
}, {
question: "Question 2",
inputType: "radio",
answer: "Answer 3",
id: "6"
}];
$scope.newArray = [];
$scope.convert = function() {
// Iterate array
angular.forEach($scope.sourceArray, function(item) {
// Check if the question alrady exists
if (!$scope.newArray.find('question', item.question)) {
// If not, push the question to the array with empty choices
$scope.newArray.push({
question: item.question,
inputType: item.inputType,
choices: []
});
}
var newArrayItem = $scope.newArray.find('question', item.question);
// Push the choices
newArrayItem.choices.push({
answer: item.answer,
id: item.id
});
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<div ng-app="sa" ng-controller="FooController">
Before:
<br>{{sourceArray | json}}
<br>
convert
<br>After:
<br>{{newArray | json}}
</div>
You can group together using a for loop,
Here is the code:
function transformArr(orig) {
var newArr = [],
answers = {},
newItem, i, j, cur;
for (i = 0, j = orig.length; i < j; i++) {
cur = orig[i];
if (!(cur.question in answers)) {
answers[cur.question] = {question: cur.question, answers: [],inputType : cur.inputType,id :cur.id};
newArr.push(answers[cur.question]);
}
answers[cur.question].answers.push(cur.answer);
}
return newArr;
}
Working App
You need to filter in the sourceArray, the two Questions List as below:
var question1 = $filter('filter')(sourceArray, {
question: 'Question 1'
});
var question2 = $filter('filter')(sourceArray, {
question: 'Question 2'
});
After that, you should create a new data of array as below:
$scope.questionData = [{
"question": "Question 1",
"inputType": "radio",
"choices": []
}, {
"question": "Question 2",
"inputType": "radio",
"choices": []
}];
Then, you should run loops over the two filtered a variables into the new Array of object:
1):
angular.forEach(question1, function(value, key) {
if (value.question == $scope.questionData[0].question) {
$scope.questionData[0].choices.push({
'answer': value.answer,
'id': value.id
});
}
});
2):
angular.forEach(question2, function(value, key) {
if (value.question == $scope.questionData[1].question) {
$scope.questionData[1].choices.push({
'answer': value.answer,
'id': value.id
});
}
});
Refer here for demo:
Related
I have JSON data that looks like this:
[
{
"id": 1,
"tags": [
"Test 1",
"Test 2",
"Test 3"
]
},
{
"id": 2,
"tags": [
"Test 2",
"Test 3",
"Test 4"
]
},
{
"id": 3,
"tags": [
"Test 3",
"Test 4"
]
}
]
I would like to transform this into data that looks like this:
[
{
"name": "Test 1",
"count": 1
},
{
"name": "Test 2",
"count": 2
},
{
"name": "Test 3",
"count": 3
},
{
"name": "Test 4",
"count": 1
}
]
I can think of some brute ways to do this, but I'm hoping there is something more performant and a little sexier? Possibly using .groupBy() or .reduce()?
Thanks for taking the time to check out my question.
I would:
parse the json
gather all tags in an array
count occurences using one of the approaches in Counting the occurrences / frequency of array elements
interface Item {
id: number,
tags: string[]
}
function countOccurences(a: string[]) {
return a.reduce(function (acc: {[key: string]: number}, curr: string) {
acc[curr] ??= 0;
acc[curr]++;
return acc;
}, {});
}
const data: Item[] = JSON.parse(json);
const tagOccurences = countOccurences(data.flatMap(o => o.tags))
Playground link
You can use reduce inside reduce to group the tags.
const array = [{
id: 1,
tags: ['Test 1', 'Test 2', 'Test 3'],
},
{
id: 2,
tags: ['Test 2', 'Test 3', 'Test 4'],
},
{
id: 3,
tags: ['Test 3', 'Test 4'],
},
];
const frequencies = Object.values(array.reduce((acc, curr) =>
curr.tags.reduce(
(nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count++,nAcc),
acc
), {}
));
console.log(frequencies);
In TypeScript:
const array = [{
id: 1,
tags: ['Test 1', 'Test 2', 'Test 3'],
},
{
id: 2,
tags: ['Test 2', 'Test 3', 'Test 4'],
},
{
id: 3,
tags: ['Test 3', 'Test 4'],
},
];
type Frequency = {
name: string,
count: number
}
const frequencies = Object.values(array.reduce((acc, curr) =>
curr.tags.reduce(
(nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count++,nAcc),
acc
), {} as Record<string, Frequency>
));
console.log(frequencies);
Playground
Using for...of iteration and a Map as a cache is a very straightforward approach... and sexy.
TS Playground
type TagsWithId = {
id: number;
tags: string[];
};
type TagCount = {
count: number;
name: string;
};
function verySexyTagCounter (input: TagsWithId[]): TagCount[] {
const map = new Map<string, number>();
for (const {tags} of input) {
for (const name of tags) {
map.set(name, (map.get(name) ?? 0) + 1);
}
}
return [...map.entries()].map(([name, count]) => ({name, count}));
}
const json = `[{"id":1,"tags":["Test 1","Test 2","Test 3"]},{"id":2,"tags":["Test 2","Test 3","Test 4"]},{"id":3,"tags":["Test 3","Test 4"]}]`;
const input: TagsWithId[] = JSON.parse(json);
const result = verySexyTagCounter(input);
console.log(result);
var array = [];
$http.get('v1/someset')
.then(function(data) {
if(data){
mysize = data.data.size;
mysize.filter(function(e) {
array.push(e.name);
})
}
}).catch(function(error){
console.log("Error", error);
})
mysize =>
{id: "1", some_id: "1", name: "Small", active: "1"},
{id: "2", some_id: "1", name: "Medium", active: "1"}
{id: "3", some_id: "1", name: "Large", active: "1"}
{id: "4", some_id: "1", name: "X-large", active: "1"}
{id: "5", some_id: "1", name: "XX-large", active: "1"}
{id: "6", some_id: "1", name: "XXX-large", active: "1"}
How to get name from http get angularjs
It should only return
['Small', Medium, 'Large', 'X-large', 'XX-large', 'XXX-large' ]
and could accessible outside then
const mysize = [
{id: "1", some_id: "1", name: "Small", active: "1"},
{id: "2", some_id: "1", name: "Medium", active: "1"},
{id: "3", some_id: "1", name: "Large", active: "1"},
{id: "4", some_id: "1", name: "X-large", active: "1"},
{id: "5", some_id: "1", name: "XX-large", active: "1"},
{id: "6", some_id: "1", name: "XXX-large", active: "1"},
]
const names = mysize.map(i => i.name)
you Should use map to create the arr type you wish
var array = [];
$http.get('v1/someset')
.then(function(data) {
if(data){
mysize = data.data.size;
//array is not the same as the array on the top
array = mysize.map(function(item){
return item.name
})
}
}).catch(function(error){
console.log("Error", error);
})
to return the data you should return a promise
apiService.js
var getData = $http.get('v1/someset')
.then(function(data) {
if(data){
var mysize = data.data.size;
var array = mysize.map(function(item){
return item.name
})
return Promise.resolve(array)
}
}).catch(function(error){
console.log("Error", error);
return Promise.reject(error)
})
and the one that calls the data from the controller should do this
scope.data = []
apiService.getData().then(function(data){
//scope is accessible here
scope.data = data
})
Currently I'm filtering data based from questions that have checked property value equals to true..
const data = [
{Name: "foo", X1: "1", X2: "1", Other: "Test1"},
{Name: "bar", X1: "2",X2: "2",Other: "Test2"},
{Name: "test",X1: "2",X2: "3",Other: "Test3"}
];
const questions = [{rows: {text: "Text 1", checked: true,fields: "1",column: "X1"}
}, {rows: {text: "Text 2", checked: true,fields: "2",column: "X1"}
}, {rows: {text: "Text 3", checked: false,fields: "1",column: "X2"}
}, {rows: {text: "Text 4", checked: false,fields: "2",column: "X2"}
}, {rows: {text: "Text 5", checked: false,fields: "3",column: "X2"}
}];
console.log(questionArr);
// console.log(dataArr);
const res = data.filter(d => questions.find(f => d[f.rows.column] === f.rows.fields && f.rows.checked));
which works but does not work when filtering the actual data below. I think there's a slight difference between the question object and the actual question object below.. What should be my filter code when accessing these kind of structure ?
I think this is what you're looking for. I matched the data structure to the image in your question. Let me know if I missed something.
const data = [
{ Name: "foo", X1: "1", X2: "1", Other: "Test1" },
{ Name: "bar", X1: "2", X2: "2", Other: "Test2" },
{ Name: "test", X1: "2", X2: "3", Other: "Test3" }
];
const questions = [
{ rows: [{ text: "Text 1", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 2", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 3", checked: false, fields: "1", column: "X2" }] },
{ rows: [{ text: "Text 4", checked: false, fields: "2", column: "X2" }] },
{ rows: [{ text: "Text 5", checked: false, fields: "3", column: "X2" }] }
];
const result = data.filter(function(item){
return questions.some(function(question){
return question.rows.some(function(row){
return (row.checked && item[row.column] === row.fields);
});
});
});
console.log(result);
The compact version
const result = data.filter((item) => questions.some((question) => question.rows.some((row) => (row.checked && item[row.column] === row.fields))));
With perf in mind
const data = [
{ Name: "foo", X1: "1", X2: "1", Other: "Test1" },
{ Name: "bar", X1: "2", X2: "2", Other: "Test2" },
{ Name: "test", X1: "2", X2: "3", Other: "Test3" }
];
const questions = [
{ rows: [{ text: "Text 1", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 2", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 3", checked: false, fields: "1", column: "X2" }] },
{ rows: [{ text: "Text 4", checked: false, fields: "2", column: "X2" }] },
{ rows: [{ text: "Text 5", checked: false, fields: "3", column: "X2" }] }
];
const result = {};
for(let a = 0, b = data.length; a < b; a++){
const item = data[a];
for(let c = 0, d = questions.length; c < d; c++){
const rows = questions[c].rows;
for(let e = 0, f = rows.length; e < f; e++){
const row = rows[e];
if(row.checked && item[row.column] === row.fields){
result[item.Name] = item;
break;
}
}
}
}
// this could be replaced with Object.values(result);
const matches = [];
for(let match in result){
matches.push(result[match]);
}
// not supported by IE yet
// Object.values(result);
console.log(matches);
What is the correct Underscore.js way to create a new object called items that consists of each of the item arrays. Where I could then make a POST of each item.name in one call?
var items = [];
item = [{
name: "item1",
desc: "this is a description of item 1",
qty: 1
},{
name: "item2",
desc: "this is a description of item 2",
qty: 5
},{
name: "item3",
desc: "this is a description of item 3",
qty: 3
}];
items.push(item);
If I understand the question correctly you want to convert an array of items to an object where each key is the name of the object
e.g.
{
item1: {
name: "item1",
desc: "this is a description of item 1",
qty: 1
},
item2: { ... },
item3: { ... },
}
If that's the case then you could use the object function that takes two parameters; the first being the list of property names and the second a list of the values:
var items = [{
name: "item1",
desc: "this is a description of item 1",
qty: 1
},{
name: "item2",
desc: "this is a description of item 2",
qty: 5
},{
name: "item3",
desc: "this is a description of item 3",
qty: 3
}
];
var itemsAsAnObject = _.object( _.pluck(items,'name'), items)
I have an array obj as following:
$scope.list = [
{
_id: "1111",
desc: "Desc 1",
image_url: "uploads/name-1.jpg",
name: "Name 1",
sequence: 0
},
{
_id: "1112",
desc: "Desc 2"
image_url: "uploads/name-2.jpg",
name: "Name 2",
sequence: 2
},
{
_id: "1113",
desc: "Desc 3",
image_url: "uploads/name-3.jpg",
name: "Name 3",
sequence: 3
},
{
_id: "1114",
desc: "Desc 4",
image_url: "uploads/name-4.jpg",
name: "Name 4",
sequence: 4
},
{
_id: "1115",
desc: "Desc 5",
image_url: "uploads/name-5.jpg",
name: "Name 5",
sequence: 0
},
{
_id: "1116",
desc: "Desc 6",
image_url: "uploads/name-6.jpg",
name: "Name 6",
sequence: 0
},
];
and HTML
<span data-ng-repeat="item in list | orderBy: 'sequence' ">{{item.sequence}}</span>
I've also tried orderBy: '-sequence', orderBy: 'sequence':true. orderBy: 'sequence':false. But the order doesn't seem to update!
Can someone please shed some light on this?
Your list is badly defined, as you're missing commas.
The code you posted does work, see here.
<span ng-repeat="item in list | orderBy: 'sequence' ">
{{item.sequence}}
</span>