Add objects of two different Arrays - javascript

I have two arrays and each of them has objects. How best can I simplify adding two objects into one but in a new list. e.g
a = [{a:1, b:2, c:3}, {d:1, e:4, f:2}]
b = [{m:1, n:2, o:4}, {r:1,s:3,u:5}, {k:1,j:4,f:8}]
z = [{a:1, b:2, c:3, m:1, n:2, o:4}, {d:1, e:4, f:2, r:1,s:3,u:5}, {k:1,j:4,f:8}]
Suppose you have list a and b, I want to add the objects of each position together in list z.

You could merge the two object in this way:
let a = [{a:1, b:2, c:3}, {d:1, e:4, f:2}]
let b = [{m:1, n:2, o:4}, {r:1,s:3,u:5}, {k:1,j:4,f:8}]
let z = [];
b.forEach((x, i) => {
let merged = {...x, ...a[i]};
z.push(merged)
})
console.log(z);

I'd go over the maximum length of a and b and use Object.assign to copy the values. Assuming you want a generic solution to any two arrays, where either can be longer than the other, note that you need to check the lengths as you go:
z = [];
for (let i = 0; i < Math.max(a.length, b.length); ++i) {
const result = i < a.length ? a[i] : {};
Object.assign(result, i < b.length ? b[i] : {});
z.push(result);
}

Try this :
const a = [{a:1, b:2, c:3}, {d:1, e:4, f:2}];
const b = [{m:1, n:2, o:4}, {r:1,s:3,u:5}, {k:1,j:4,f:8}];
let c = [];
if(a.length > b.length){
c = a.map((e,i) => {
return {
...e,
...b[i]
}
});
}else{
c = b.map((e,i) => {
return {
...e,
...a[i]
}
});
}
console.log(c);

You could check first the length of both arrays and then merge them.
const a = [{ a: 1, b: 2, c: 3 }, { d: 1, e: 4, f: 2 }]
const b = [{ m: 1, n: 2, o: 4 }, { r: 1, s: 3, u: 5 }, { k: 1, j: 4, f: 8 }]
const mergeArrays = (arr1, arr2) => arr1.map((x, i) => ({ ...x, ...arr2[i] }))
const z = a.length > b.length ? mergeArrays(a, b) : mergeArrays(b, a);
console.log(z);

Logic
Create a new array with length of maximum of both array. Using that array indices return data from array a and b
const a = [{ a: 1, b: 2, c: 3 }, { d: 1, e: 4, f: 2 }]
const b = [{ m: 1, n: 2, o: 4 }, { r: 1, s: 3, u: 5 }, { k: 1, j: 4, f: 8 }];
const z = Array.from({ length: Math.max(a.length, b.length) }, (_, index) => ({ ...a[index] , ...b[index] }));
console.log(z);

Related

Is there a more concise way to only keep properties of an object depending on a 'lookup object'

