Union of multiple objects based on key - javascript

What is the best way to merge all objects in my array to one and addition all value with the same key?
I tried to achieve this in es6 with the spread operator, but I have no success so far...
const array = [{
on: 1,
off: 1,
alarm: 1,
},{
on: 1,
off: 1,
alarm: 1,
},{
on: 1,
off: 1,
alarm: 1,
}];
const output = [{
on: 3,
off: 3,
alarm: 3,
}];

Another way, using the spread operator as you suggested:
const array = [
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 }
];
const output = array.reduce((res, o) => ({
...res,
...Object.fromEntries(
Object.entries(o).map(([k, v]) => [k, v + (res[k] || 0)])
)
}), {});
console.log(output);

Reduce the array, iterate each object entries with Array.forEach(), and assign / add to the respective key on the accumulator (acc):
const array = [{"on":1,"off":1,"alarm":1},{"on":2,"off":2,"alarm":2},{"on":3,"off":3,"alarm":3}];
const result = array.reduce((acc, o) => {
Object.entries(o).
forEach(([k, v]) => acc[k] = (acc[k] ?? 0) + v)
return acc;
}, {});
console.log(result);

OK, here is my two-pennies-worth:
const arr = [{ on: 1, off: 1, alarm: 1},
{on: 1,off: 1,alarm: 1},
{on: 1,off: 1,alarm: 1}];
const res={};
arr.forEach(c=>Object.entries(c).forEach(([k,v])=>res[k]=(res[k] || 0) + v) )
console.log(res)

array.reduce((acc, elem) => {
Object.keys(elem).forEach(key => {
if (acc[key]) {
acc[key] += elem[key];
} else {
acc[key] = elem[key];
}
});
return acc;
}, {})
will return an object {on: 3, off: 3, alarm: 3}

Other solutions appear more elegant, but this was the simplest for me to understand.
const array = [
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 },
{ on: 1, off: 1, alarm: 1 }
];
let output = { 'on':0, 'off':0, 'alarm':0 }
array.forEach( (elem,ndx) => { // console.log(ndx, elem)
output.on += elem.on;
output.off += elem.off;
output.alarm += elem.alarm;
});
console.log(JSON.stringify(output,null,2));

Related

Sum array of objects with dynamic keys - Javascript

I've got the following array
arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
Expected output: 9
And I would like to sum the properties the shortest way possible. The thing is that the object keys are always variable so i can't go for:
arr.reduce((acc,item) => acc+item.keyName)
Found this post but can't adapt the code to my solution either:
var data = [{ A: 4, B: 2 }, { A: 2, B: 1 }, { A: 3, B: 1 }, { A: 2, B: 1, C: 1 }],
result = data.reduce((r, o) => (Object.entries(o).forEach(([k, v]) => r[k] = (r[k] || 0) + v), r), {});
Thank you in advance
Here's my solution. Map through the array and flatten the properties values, then reduce them.
const arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
console.log(arr.flatMap(e => Object.values(e)).reduce((a, b) => a + b));
Use reduce twice, once on the outer array and once on the values of each object inside it.
const arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
];
const total = arr.reduce((gt, item) =>
gt + Object.values(item).reduce((t, sub) => t + sub, 0)
, 0);
console.log(total);
Maybe something like this?
let acc = {};
for (let o of arr) for (let key in o) {
acc[key] ??= 0;
acc[key] += o[key];
}
Not a one-liner though
You can do something like this:
total = arr.reduce((acc,item) => {
const values = Object.values(item)
values.forEach(item=> acc += item)
return acc;
}, 0);
Check I passed the first value (0) as the second parameter to reduce method.
Hope this would help:
arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
var sum = arr.map(item => {
var x = 0
for (const [key, value] of Object.entries(item)) {
x+=value
}
return x
}).reduce( (acc, curr) => acc + curr)
console.log(sum)
arr = [
{ 'Atencion Personalizada': 2, 'Caja': 3 },
{ 'Atencion Personalizada': 1 },
{ 'Tarifa Social': 3 }
]
var total = 0;
arr.forEach((x, i) =>
total += Object.values(x).reduce((a,b) => a+b)
);
console.log(total)
Solve it with this approach:
use JSON.stringify to get arr as string.
extract numbers with match with regex \d+ and flag g to return it as group.
map it to real numbers rather than string.
reduce it to the sum of all numbers.
JSON.stringify(arr) //'[{"Atencion Personalizada":2,"Caja":3},{"Atencion Personalizada":1},{"Tarifa Social":3}]'
.match(/\d+/g) //['2', '3', '1', '3']
.map(Number) //[2, 3, 1, 3]
.reduce((acc, n)=>acc+n, 0) //9

