I try to get an object like:
var result = [{ "surveyCode":"C", "count": 1}, {"surveyCode":"A", count: 4}]
by joining the two objects on surveyCode and counting the items in object a for each selected surveyCode.
I also can't link a map after merge. Any thoughts?
My attempt is:
var a = [{"id":319268,"surveyCode":"A", "field3": 4},
{"id":319269,"surveyCode":"A", "field3": 4},
{"id":268393,"surveyCode":"A", "field3": 4},
{"id":319266,"surveyCode":"A", "field3": 5},
{"id":319267,"surveyCode":"C", "field3": 4},
{"id":319267,"surveyCode":"B", "field3": 5}];
var b = [{"surveyCode":"C"},{"surveyCode":"A"}]
var merge = function() {
var obj = {},
i = 0,
il = arguments.length,
key;
for (; i < il; i++) {
for (key in arguments[i]) {
if (arguments[i].hasOwnProperty(key)) {
obj[key] = arguments[i][key];
}
}
}
return obj;
};
function groupBy(data, property) {
return data.reduce((acc, obj) => {
const key = obj[property];
if (!acc[key]) {
acc[key] = 0;
}
acc[key]++;
return acc;
}, {});
};
var allGroupedBySurveyCode = groupBy(a, 'surveyCode'); // returns [{ "A": 4}, {"B":1}, {"C": 1}]
var keepOnlyJoinedSurveyCodes = merge(c, allGroupedBySurveyCode); // doesn't work - expects common key e.g. surveyCode, not A.
You could count the occurance of surveyCode and map the count by the grouped array.
var data = [{ id: 319268, surveyCode: "A", field3: 4 }, { id: 319269, surveyCode: "A", field3: 4 }, { id: 268393, surveyCode: "A", field3: 4 }, { id: 319266, surveyCode: "A", field3: 5 }, { id: 319267, surveyCode: "C", field3: 4 }, { id: 319267, surveyCode: "B", field3: 5 }],
groups = [{ surveyCode: "C" }, { surveyCode: "A" }],
counts = data.reduce((r, { surveyCode }) => (r[surveyCode] = (r[surveyCode] || 0) + 1, r), {}),
result = groups.map(({ surveyCode }) => ({ surveyCode, count: counts[surveyCode] }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.map and Array.reduce to count the number of data
const a = [{
id: 319268,
surveyCode: 'A',
field3: 4,
},
{
id: 319269,
surveyCode: 'A',
field3: 4,
},
{
id: 268393,
surveyCode: 'A',
field3: 4,
},
{
id: 319266,
surveyCode: 'A',
field3: 5,
},
{
id: 319267,
surveyCode: 'C',
field3: 4,
}
];
const b = [{
surveyCode: 'C',
}, {
surveyCode: 'A',
}];
// We are going to create a new array named "count"
// the array will have the same number of entry as 'b'
// Array.map will loop on every item of the 'b' array and execute a function
// the result of the function for each entry will be the entry of the
// new array
const count = b.map(x => ({
// we copy all the key of the 'b' entry on the new element
...x,
// we are going to count the number of time the surveyCode is
// present on the 'a' array, for this we use of Array.reduce
// Array.reduce will start with a value and loop on every entry of 'a'
// What we do is simple, is the surveyCode the same ?
// > yes? count +1, > no? count +0
count: a.reduce((tmp, {
surveyCode,
}) => (surveyCode === x.surveyCode ? tmp + 1 : tmp), 0),
}));
console.log(count);
Irrelevant :
When you are coding in es6+ try to be consistent. Use of const and let instead of var which is legacy javascript. The use of Array.reduce tells me that you are not coding legacy.
Any issue using lodash?
I'd probably reach straight for:
https://lodash.com/docs/#countBy
You could map over the survey codes and count each entry in a for that particular survey code. Code would look something like this
const a = [
{id: 319268, surveyCode: 'A', field3: 4},
{id: 319269, surveyCode: 'A', field3: 4},
{id: 268393, surveyCode: 'A', field3: 4},
{id: 319266, surveyCode: 'A', field3: 5},
{id: 319267, surveyCode: 'C', field3: 4},
{id: 319267, surveyCode: 'B', field3: 5}
];
const b = [{surveyCode: 'C'}, {surveyCode: 'A'}];
const result = b.map((item) => {
return {...item, count: a.filter(({surveyCode}) => surveyCode === item.surveyCode).length};
});
You can try using reduce.
Loop over data, filter data based on array b and property
Check if passed property from current element is present on op or not, if present increase count by 1 else set it with desired value and then increase count by 1
Take the values
var a = [{"id":319268,"surveyCode":"A", "field3": 4},
{"id":319269,"surveyCode":"A", "field3": 4},
{"id":268393,"surveyCode":"A", "field3": 4},
{"id":319266,"surveyCode":"A", "field3": 5},
{"id":319267,"surveyCode":"C", "field3": 4},
{"id":319267,"surveyCode":"B", "field3": 5}];
function groupBy(data, property) {
var b = [{ "surveyCode": "C" }, { "surveyCode": "A" }].map(v => v[property])
let filteredData = data.filter(v => b.includes(v[property]))
return Object.values(filteredData.reduce((op, inp) => {
op[inp[property]] = op[inp[property]] || {
[property]: inp[property],
count: 0
}
op[inp[property]].count++
return op
}, {}))
}
console.log(groupBy(a, 'surveyCode'))
Related
If I have the following
arr = [
{key: "a",
values : [{key: "aa", value: 2}, {key: "bb", value: 5}]},
{key: "b",
values : [{key: "cc", value: 7}, {key: "dd", value: 3}]}
]
How to use reduce in javascript to find the maximum from the nested objects? The answer should be 7 in the above case.
I currently am able to use a loop to achieve this:
let max = 0;
let findDataMax = function(d) {
for (let i = 0; i < d.length; i++) {
let currArr = d[i].values;
let tempArr = []
currArr.forEach((d) => tempArr.push(+d.value));
if (Math.max(...tempArr) > max) {
max = Math.max(...tempArr);
}
}
}
let arr = [
{key: "a", values : [{key: "aa", value: 2}, {key: "bb", value: 5}]},
{key: "b",values : [{key: "cc", value: 7}, {key: "dd", value: 3}]}
];
findDataMax(arr);
console.log(max);
I would prefer to use other methods other than reduce for this, but if you have to, then you can set the accumulator as -Infinity to begin with (this way any value compared with the accumulator will be bigger than -Infinity). For each object in your array, you can find the max by mapping the array of values to an array of value numbers from each object, and then spreading these numbers into a call to Math.max(). You can then compare whether or not this is larger than the current maximum, and if it is, return that as the new value to use as the accumulator, otherwise, use the old accumulator value:
const arr = [ {key: "a", values : [{ key: "aa", value: 2}, { key: "bb",value: 5}]}, {key: "b", values : [{ key: "cc", value: 7}, { key: "dd", value: 3}]} ];
const max = arr.reduce((max, {values}) => {
const newMax = Math.max(...values.map(({value}) => value));
return newMax > max ? newMax : max;
}, -Infinity);
console.log(max);
As previously mentioned, I would probably use a different approach to .reduce(), such as .flatMap() to grab all object value numbers, which you can then spread into a call to Math.max():
const arr = [ {key: "a", values : [{ key: "aa", value: 2}, { key: "bb",value: 5}]}, {key: "b", values : [{ key: "cc", value: 7}, { key: "dd", value: 3}]} ];
const max = Math.max(...arr.flatMap(({values}) => values.map(({value}) => value)));
console.log(max);
I don't know if the use of the reduce function is a clean solution for this problem but here you have it:
const arr = [{ key: 'a', values: [{ key: 'aa', value: 2 }, { key: 'bb', value: 5 }] }, { key: 'b', values: [{ key: 'cc', value: 7 }, { key: 'dd', value: 3 }] }];
// O(n * b)
const maxValue = arr.reduce((prev, item) => item
.values.reduce((subPrev, subItem) => (subItem.value > subPrev ? subItem.value : subPrev), prev), 0);
console.log(maxValue); // 7
I have an array of Objects as below.
var options = [
{value: 'b', label: "test1"},
{value: 'a', label: "test12"},
{value: 'c', label: "test123"}
]
and an array of ranks as below.
var ranks = [
{rank: 1, value: "a"},
{rank: 2, value: "b"},
{rank: 3, value: "c"}
]
Now I need to sort the options array according to the rank property in ranks array
Try this:
var options = [
{value: 'b', label: "test1"},
{value: 'a', label: "test12"},
{value: 'c', label: "test123"}
]
var ranks = [
{rank: 1, value: "a"},
{rank: 2, value: "b"},
{rank: 3, value: "c"}
]
options.sort((a, b) => {
return ranks.find(_ => _.value === a.value).rank - ranks.find(_ => _.value === b.value).rank
})
console.log (options)
With ES6, you could use a Map and take this as closure for sorting.
var options = [{ value: 'b', label: "test1" }, { value: 'a', label: "test12" }, { value: 'c', label: "test123" }],
ranks = [{ rank: 1, value: "a" }, { rank: 2, value: "b" }, { rank: 3, value: "c" }];
options.sort(
(m => ({ value: a }, { value: b }) => m.get(a) - m.get(b))
(ranks.reduce((m, { rank, value }) => m.set(value, rank), new Map))
);
console.log(options);
To bring down the complexity and improve performance, you can first create a temp object/map with key as obj.value and rank as value. Then simply use Array.sort to sort on the basis of rank stored in the temp object.
let options = [{value: 'b', label: "test1"},{value: 'a', label: "test12"},{value: 'c', label: "test123"}];
let ranks = [{rank: 1, value: "a"},{rank: 2, value: "b"},{rank: 3, value: "c"}];
let temp = ranks.reduce((a,c) => Object.assign(a,{[c.value]:c.rank}), {});
options.sort((a,b) => temp[a.value] - temp[b.value]);
console.log(options);
var b = ["text1", "text2"];
var a = [
{name: "text3", value: 2},
{name: "text4", value: 7},
{name: "text1", value: 4}
];
There is a variety of Lodash functions that I tried, but none of them returning what I want to achieve.
What I want is:
var c = ["text1"]; // uniques from a compared to b
var d = [
{name: "text3", value: 2},
{name: "text4", value: 7}
]; // uniques from b compared to b
You could filter the array and push not unique items.
var b = ["text1", "text2"],
a = [{ name: "text3", value: 2 }, { name: "text4", value: 7 }, { name: "text1", value: 4 }],
c = [],
d = a.filter(({ name }) => !b.includes(name) || !c.push(name));
console.log(c);
console.log(d);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var c = _.reduce(a, (accumulator, item) => {
if(b.indexOf(item.name) !== -1)
accumulator.push(item.name)
return accumulator
}, [])
var d = _.filter(a, (item) => b.indexOf(item.name) === -1)
Im new in JS and I hope you help me) I need to transform array of objects in this way:
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
...
]
const transformedArr = [
{key1: 1},
{key2: 1},
{key2: 2},
{key2: 3},
{key2: 4},
....
]
How can I do it?
Since you are new to JS ..
Good Old JS with for loops might be easy for you to understand
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
]
const transformedArr =[]
for(var i = 0 ; i < (arr.length); i++){
var valArr = arr[i].value
if( Array.isArray(valArr) ){ // to check if the value part is an array
for(var j=0 ; j < valArr.length ; j++){
transformedArr.push({id: arr[i].id,value:valArr[j] })
}
}else{
transformedArr.push({id: arr[i].id,value:valArr })
}
}
console.log(transformedArr)
You can use ES6 spread syntax ... with map() method.
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
]
var result = [].concat(...arr.map(({id, value}) => {
return Array.isArray(value) ? value.map(e => ({[id]: e})) : {[id]: value}
}))
console.log(result)
You can also use reduce() instead of map() method.
const arr = [
{id: 'key1', value: 1 },
{id: 'key2', value: [1,2,3,4]}
]
var result = arr.reduce(function(r, {id, value}) {
r = r.concat(Array.isArray(value) ? value.map(e => ({[id]: e})) : {[id]: value})
return r;
}, [])
console.log(result)
This proposal features Array.concat, because it add items without array and arrays to an array.
const
array = [{ id: 'key1', value: 1 }, { id: 'key2', value: [1, 2, 3, 4] }],
result = array.reduce(
(r, a) => r.concat(
[].concat(a.value).map(
v => ({ [a.id]: v })
)
),
[]
);
console.log(result);
Suppose I have response array from my web-service like this
Array1 = [
0:[{name:A,count:2,hours:3},{name:B,count:3,hours:3},{name:C,count:2,hours:4}]
1:[{name:A,count:3,hours:4},{name:B,count:3,hours:3},{name:C,count:2,hours:2}]
2:[{name:A,count:3,hours:1},{name:B,count:3,hours:4},{name:C,count:2,hours:5},{name:D,count:2,hours:3}]
];
and
Array2 = ['A','B','C','D'];
In my output, I need to check for 24 hours.But for simplicity,we will now take only hours to 5.Each sub array from Array1 belong to one user. Now,I must group array by hours and activity. Means I need to make key as name and value as total count of name in each hours.
And output must look like this
var output = [
{hours:1, A:3, B:0, C:0, D:0},
{hours:2, A:0, B:0, C:2, D:0},
{hours:3, A:2, B:6, C:0, D:2},
{hours:4, A:3, B:3, C:2, D:0},
{hours:5, A:0, B:0, C:2, D:0},
];
And my try below
angular.forEach(Array1 , function(array){ //angularjs foreach
array.forEach(function(a){
obj[a.hours] = obj[a.hours]||[0];
if(obj[a.hours].hasOwnProperty(a.name)){
obj[a.hours][a.name] = parseInt(obj[a.hours][a.name]) + parseInt(a.count);
}else{
obj[a.hours][a.name] = parseInt(a.count);
}
obj[a.hours]['hours'] = a.hours;
});
});
where I try to group my array with hours and name as key and total count as value. What more I try is
var startHour = 1;
var endHours = 5;
var newData = []; //#TODO
newData.push(obj); //#TODO
for(i= startDate; i < endDate; i++) {
var found = newData.some(function(el){
//el.forEach(function(a){
$.each(el, function(key, value) {
if(value.hours){
return value.hours === i;
}
});
});
if(!found){
console.log(i + "not found");
newData.push([{'hours':i}]);
}
}
console.log(newData);
But every time I am in not found.As my output I need to push key-value pairs name and count 0 if not exit. But first I try to push only hours if not exists. Can anyone suggest me what I did wrong. I am back-end programmer so,I don't have good knowledge of JavaScript.
Thank You.
You could use a hash table for the reference of the right hour object and iterate the data with Array#forEach. Later sort the result array for the wanted order.
var array1 = [[{ name: 'A', count: 2, hours: 3 }, { name: 'B', count: 3, hours: 3 }, { name: 'C', count: 2, hours: 4 }], [{ name: 'A', count: 3, hours: 4 }, { name: 'B', count: 3, hours: 3 }, { name: 'C', count: 2, hours: 2 }], [{ name: 'A', count: 3, hours: 1 }, { name: 'B', count: 3, hours: 4 }, { name: 'C', count: 2, hours: 5 }, { name: 'D', count: 2, hours: 3 }]],
array2 = ['A', 'B', 'C', 'D'],
grouped = [];
array1.forEach(function (a) {
a.forEach(function (b) {
if (!this[b.hours]) {
this[b.hours] = { hours: b.hours };
array2.forEach(function (c) { this[c] = 0; }, this[b.hours]);
grouped.push(this[b.hours]);
}
this[b.hours][b.name] += b.count;
}, this);
}, Object.create(null));
grouped.sort(function (a, b) { return a.hours - b.hours; });
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Proposal with 24h array, with zero based hour.
var array1 = [[{ name: 'A', count: 2, hours: 3 }, { name: 'B', count: 3, hours: 3 }, { name: 'C', count: 2, hours: 4 }], [{ name: 'A', count: 3, hours: 4 }, { name: 'B', count: 3, hours: 3 }, { name: 'C', count: 2, hours: 2 }], [{ name: 'A', count: 3, hours: 1 }, { name: 'B', count: 3, hours: 4 }, { name: 'C', count: 2, hours: 5 }, { name: 'D', count: 2, hours: 3 }]],
array2 = ['A', 'B', 'C', 'D'],
grouped = Array.apply(null, { length: 24 }).map(function (_, i) {
var o = { hours: i }
array2.forEach(function (a) { this[a] = 0; }, o);
return o;
});
array1.forEach(function (a) {
a.forEach(function (b) {
grouped[b.hours][b.name] += b.count;
}, this);
}, Object.create(null));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could start on a array of object (1 object per hour). And pushing inside this array the new additional count coresponding the hour and name :
var Array1 = [[{name: "A",count: 2,hours: 3},{name: "B",count: 3,hours: 3},{name: "C",count: 2,hours: 4}],[{name: "A",count: 3,hours: 4},{name: "B",count: 3,hours: 3},{name: "C",count: 2,hours: 2}],[{name: "A",count: 3,hours: 1},{name: "B",count: 3,hours: 4},{name: "C",count: 2,hours: 5},{name: "D",count: 2,hours: 3}]];
var Array2 = ['A','B','C','D'];
var res = [1,2,3,4,5].map(x => ({"hours": x})).map(x => {
Array2.forEach(y => x[y] = 0);
return x;
});
Array1.reduce((a,b) => a.concat(b)).forEach(x => {
if (Array2.indexOf(x.name) !== -1)
res[x.hours - 1][x.name] += x.count;
})
console.log(res);