If x = {a: 4, b:5, c:6, d:7} and y = {a: true, d: true}
I want to produce {a: 4, d: 7}
Note that the object y will only ever contain the fields that are to be included and the values of y will always be true (otherwise the key will not exist in the first place.
This is what I have, its a good solution (I think) but was wondering if there is anything simpler and maybe using the spread syntax ...
const changedFields = Object.keys(y).reduce((acc, key) => {
acc[key] = x[key];
return acc;
}, {});
Your .reduce looks fine. Another option is to map the keys in y to an array of entries.
const x = {a: 4, b:5, c:6, d:7};
const y = {a: true, d: true};
const output = Object.fromEntries(
Object.keys(y).map(key => [key, x[key]])
);
console.log(output);
You can use Object.entries and filter
const x = {
a: 4,
b: 5,
c: 6,
d: 7
}
const y = {
a: true,
d: true
}
const result = Object.fromEntries(Object.entries(x).filter(([key]) => {
return y[key]
}))
console.log(result)
Other option is to use reduce
const x = {
a: 4,
b: 5,
c: 6,
d: 7
}
const y = {
a: true,
d: true
}
const result = Object.keys(y).reduce((acc, key) => ({
...acc,
[key]: x[key]
}), {})
console.log(result)
also u can use reduce and hasOwnProperty
const x = {a: 4, b:5, c:6, d:7}
const y = {a: true, d: true}
const res = Object.entries(x).reduce((prev, [key, value]) => {
if (y.hasOwnProperty(key)) {
prev[key] = value
}
return prev
}, {})
console.log(res)
Sometimes, simplest solution can be going back to for..in
const x = {a: 4, b:5, c:6, d:7};
const y = {a: true, d: true};
const changedFields = {}
for (let key in y) {
changedFields[key] = x[key]
}
console.log(changedFields)

Cumulative sum of specific keys with array output using reduce

Say I have the following array:
let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
I would like to compute the cumulative sum of each key, but I would also like the output to be an array of the same length with the cumulative values at each step. The final result should be:
[{a: 1, b: 2}, {a: 3, b: 6}, {a: 11, b: 5}]
My issue is that I am not able to obtain the array as desired. I only get the final object with this:
let result = arr.reduce((accumulator, element) => {
if(accumulator.length === 0) {
accumulator = element
} else {
for(let i in element){
accumulator[i] = accumulator[i] + element[i]
}
}
return accumulator
}, [])
console.log(result); // {a: 11, b: 5}
What you're after sounds like the scan() higher-order function (borrowing the idea from ramda.js), which allows you to return an accumulated result for each element within your array. The scan method is similar to how the .reduce() method behaves, except that it returns the accumulator for each element. You can build the scan() function yourself like so:
let arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}];
const scan = ([x, ...xs], fn) => xs.reduce((acc, elem) => {
return [...acc, fn(acc.at(-1), elem)];
}, xs.length ? [x] : []);
const res = scan(arr, (x, y) => ({a: x.a+y.a, b: x.b+y.b}));
console.log(res);
You might consider further improvements such as providing an initial value to the scan method (similar to how reduce accepts one). Also, if you need better browser support the .at() method currently has limited browser support, so you may instead consider creating your own at() function:
const at = (arr, idx) => idx >= 0 ? arr[idx] : arr[arr.length + idx];
You can easily achieve the result using reduce as
let arr = [
{ a: 1, b: 2 },
{ a: 2, b: 4 },
{ a: 8, b: -1 },
];
const result = arr.reduce((acc, curr, i) => {
if (i === 0) acc.push(curr);
else {
const last = acc[i - 1];
const newObj = {};
Object.keys(curr).forEach((k) => (newObj[k] = curr[k] + last[k]));
acc.push(newObj);
}
return acc;
}, []);
console.log(result);
Something like this:
const arr = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
const result = arr.reduce((accumulator, element, index) => {
if(accumulator.length === 0) {
accumulator.push(element)
} else {
const sum = {};
for(let i in element) {
sum[i] = element[i] + (accumulator[index - 1][i] || 0)
}
accumulator.push(sum)
}
return accumulator
}, [])
console.log(result);
Another option is keep sum result using a Map, it helps if keys in elements of the array are not always same.
const arr = [{a: 1, b: 2}, {a: 2}, {a: 8, b: -1}];
const map = new Map();
const result = arr.map((element) => {
const sum = {};
for (let i in element) {
sum[i]= element[i] + (map.get(i) || 0);
map.set(i, sum[i]);
}
return sum;
});
console.log(result);
Here is a bit more concise reduce, probably not as readable as a consequence...
array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])
let array = [{a: 1, b: 2}, {a: 2, b: 4}, {a: 8, b: -1}]
let culm = array.reduce((y,x,i) => ( i===0 ? y : [...y, {a: x.a + y[i-1].a, b: x.b + y[i-1].b}]),[array[0]])
console.log(culm)
Given:
const xs =
[ {a: 1, b: 2}
, {a: 2, b: 4}
, {a: 8, b: -1}];
Define a function sum such as:
const sum = ([head, ...tail]) =>
tail.reduce((x, y) =>
({a: (x.a+y.a), b: (x.b+y.b)}), head);
sum(xs);
//=> {a: 11, b: 5}
Then apply that function in a map on larger slices of xs:
xs.map((_, i, arr) => sum(arr.slice(0, i+1)));
//=> [ {a: 1, b: 2}
//=> , {a: 3, b: 6}
//=> , {a: 11, b: 5}]

How to make array of object with limmited values

Sorry, in advance if the title is unclear, but it's hard to describe it in a words.
What I have:
const obj = {
a: 5,
b: 3,
c: 0,
d: 9
}
What I want to have:
const arr = [[a, 5] ,[b, 3]]
Basically, I try to write a function that return me array of entries, but it has too meet requirements:
don't want objects when values is equal to 0
sum of values must be less than 10
First point is easy for me and I can do it by
Object.entries(obj).filter(([k, v])=> v !== 0)
but I can't handle with the second one.
May I use reduce here?
You can use a closure and an IIFE to store the sum
Object.entries(obj).filter((() => {
let sum = 0;
return ([k, v]) => { sum += v; return v !== 0 && sum < 10; };
})());
Examples:
function convert(obj) {
return Object.entries(obj).filter((() => {
let sum = 0;
return ([k, v]) => { sum += v; return v !== 0 && sum < 10; };
})());
}
const obj = { a: 5, b: 3, c: 0, d: 9 };
const arr = convert(obj);
console.log(arr);
const obj2 = { a: 0, b: 0, c: 8, d: 0, e: 1, f: 5 };
const arr2 = convert(obj2);
console.log(arr2);
const obj3 = { a: 12 };
const arr3 = convert(obj3);
console.log(arr3);
#jabaa's answer is great and you should accept it.
Just to confirm your intuition, you could have used reduce, but it would get rather complicated:
const obj = {
a: 5,
b: 3,
c: 0,
d: 9
}
const result = Object.entries(obj).reduce(
(o, newPair) => {
o.sum += newPair[1];
newPair[1] !== 0 && o.sum < 10 && o.pairs.push(newPair);
return o;
},
{
sum: 0,
pairs: []
}
).pairs;
console.log(result)