Find objects with same prop and return the update value with its associated prop [duplicate]

I have javascript array object as below. My need is to sum value base on seach id in the array object.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
],
For example sum of value for id 1 is 10 + 30 + 25 + 20 = 85 , It may be something link linq but I'm not sure in javascript. Thanks for all answers.
You can use a combination of filter and reduce to get the result you want:
sumOfId = (id) => array.filter(i => i.id === id).reduce((a, b) => a + b.val, 0);
Usage:
const sumOf1 = sumOfId(1); //85
Reading material:
Array.prototype.filter
Array.prototype.reduce
A way to do it with a traditional for loop
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sums = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
sums[obj.id] = sums[obj.id] === undefined ? 0 : sums[obj.id];
sums[obj.id] += parseInt(obj.val);
}
console.log(sums);
running example
You can use reduce() and findIndex()
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
let res = array.reduce((ac,a) => {
let ind = ac.findIndex(x => x.id === a.id);
ind === -1 ? ac.push(a) : ac[ind].val += a.val;
return ac;
},[])
console.log(res);
JS noob here ... I guess something like this should be here too :-)
let newArray = {}
array.forEach((e) => {
!newArray[e.id] && (newArray[e.id] = 0);
newArray[e.id] += e.val;
});
You can loop on the array and check the ids.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sum = 0;
var id = 1;
$.each(array, function(index, object){
if (object.id == id) {
sum += object.val;
}
});
console.log(sum);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Using Array#reduce and Map you can get the sum for each id like so. This also uses destructuring to have quicker access to properties.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
console.log(res.get(1));
console.log(res.get(2));
If you wanted to output all the sums, then you need to use Array#from
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
);
console.log(res);
If the format should be similar as to your original structure, you need to add a Array#map afterwards to transform it.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
).map(([id,sum])=>({id,sum}));
console.log(res);
You could take GroupBy from linq.js with a summing function.
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }],
result = Enumerable
.From(array)
.GroupBy(null, null, "{ id: $.id, sum: $$.Sum('$.val') }", "$.id")
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
Here is another option, introducing an Array.prototype.sum helper:
Array.prototype.sum = function (init = 0, fn = obj => obj) {
if (typeof init === 'function') {
fn = init;
init = 0;
}
return this.reduce(
(acc, ...fnArgs) => acc + fn(...fnArgs),
init
);
};
// .sum usage examples
console.log(
// sum simple values
[1, 2, 3].sum(),
// sum simple values with initial value
[1, 2, 3].sum(10),
// sum objects
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(obj => obj.a),
// sum objects with initial value
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(10, obj => obj.a),
// sum custom combinations
[{ amount: 1, price: 2 }, { amount: 3, price: 4 }]
.sum(product => product.amount * product.price)
);
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }];
// solutions
console.log(
array.filter(obj => obj.id === 1).sum(obj => obj.val),
array.filter(({id}) => id === 1).sum(({val}) => val),
array.sum(({id, val}) => id === 1 ? val : 0)
);
references:
Array.prototype.reduce
Array.prototype.filter
Arrow functions used in sum(obj => obj.val)
Object destructing assignment used in ({id}) => id === 1
Rest parameters used in (acc, ...fnArgs) => acc + fn(...fnArgs)
Conditional (ternary) operator used in id === 1 ? val : 0

What is the best way to merge nested arrays

I have an array of objects that looks like this:
[
{external_id: 1, items: [{k: 'v'}] },
{external_id: 2, items: [{k1: 'v1'}] },
{external_id: 1, items: [{k2: 'v2'}, {k3: 'v3'}] }
]
What I want to do is merge nested arrays based on external_id and return 'clean array' which will look like this:
[
{external_id: 1, items: [{k: 'v'}, {k2: 'v2'}, {k3: 'v3'}] },
{external_id: 2, items: [{k1: 'v1'}] }
]
So my question is what is the clean way to achieve that without using classic for-loop ?
Using reduce and sort
let data = [{
external_id: 1,
items: [{
k: 'v'
}]
},
{
external_id: 2,
items: [{
k1: 'v1'
}]
},
{
external_id: 1,
items: [{
k2: 'v2'
}, {
k3: 'v3'
}]
},
];
let ans = data
.sort(function(a, b) {
return a['external_id'] - b['external_id'];
})
.reduce((acc, currentObject) => {
const endObject = acc[acc.length - 1];
if (endObject && endObject['external_id'] === currentObject.external_id) {
endObject.items.push(...currentObject.items);
} else {
acc.push({ ...currentObject
});
}
return acc;
}, []);
console.log(ans);
You will have to loop. You can choose to loop with for, forEach, map, reduce, find, ...etc, but you'll have to loop.
Here is a way that creates a Map keyed by the external_id, and then populates that map with the items. Finally the values of that map represent the result:
let data = [{external_id: 1, items: [{k: 'v'}] },{external_id: 2, items: [{k1: 'v1'}] },{external_id: 1, items: [{k2: 'v2'}, {k3: 'v3'}] }];
let map = new Map(data.map(({external_id}) => [external_id, { external_id, items:[] }]));
data.forEach(o => map.get(o.external_id).items.push(...o.items));
let result = [...map.values()];
console.log(result);
using another object to group objects with same external_id property
let a = [
{external_id: 1, items: [{k: 'v'}] },
{external_id: 2, items: [{k1: 'v1'}] },
{external_id: 1, items: [{k2: 'v2'}, {k3: 'v3'}] }
]
let obj = {};
a.forEach( ({external_id, items}) => {
obj[external_id] ??= new Set;
items.map( i => {
obj[external_id].add(JSON.stringify(i));
});
});
a = Object.keys(obj).map( (n) => {
n = Number(n);
return {
external_id:n,
items: [...obj[n]].map(JSON.parse)
}
})

make array property to have index as namespace

I have problem making a new array of object. I want to transform this
[{
a: 1,
b: true
},{
a: 2,
b: false
}]
to
[{
a_1: 1
},{
a_2: 2
}]
I tried map
const result = a.map((o, i) => {
let row = []
i = ++i
row = {
[`a_${i}`]: o.a,
[`b_${i}`]: b.a
}
return row
})
but it returned this
[
{
"a_1": 1,
"b_1": true
},
{
"a_2": 2,
"b_2": false
}
]
How do I get this
[
{
"a_1": 1,
},{
"b_1": true
},{
"a_2": 2,
},
{
"b_2": false
}
]
I can flatten it but the property key has dynamic index, imagine it's not small size like this.
You can use map and Object.entries and flat
let arr = [{ a: 1, b: true }, { a: 2, b: false}]
const result = arr.map((o, i) => {
return Object.entries(o).map(([key, value]) => ({
[key + '_' + (i + 1)]: value
}))
}).flat()
console.log(result)
Also you can use Array.flatMap
let arr = [{ a: 1, b: true }, { a: 2, b: false}]
const result = arr.flatMap((o, i) => {
return Object.entries(o).map(([key, value]) => ({
[key + '_' + (i + 1)]: value
}))
})
console.log(result)
And you can use reduce:
let arr = [{a: 1,b: true},{a: 2,b: false}];
let brr= arr.reduce((acc,e, i)=>{
Object.entries(e).map(([key,value])=>{
acc.push({[`${key}_${i+1}`]:value})
});
return acc
},[])
console.log(brr)

How to sum value in javascript array object form specific search id?