Sum of certain values of object

I have to calculate a sum of certain object values ( not all )
I have this object :
let object = {a: 1, b: 4, c: 2, d: 3, e: 10}
I need to sum just the a, c, d, e values.
Actually I use this method which sums all the values and gives me 20, but I need to have 16.
Object.keys(object).reduce((sum, key) => sum + parseFloat(object[key] || 0), 0)
How can I do this sum ?
Your sum function is good as it is, you just need to apply a filter
Object.keys(object)
.filter(key => key !== 'b')
.reduce((sum, key) => sum + parseFloat(object[key] || 0), 0)
Or, if you want a whitelist
const validKeys = {
a: true,
b: false, // optional
c: true,
d: true,
e: true
}
Object.keys(object)
.filter(key => validKeys[key])
.reduce((sum, key) => sum + parseFloat(object[key] || 0), 0)
To follow what you originally did, You should have an array of the keys and check to see if it is included before you add it.
const myObject = {a: 1, b: 4, c: 2, d: 3, e: 10}
const keys = ['a', 'c','d', 'e']
const entries = Object.entries(myObject)
const result = entries.reduce( (total, [key, value]) => (keys.includes(key) ? value : 0) + total, 0)
console.log(result)
smarter way is to loop over the keys
const myObject = {a: 1, b: 4, c: 2, d: 3, e: 10}
const keys = ['a', 'c','d', 'e']
const result = keys.reduce( (total, key) => (myObject[key] || 0) + total, 0)
console.log(result)
I'll add my two cents to the thread for...in is awesome too xD
let object = {a: 1, b: 4, c: 2, d: 3, e: 10}
let sum = 0;
const keys = ['a', 'c', 'd', 'e'];
for(let key in object) {
if(keys.includes(key)) //or key === 'a' || key === 'c' ..
sum += object[key];
}
console.log(sum);
You could take the wanted keys directly.
let object = {a: 1, b: 4, c: 2, d: 3, e: 10},
keys = ['a', 'c', 'd', 'e'],
result = keys.reduce((sum, key) => sum + (object[key] || 0), 0);
console.log(result);
You could either declare the keys you want to sum (whitelist) or those you wish to omit (blacklist). I've used the latter approach here:
let object = {a: 1, b: 4, c: 2, d: 3, e: 10},
ignore = ['b'],
sum = Object.keys(object)
.filter(key => !ignore.includes(key))
.reduce((total, key) => total += object[key], 0);
console.log(sum); //16
Fiddle

javascript pattern matching object

Given a javascript object array eg.
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
is there a faster way of getting all the b values from each object which meet a specific criteria such as a = 1 to return something like
b_consolidated = [2,3]
instead of looping through every object in the array?
You can use Array#filter function to get the items of your criteria, then use Array#map to get only b property.
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}];
let values = objArray.filter(item => item.a === 1).map(item => item.b);
console.log(values);
Or you can do this in one loop
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}];
let values = [];
objArray.forEach(item => {
if(item.a === 1) {
values.push(item.b);
}
});
console.log(values);
You could use Array#reduce in a single loop.
let array = [{ a: 1, b: 2, c: 3}, { a: 1, b: 3, c: 2 }, { a: 2, b: 5, c: 1 }],
result = array.reduce((r, o) => o.a === 1 ? r.concat(o.b) : r, []);
console.log(result);
Fastest version with for loop.
let array = [{ a: 1, b: 2, c: 3}, { a: 1, b: 3, c: 2 }, { a: 2, b: 5, c: 1 }],
i, l,
result = [];
for (i = 0, l = array.length; i < l; i++) {
if (array[i].a === 1) {
result.push(array[i].b);
}
}
console.log(result);
You only need to iterate over the array once, if you use reduce:
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
let result = objArray.reduce((arr, val) => {
if(val.a === 1)
arr.push(val.b);
return arr;
}, []);
console.log(result);
This is as fast as it'll get, short of a manual for loop:
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
let result = [];
for(var i = 0 ; i < objArray.length; i++){
if(objArray[i].a === 1)
result.push(objArray[i].b);
}
console.log(result);
Here's a JSPerf to illustrate the difference.
A manual for loop is by far the fastest.
More faster would be using .reduce
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}];
objArray.reduce(function(res,obj){
if(obj.a===1)
res.push(obj.b);
return res;
},[]);
// [2,3]
In Ramda
let objArray = [{a: 1, b: 2 , c:3},{a: 1, b:3, c:2},{a: 2, b:5, c:1}]
R.pipe(
R.filter(R.propEq('a', 1)),
R.pluck('b')
)(objArray)
// [2, 3]
Filter returns the array values matched by the condition.
Pluck returns a new list by plucking the same named property off all objects in the list supplied.
Edit 1:
Example of using the mentioned reduce pattern in Ramda:
R.reduce((acc, x) => R.ifElse(
R.propEq('a', 1),
(item) => R.pipe(R.prop('b'), R.append(R.__, acc))(item),
R.always(acc)
)(x), [])(objArray)
// [2, 3]

Categories