I have javascript array object as below. My need is to sum value base on seach id in the array object.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
],
For example sum of value for id 1 is 10 + 30 + 25 + 20 = 85 , It may be something link linq but I'm not sure in javascript. Thanks for all answers.
You can use a combination of filter and reduce to get the result you want:
sumOfId = (id) => array.filter(i => i.id === id).reduce((a, b) => a + b.val, 0);
Usage:
const sumOf1 = sumOfId(1); //85
Reading material:
Array.prototype.filter
Array.prototype.reduce
A way to do it with a traditional for loop
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sums = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
sums[obj.id] = sums[obj.id] === undefined ? 0 : sums[obj.id];
sums[obj.id] += parseInt(obj.val);
}
console.log(sums);
running example
You can use reduce() and findIndex()
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
let res = array.reduce((ac,a) => {
let ind = ac.findIndex(x => x.id === a.id);
ind === -1 ? ac.push(a) : ac[ind].val += a.val;
return ac;
},[])
console.log(res);
JS noob here ... I guess something like this should be here too :-)
let newArray = {}
array.forEach((e) => {
!newArray[e.id] && (newArray[e.id] = 0);
newArray[e.id] += e.val;
});
You can loop on the array and check the ids.
var array = [
{ id: 1, val: 10 },
{ id: 2, val: 25 },
{ id: 3, val: 20 },
{ id: 1, val: 30 },
{ id: 1, val: 25 },
{ id: 2, val: 10 },
{ id: 1, val: 20 }
];
var sum = 0;
var id = 1;
$.each(array, function(index, object){
if (object.id == id) {
sum += object.val;
}
});
console.log(sum);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Using Array#reduce and Map you can get the sum for each id like so. This also uses destructuring to have quicker access to properties.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
console.log(res.get(1));
console.log(res.get(2));
If you wanted to output all the sums, then you need to use Array#from
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
);
console.log(res);
If the format should be similar as to your original structure, you need to add a Array#map afterwards to transform it.
const data=[{id:1,val:10},{id:2,val:25},{id:3,val:20},{id:1,val:30},{id:1,val:25},{id:2,val:10},{id:1,val:20}];
const res = Array.from(
data.reduce((a,{id,val})=>{
return a.set(id, (a.get(id)||0) + val);
}, new Map())
).map(([id,sum])=>({id,sum}));
console.log(res);
You could take GroupBy from linq.js with a summing function.
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }],
result = Enumerable
.From(array)
.GroupBy(null, null, "{ id: $.id, sum: $$.Sum('$.val') }", "$.id")
.ToArray();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
Here is another option, introducing an Array.prototype.sum helper:
Array.prototype.sum = function (init = 0, fn = obj => obj) {
if (typeof init === 'function') {
fn = init;
init = 0;
}
return this.reduce(
(acc, ...fnArgs) => acc + fn(...fnArgs),
init
);
};
// .sum usage examples
console.log(
// sum simple values
[1, 2, 3].sum(),
// sum simple values with initial value
[1, 2, 3].sum(10),
// sum objects
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(obj => obj.a),
// sum objects with initial value
[{ a: 1 }, { a: 2 }, { a: 3 }].sum(10, obj => obj.a),
// sum custom combinations
[{ amount: 1, price: 2 }, { amount: 3, price: 4 }]
.sum(product => product.amount * product.price)
);
var array = [{ id: 1, val: 10 }, { id: 2, val: 25 }, { id: 3, val: 20 }, { id: 1, val: 30 }, { id: 1, val: 25 }, { id: 2, val: 10 }, { id: 1, val: 20 }];
// solutions
console.log(
array.filter(obj => obj.id === 1).sum(obj => obj.val),
array.filter(({id}) => id === 1).sum(({val}) => val),
array.sum(({id, val}) => id === 1 ? val : 0)
);
references:
Array.prototype.reduce
Array.prototype.filter
Arrow functions used in sum(obj => obj.val)
Object destructing assignment used in ({id}) => id === 1
Rest parameters used in (acc, ...fnArgs) => acc + fn(...fnArgs)
Conditional (ternary) operator used in id === 1 ? val : 0

Categories