Aggregating keys with the same value in javascript objects - javascript

I have an array of objects like so:
[{state: CA, value: 1}, {state: CA, value: 1}, {state: NY, value: 10}]
I would like the final result to be an object that contains the aggregates of the value for the same states:
{CA: 2, NY: 10}
I have this code below to do that:
let arr = [{state: 'CA', value: 1}, {state: 'CA', value: 1}, {state: 'NY', value: 10}];
let finalObj = {};
arr.map(function(item) {
let state = item.state;
if (!finalObj[state]) {
finalObj[state] = item.value;
}else {
finalObj[state] += item.value;
}
});
console.log(finalObj);
Is there a better way to do this rather than having those if and else checks inside of the map function?

First I recommend forEach instead of map if you want a simple loop. However in this case reduce is what you really want. I think this is the shortest way to do this I can think of:
const arr = [{state: 'CA', value: 1}, {state: 'CA', value: 1}, {state: 'NY', value: 10}];
const finalObj = arr.reduce((acc, item) => {
acc[item.state] = (acc[item.state] || 0) + item.value;
return acc;
}, {});
You can just replace the if/else with always using an assignment and just doing (acc[item.state] || 0) to always get a valid number.
Another option could be to only do the if part (which I think is a pretty clean solution):
const arr = [{state: 'CA', value: 1}, {state: 'CA', value: 1}, {state: 'NY', value: 10}];
const finalObj = arr.reduce((acc, item) => {
if(!acc[item.state]) {
acc[item.state] = 0;
}
acc[item.state] += item.value;
return acc;
}, {});

Related

How to update properties of objects in an array with calculated dynamically value

I have an array of objects (examples simplified here):
array1 = [
{name: "alfa", par1: 1, value: null},
{name: "beta", par1: 2, value: null},
{name: "gamma",par1: 3, value: null},
]
An object with some parameters:
parameters = {par2: 1, par3: 9}
And function calculating value using some properties of objects from array1 and some parameters from parameters object (par1 is from the array object itself and par2,par3 are stored in another object - common for all array1) :
func = (par1, par2, par3) => {
return par1 + par2 + par3
}
What is the simplest way to update "value" property in objects in array1 with result of function func?
This is expected result:
result = [
{name: "alfa", par1: 1, value: 11},
{name: "beta", par1: 2, value: 12},
{name: "gamma",par1: 3, value: 13},
]
There must be an elegant way to achieve this...
Thanks for all help :)
EDIT:
Sorry for not clarifying it enough...
The key of the problem is using the func function. The example above is very simplified, in fact the function has complicated formula (using reduce for parameters object is not possible) and using over 20 parameters.
It needs to use all parameters at the same time!
Using func
let array1 = [
{name: "alfa", par1: 1, value: null},
{name: "beta", par1: 2, value: null},
{name: "gamma",par1: 3, value: null},
]
let func = (par1, par2, par3) => par1 + par2 + par3
let parameters = {par2: 1, par3: 9}
array1.forEach(e => e.value = func(e.par1, ...Object.values(parameters)))
console.log(array1)
One alternative usuing forEach, Object.values and reduce
Scroll through all the objects in the array arra1 {name: '...', p1: ..., values: null}' and use reduce to sum all the values ​​of the parameter object, then just add the result with the value of the p1 object of arra1
let array1 = [
{name: "alfa", par1: 1, value: null},
{name: "beta", par1: 2, value: null},
{name: "gamma",par1: 3, value: null},
]
let parameters = {par2: 1, par3: 9}
array1.forEach(e => e.value = Object.values(parameters).reduce((ac, curr) => ac + curr, e.par1))
console.log(array1)
You can modify your func to be a curried function. This way you can first pass parameters to it and extract the par2 and par3 properties/values from it using destructuring assignment. This function will return a new function (add) which accepts another object with a par1 property and closes over the par2 and par3 values. You can then use .map() on your array1 to return a new array with each object containing a value property. The value of the value key will be the result of passing o into the function returned by func - which will add par1, par2 and par3 together.
See example below:
const array1 = [
{name: "alfa", par1: 1, value: null},
{name: "beta", par1: 2, value: null},
{name: "gamma",par1: 3, value: null},
];
const parameters = {par2: 1, par3: 9}
const func = ({par2, par3}) => ({par1}) => {
return par1 + par2 + par3;
};
const add = func(parameters);
const res = array1.map(o => ({...o, value: add(o)}));
console.log(res);
A scalable version of the object can be achieved by grabbing the values of the parameters object and summing them using .reduce():
const array1 = [
{name: "alfa", par1: 1, value: null},
{name: "beta", par1: 2, value: null},
{name: "gamma",par1: 3, value: null},
];
const parameters = {par2: 1, par3: 9}
const func = params => {
const sum = Object.values(params).reduce((t, n) => t+n, 0);
return ({par1}) => par1 + sum;
};
const add = func(parameters);
const res = array1.map(o => ({...o, value: add(o)}));
console.log(res);
You can try this, it's one or two argument is enough for you function to return the new value for the value property.
You can even get rid of the base param and just have it inside the function.
const array1 = [{
name: "alfa",
par1: 1,
value: null
},
{
name: "beta",
par1: 2,
value: null
},
{
name: "gamma",
par1: 3,
value: null
},
]
const func = (base, par1) => {
return base + par1;
}
const result = array1.map(x => ({
...x,
value: func('1', x.par1)
}))
console.log(result)
array1 = [
{name: "alfa", par1: 1, value: null},
{name: "beta", par1: 2, value: null},
{name: "gamma",par1: 3, value: null},
];
parameters = {par2: 1, par3: 9};
array1.forEach(item=>item.value=item.par1+parameters.par2+parameters.par3);
console.log(array1);
And for unknown number of parameters (disregarding their names):
array1 = [
{name: "alfa", par1: 1, value: null},
{name: "beta", par1: 2, value: null},
{name: "gamma",par1: 3, value: null},
];
parameters = {par2: 1, par3: 9, x: 3, y:7};
array1.forEach(item=>
item.value=item.par1+
Object.values(parameters).reduce((acc,cv)=>acc+cv,0)
)
console.log(array1);

convert an array of objects to an object based on a given key

I am new to Javascript.
I need to write a function to covert an array of objects to an object with a given key.
The input is like this
convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id')
and output needs to be like this
{
'1': {id: 1, value: 'abc'},
'2': {id: 2, value: 'xyz'}
}
I have tried the below code.
When I am trying this directly in console it seems it is working.
var arr = [{ id: 1, name: 'John', role: 'Developer'},
{ id: 2, name: 'Jane', role: 'Lead'},
{ id: 3, name: 'Robbie', role: 'QA'}];
let res = arr.reduce((prev, current) => {
prev[current.v] = current;
return prev;
}, {})
console.log(res)
But, when I am trying do it from the function it is not working.
function f(k, v) {
//console.log(k);
k.reduce((prev, current) => {
prev[current.v] = current;
return prev;
console.log(prev)
}, {})
}
f(arr, 'role');
Any help will be highly appreciated.
You could take a dunctional approach by mapping an assigning new object.
function convert(array, key) {
return Object.assign(...array.map(o => ({ [o[key]]: o })));
}
console.log(convert([{ id: 1, value: 'abc' }, { id: 2, value: 'xyz' }], 'id'))
This solution works for me:
function convert(obj, key) {
var newObj = {};
obj.forEach(element => {
newObj[element[key]] = element;
});
return newObj;
}
var newObj = convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id');
console.log(newObj);
Its simple. Why complicating by reduce and etc.,
function convert(arr, key) {
output = {};
arr.forEach(function(item) {
output[item[key]] = item;
})
console.log(output)
return output
}
convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id')
https://jsfiddle.net/cvydtL7p/
You're close, but you need nested bracket notation to get to the proper key name, eg
prev[current[v]]
or
a[item[keyName]] // as in code below
const convert = (arr, keyName) => arr.reduce((a, item) => {
a[item[keyName]] = item;
return a;
}, {});
console.log(
convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id')
);
Your code is pretty much working, the only errors are to use bracket notation to access a variable key; for example:
obj[v] will evaluate to obj.id if v was id
The other error is that you are simply missing to return from your function, resulting in an undefined result
var arr = [{ id: 1, name: 'John', role: 'Developer'},
{ id: 2, name: 'Jane', role: 'Lead'},
{ id: 3, name: 'Robbie', role: 'QA'}];
function f(k, v) {
//console.log(k);
return k.reduce((prev, current) => {
prev[current[v]] = current;
return prev;
}, {})
}
console.log(f(arr, 'role'));
Also note that nothing after return will happen, so the console.log line in your reducer should be before that, otherwise it's ignored.
You can use reduce and spread like this:
var arr = [{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}];
const res = arr.reduce((total, curr) => {
return {...total, [curr.id]: curr };
}, {});
console.log(res);
refs:
reduce
Spread syntax

Merge arrays without duplicates (with custom equality comparison function) [duplicate]

Lets have a look at an example.
var arr1 = new Array({name: "lang", value: "English"},
{name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'},
{name: "lang", value: "German"});
I need to merge those 2 arrays of objects and create the following array:
var arr3 = new Array({name: "lang", value: "German"},
{name: "age", value: "18"},
{name : "childs", value: '5'});
Is there any JavaScript or jQuery function to do this?
$.extend doesn't suit me. It returns
var arr4 = new Array({name : "childs", value: '5'},
{name: "lang", value: "German"});
If you want to merge 2 arrays of objects in JavaScript. You can use this one line trick
Array.prototype.push.apply(arr1,arr2);
For Example
var arr1 = [{name: "lang", value: "English"},{name: "age", value: "18"}];
var arr2 = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];
Array.prototype.push.apply(arr1,arr2);
console.log(arr1); // final merged result will be in arr1
Output:
[{"name":"lang","value":"English"},
{"name":"age","value":"18"},
{"name":"childs","value":"5"},
{"name":"lang","value":"German"}]
With ES6 you can do it very easy as below:
var arr1 = new Array({name: "lang", value: "German"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var arr3 = [...arr1, ...arr2];
Output:
arr3 = [
{"name":"lang","value":"German"},
{"name":"age","value":"18"},
{"name":"childs","value":"5"},
{"name":"lang","value":"German"}
]
For those who are experimenting with modern things:
var odd = [{
name: "1",
arr: "in odd"
},
{
name: "3",
arr: "in odd"
}
];
var even = [{
name: "1",
arr: "in even"
},
{
name: "2",
arr: "in even"
},
{
name: "4",
arr: "in even"
}
];
// ----
// ES5 using Array.filter and Array.find
function merge(a, b, prop) {
var reduced = a.filter(function(aitem) {
return !b.find(function(bitem) {
return aitem[prop] === bitem[prop];
});
});
return reduced.concat(b);
}
console.log("ES5", merge(odd, even, "name"));
// ----
// ES6 arrow functions
function merge(a, b, prop) {
var reduced = a.filter(aitem => !b.find(bitem => aitem[prop] === bitem[prop]))
return reduced.concat(b);
}
console.log("ES6", merge(odd, even, "name"));
// ----
// ES6 one-liner
var merge = (a, b, p) => a.filter(aa => !b.find(bb => aa[p] === bb[p])).concat(b);
console.log("ES6 one-liner", merge(odd, even, "name"));
// Results
// ( stuff in the "b" array replaces things in the "a" array )
// [
// {
// "name": "3",
// "arr": "in odd"
// },
// {
// "name": "1",
// "arr": "in even"
// },
// {
// "name": "2",
// "arr": "in even"
// },
// {
// "name": "4",
// "arr": "in even"
// }
// ]
// for posterity, here's the old skool version
function merge(a, b, prop) {
var reduced = [];
for (var i = 0; i < a.length; i++) {
var aitem = a[i];
var found = false;
for (var ii = 0; ii < b.length; ii++) {
if (aitem[prop] === b[ii][prop]) {
found = true;
break;
}
}
if (!found) {
reduced.push(aitem);
}
}
return reduced.concat(b);
}
Update 12 Oct 2019
New version based only on newer Javascript and without the need of any 3rd party library.
const mergeByProperty = (target, source, prop) => {
source.forEach(sourceElement => {
let targetElement = target.find(targetElement => {
return sourceElement[prop] === targetElement[prop];
})
targetElement ? Object.assign(targetElement, sourceElement) : target.push(sourceElement);
})
}
var target /* arr1 */ = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
var source /* arr2 */ = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];
mergeByProperty(target, source, 'name');
console.log(target)
This answer was getting old, libs like lodash and underscore are much less needed these days.
In this new version, the target (arr1) array is the one we’re working with and want to keep up to date.
The source (arr2) array is where the new data is coming from, and we want it merged into our target array.
We loop over the source array looking for new data, and for every object that is not yet found in our target array we simply add that object using target.push(sourceElement)
If, based on our key property ('name'), an object is already in our target array - we update its properties and values using Object.assign(targetElement, sourceElement).
Our “target” will always be the same array and with updated content.
Old answer using underscore or lodash
I always arrive here from google and I'm always not satisfy from the answers. YOU answer is good but it'll be easier and neater using underscore.js
DEMO: http://jsfiddle.net/guya/eAWKR/
Here is a more general function that will merge 2 arrays using a property of their objects. In this case the property is 'name'
var arr1 = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
var arr2 = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];
function mergeByProperty(arr1, arr2, prop) {
_.each(arr2, function(arr2obj) {
var arr1obj = _.find(arr1, function(arr1obj) {
return arr1obj[prop] === arr2obj[prop];
});
arr1obj ? _.extend(arr1obj, arr2obj) : arr1.push(arr2obj);
});
}
mergeByProperty(arr1, arr2, 'name');
console.log(arr1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.core.min.js"></script>
[{name: "lang", value: "German"}, {name: "age", value: "18"}, {name : "childs", value: '5'}]
Very simple using ES6 spread operator:
const array1 = [{a: 'HI!'}, {b: 'HOW'}]
const array2 = [{c: 'ARE'}, {d: 'YOU?'}]
const mergedArray = [ ...array1, ...array2 ]
console.log('Merged Array: ', mergedArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Merged Array: [ {a: 'HI!'}, {b: 'HOW'} {c: 'ARE'}, {d: 'YOU?'} ]
Note: The above solution is to just merge two arrays using ES6 spread operator.
Edit on 07 January 2020 by #bh4r4th :
As the context changed due to edits after my initial solution. I would like to update my solution to match current criteria. i.e.,
Merger array objects without creating duplicate objects and,
update the value if the name property already exists in the prior array
const arr1 = [
{ name: "lang", value: "English" },
{ name: "age", value: "18" }
]
const arr2 = [
{ name: "childs", value: '2' },
{ name: "lang", value: "German" }
]
const arr3 = [
{ name: "lang", value: "German" },
{ name: "age", value: "28" },
{ name: "childs", value: '5' }
]
// Convert to key value dictionary or object
const convertToKeyValueDict = arrayObj => {
const val = {}
arrayObj.forEach(ob => {
val[ob.name] = ob.value
})
return val
}
// update or merge array
const updateOrMerge = (a1, a2) => {
const ob1 = convertToKeyValueDict(a1)
const ob2 = convertToKeyValueDict(a2)
// Note: Spread operator with objects used here
const merged_obj = {...ob1, ...ob2}
const val = Object.entries(merged_obj)
return val.map(obj => ({ name: obj[0], value: obj[1] }))
}
const v1 = updateOrMerge(arr1, arr2)
const v2 = updateOrMerge(v1, arr3)
console.log(`Merged array1 and array2: ${JSON.stringify(v1)} \n\n`)
console.log(`Merged above response and array3: ${JSON.stringify(v2)} \n\n`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
var arr3 = [];
for(var i in arr1){
var shared = false;
for (var j in arr2)
if (arr2[j].name == arr1[i].name) {
shared = true;
break;
}
if(!shared) arr3.push(arr1[i])
}
arr3 = arr3.concat(arr2);
Merging two arrays:
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var result=arr1.concat(arr2);
// result: [{name: "lang", value: "English"}, {name: "age", value: "18"}, {name : "childs", value: '5'}, {name: "lang", value: "German"}]
Merging two arrays without duplicated values for 'name':
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var i,p,obj={},result=[];
for(i=0;i<arr1.length;i++)obj[arr1[i].name]=arr1[i].value;
for(i=0;i<arr2.length;i++)obj[arr2[i].name]=arr2[i].value;
for(p in obj)if(obj.hasOwnProperty(p))result.push({name:p,value:obj[p]});
// result: [{name: "lang", value: "German"}, {name: "age", value: "18"}, {name : "childs", value: '5'}]
The easiest way is with some ES6 magic:
Merge two with duplicates:
const a = [{a: 1}, {b: 2}]
const b = [{a: 1}]
const result = a.concat(b) // [{a: 1}, {b: 2}, {a: 1}]
Without duplicates it is same as the above plus:
const distinct = [...new Set(result.map(item => item.YOUR_PROP_HERE))]
I'd merge two arrays with duplicates and then use my this answer to remove duplicates. This looks like shortest way.
const arr1 = [{
name: "lang",
value: "English"
},
{
name: "age",
value: "18"
}
];
const arr2 = [{
name: "childs",
value: '5'
},
{
name: "lang",
value: "German"
}
];
const mergedArray = [...arr1, ...arr2];
const uniqueData = [...mergedArray.reduce((map, obj) => map.set(obj.name, obj), new Map()).values()];
console.log(uniqueData)
With lodash:
_.uniqBy([...arr1, ...arr2], 'name')
Yet another version using reduce() method:
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var arr = arr1.concat(arr2).reduce(function(prev, current, index, array){
if(!(current.name in prev.keys)) {
prev.keys[current.name] = index;
prev.result.push(current);
}
else{
prev.result[prev.keys[current.name]] = current;
}
return prev;
},{result: [], keys: {}}).result;
document.getElementById("output").innerHTML = JSON.stringify(arr,null,2);
<pre id="output"/>
This is how I've tackled a similar issue in an ES6 context:
function merge(array1, array2, prop) {
return array2.map(function (item2) {
var item1 = array1.find(function (item1) {
return item1[prop] === item2[prop];
});
return Object.assign({}, item1, item2);
});
}
Note: This approach will not return any items from array1 that don't appear in array2.
EDIT: I have some scenarios where I want to preserve items that don't appear in the second array so I came up with another method.
function mergeArrays(arrays, prop) {
const merged = {};
arrays.forEach(arr => {
arr.forEach(item => {
merged[item[prop]] = Object.assign({}, merged[item[prop]], item);
});
});
return Object.values(merged);
}
var arr1 = [
{ name: 'Bob', age: 11 },
{ name: 'Ben', age: 12 },
{ name: 'Bill', age: 13 },
];
var arr2 = [
{ name: 'Bob', age: 22 },
{ name: 'Fred', age: 24 },
{ name: 'Jack', age: 25 },
{ name: 'Ben' },
];
console.log(mergeArrays([arr1, arr2], 'name'));
jsut using vanilla js (ES6 version)
// no need new Array constructor, just using an array literal
const arr1 = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
const arr2 = [{name: "childs", value: '5'}, {name: "lang", value: "German"}];
// 1. create a map
const map = new Map();
// 2. concat array
// arr1.concat(arr2) === [...arr1, ...arr2]
const arr3 = [...arr1, ...arr2];
// 3. for ... of, iterator array
for(const obj of arr3) {
if(!map.has(obj.name)) {
// add
map.set(obj.name, obj);
} else {
// update
map.set(obj.name, {
...map.get(obj.name),
...obj,
});
}
}
// 4. get new merged unqiue array
const arr4 = [...map.values()];
console.log(`result array =`, JSON.stringify(arr4, null, 4));
/*
result array = [
{
"name": "lang",
"value": "German"
},
{
"name": "age",
"value": "18"
},
{
"name": "childs",
"value": "5"
}
]
*/
test ✅ (Chrome)
refs
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#array_literals
Simple solution
var tx = [{"id":1},{"id":2}];
var tx1 = [{"id":3},{"id":4}];
var txHistory = tx.concat(tx1)
console.log(txHistory);
// output
// [{"id":1},{"id":2},{"id":3},{"id":4}];
You could use an object to collect up your properties while replacing duplicates and then expand/flatten that object back to an array. Something like this:
function merge(args) {
args = Array.prototype.slice.call(arguments);
var o = { };
for(var i = 0; i < args.length; ++i)
for(var j = 0; j < args[i].length; ++j)
o[args[i][j].name] = args[i][j].value;
return o;
}
function expand(o) {
var a = [ ];
for(var p in o)
if(o.hasOwnProperty(p))
a.push({ name: p, value: o[p]});
return a;
}
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var arr3 = expand(merge(arr1, arr2));
I don't know if this is the fastest way but it works for any number of input arrays; for example, this:
var a = expand(
merge(
[{name: "lang", value: "English"}, {name: "age", value: "18"}],
[{name: "childs", value: '5'}, {name: "lang", value: "German"}],
[{name: 'lang', value: 'Pancakes'}]
)
);
Gives you the same thing in a that was in arr3 with "German" replaced by "Pancakes".
This approach does assume that your objects all have the same {name: ..., value: ...} form of course.
You can see it working here (open your console please): http://jsfiddle.net/ambiguous/UtBbB/
Solution utilizing JS Map:
const merge = (arr1, arr2, prop) => {
const resultMap = new Map(arr1.map((item) => [item[prop], item]));
arr2.forEach((item) => {
const mapItem = resultMap.get(item[prop]);
if (mapItem) Object.assign(mapItem, item);
else resultMap.set(item[prop], item);
});
return [...resultMap.values()];
};
const arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
const arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
console.log(merge(arr1, arr2, "name"));
Which produces:
you could use following function
const merge = (a, b, key = "id") =>
a.filter(elem => !b.find(subElem => subElem[key] === elem[key]))
.concat(b);
and try
merge(arr1, arr2, 'name');
What about jQuery Merge?
http://api.jquery.com/jQuery.merge/
jsFiddle example here: http://jsfiddle.net/ygByD/
I was facing the same problem and based on guya answer I have extended the underscore library and also added a bit more of functionality that I was requiring. Here's the Gist.
/**
* Merges two object-like arrays based on a key property and also merges its array-like attributes specified in objectPropertiesToMerge.
* It also removes falsy values after merging object properties.
*
* #param firstArray The original object-like array.
* #param secondArray An object-like array to add to the firstArray.
* #param keyProperty The object property that will be used to check if objects from different arrays are the same or not.
* #param objectPropertiesToMerge The list of object properties that you want to merge. It all must be arrays.
* #returns The updated original array.
*/
function merge(firstArray, secondArray, keyProperty, objectPropertiesToMerge) {
function mergeObjectProperties(object, otherObject, objectPropertiesToMerge) {
_.each(objectPropertiesToMerge, function (eachProperty) {
object[eachProperty] = _.chain(object[eachProperty]).union(otherObject[eachProperty]).compact().value();
});
}
if (firstArray.length === 0) {
_.each(secondArray, function (each) {
firstArray.push(each);
});
} else {
_.each(secondArray, function (itemFromSecond) {
var itemFromFirst = _.find(firstArray, function (item) {
return item[keyProperty] === itemFromSecond[keyProperty];
});
if (itemFromFirst) {
mergeObjectProperties(itemFromFirst, itemFromSecond, objectPropertiesToMerge);
} else {
firstArray.push(itemFromSecond);
}
});
}
return firstArray;
}
_.mixin({
merge: merge
});
Hope it to be useful!
Regards!
I was recently stumped with this problem and I came here with the hope to have an answer but the accepted answer uses 2 for in loops which I wouldn't prefer. I finally managed to make my own. Doesn't depend on any library whatsoever:
function find(objArr, keyToFind){
var foundPos = objArr.map(function(ob){
return ob.type;
}).indexOf(keyToFind);
return foundPos;
}
function update(arr1,arr2){
for(var i = 0, len = arr2.length, current; i< len; i++){
var pos = find(arr1, arr2[i].name);
current = arr2[i];
if(pos !== -1) for(var key in arr2) arr1[pos][key] = arr2[key];
else arr1[arr1.length] = current;
}
}
This also maintains the order of arr1.
var arr1 = [{ name: "lang", value: "English" }, { name: "age", value: "18" }];
var arr2 = [{ name: "childs", value: '5' }, { name: "lang", value: "German" }];
function mergeArrayByProperty(arr1, arr2, prop) {
var newArray =
arr1.map(item => {
if (typeof (item[prop]) !== "undefined") {
var nItems = arr2.filter(ni => { if (typeof (ni[prop]) !== "undefined" && ni[prop] === item[prop]) return ni; });
if (nItems.length > 0) {
item = Object.assign({}, item, nItems[0]);
}
return item;
}
});
var arr2nd = arr2.flatMap(item => { return item[prop] });
var arr1nd = arr1.flatMap(item => { return item[prop] });
var nonDupArr = arr2nd.map(p => { if (arr1nd.includes(p) === false) return arr2.filter(i2 => { if (i2[prop] === p) return Object.assign({}, i2) })[0]; });
return newArray.concat(nonDupArr).filter(i=>{if(i !== null)return i})
}
var arr = mergeArrayByProperty(arr1, arr2, 'name');
console.log(arr)
I know this has been answered a lot, but I thought I would share.
This finds the duplicate key in the first array and merges the second arrays object having the same key value. If no value is found in the second array, it uses the original object. As you can see, lang is only found once in the result set; having german for the value.
Using the traditional for loop
const merge = (first, second) => {
for(let i=0; i<second.length; i++) {
first.push(second[i]);
}
return first;
}
console.log(merge([1,2,3], [4,5,6])); // [1,2,3,4,5,6]
console.log(merge(merge([1,2,3], [4,5,6]), [7,8,9])); // [1,2,3,4,5,6,7,8,9]
Using the Spread operator
const arr1 = [1,2,3];
const arr2 = [4,5,6];
// Merge arrays
const merged = [...arr1, ...arr2];
console.log(merged); // [1,2,3,4,5,6]
Using the concat() array method
const arr1 = [1,2,3];
const arr2 = [4,5,6];
// Merge arrays
const merged1 = arr1.concat(arr2); // bit confusing, seems like `arr1` itself is being modified but it's not
const merged2 = [].concat(arr1, arr2); // cleaner approach
console.log(merged1); // [1,2,3,4,5,6]
console.log(merged2); // [1,2,3,4,5,6]
Using the push() array method
const arr1A = [1,2,3];
const arr2A = [4,5,6];
const arr1B = [1,2,3];
const arr2B = [4,5,6];
const arr1C = [1,2,3];
const arr2C = [4,5,6];
const arr3C = [7,8,9];
// Merge arrays
const merged1 = arr1A.push(...arr2A);
// Merging without the ... on arr2B
const merged2 = arr1B.push(arr2B);
// Merge more than two arrays
arr1C.push(...[...arr2C, ...arr3C]);
console.log(arr1C); // [1,2,3,4,5,6,7,8,9]
console.log(merged1); // 6
console.log(arr1A); // [1,2,3,4,5,6]
console.log(arr2A); // [4,5,6]
console.log(merged2); // 4
console.log(arr1B); // [1,2,3,[4,5,6]]
console.log(arr2B); // [4,5,6]
Using the reduce() array method
const arr1 = [1,2,3];
const arr2 = [4,5,6];
const merged = arr2.reduce((arr, item) => {
arr.push(item);
return arr;
}, arr1);
console.log(merged); // [1,2,3,4,5,6]
To Summarize,
There are more than a couple of ways to merge two or more arrays into one in JavaScript.
Using the spread operator or the concat() method is the most optimal solution.
If you are sure that all inputs to merge are arrays, use spread operator. In case you are unsure, use the concat() method.
You can use the push() method to merge arrays when you want to change one of the input arrays to merge.
Using the reduce() method to merge arrays is a bit of overhead.
For more info refer the detailed blog here and video here
Off the top of my head - try jquery extend
var arr3 = jQuery.extend(arr1,arr2....)
Here I first filter arr1 based on element present in arr2 or not. If it's present then don't add it to resulting array otherwise do add. And then I append arr2 to the result.
arr1.filter(item => {
if (!arr2.some(item1=>item.name==item1.name)) {
return item
}
}).concat(arr2)
let mergeArray = arrA.filter(aItem => !arrB.find(bItem => aItem.name === bItem.name))
Posting this because unlike the previous answers this one is generic, no external libraries, O(n), actually filters out the duplicate and keeps the order the OP is asking for (by placing the last matching element in place of first appearance):
function unique(array, keyfunc) {
return array.reduce((result, entry) => {
const key = keyfunc(entry)
if(key in result.seen) {
result.array[result.seen[key]] = entry
} else {
result.seen[key] = result.array.length
result.array.push(entry)
}
return result
}, { array: [], seen: {}}).array
}
Usage:
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"})
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"})
var arr3 = unique([...arr1, ...arr2], x => x.name)
/* arr3 == [
{name: "lang", value: "German"},
{name: "age", value: "18"},
{name: "childs", value: "5"}
]*/
const arr1 = [{ name: "lang", value: "English" }, { name: "age", value: "18" }];
const arr2 = [{ name: "childs", value: '5' }, { name: "lang", value: "German" }];
const mergeArrOfObjects = (dataset1, dataset2) => {
const map1 = new Map();
dataset1.map((d1, i) => {
map1.set(d1.name, i);
})
for (let d2 of dataset2) {
if (d2 && map1.has(d2.name)) {
dataset1[map1.get(d2.name)] = d2;
} else if(d2){
dataset1.push(d2);
}
}
return dataset1;
};
const arr3 = mergeArrOfObjects(arr1, arr2);
console.log(arr3);
Just use helprjs
const arr1 = [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jack'}];
const arr2 = [{ id: 2, name: 'Jane'}, { id: 3, name: 'Rod'}];
mergeArrays(arr1, arr2, "name");
// [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jane'}, { id: 3, name: 'Rod'}];
mergeArrays(arr1, arr2, "id");
// [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jack'}, { id: 3, name: 'Rod'}];
Check out the demo
Based on the question, I understand that there is a key that you want to use to override other attributes, not to merge them.
interface Foo {
name: string;
value: string;
}
var arr1: Foo[] = [
{ name: "lang", value: "English" },
{ name: "age", value: "18" },
];
var arr2: Foo[] = [
{ name: "childs", value: "5" },
{ name: "lang", value: "German" },
];
We can use combination of Map with Reduce to select the key that will be used to overwrite the record.
const merged: Foo[] = Array.from(
[...arr1, ...arr2].reduce(
(acc, curr) => acc.set(curr.name, curr),
new Map<Foo["name"], Foo>(),
)
.values(),
);
// [
// { name: "lang", value: "German" },
// { name: "age", value: "18" },
// { name: "childs", value: "5" },
// ];
var newArray = yourArray.concat(otherArray);
console.log('Concatenated newArray: ', newArray);

consolidating values in a list of objects based on an attribute which is the same

Bit of background, this comes from a submitted form that I used serializeArray() on
I have a list of objects like so.
[
{name: 0, value: 'waffles'},
{name: 0, value: 'pancakes'},
{name: 0, value: 'french toast'},
{name: 1, value: 'pancakes'}
]
I want to take all things that have the same name attribute and put them together. EG,
[
{name: 0, value: ['waffles', 'pancakes', 'french toast']},
{name: 1, value: ['pancakes']}
]
how would one go about this? All the things I've tried only result in one answer being shown for each name key.
This should do it:
var newlist = _.map(_.groupBy(oldlist, "name"), function(v, n) {
return {name: n, values: _.pluck(v, "value")};
});
Here's the best I could come up with:
http://jsfiddle.net/Z6bdB/
var arr = [
{name: 0, value: 'waffles'},
{name: 0, value: 'pancakes'},
{name: 0, value: 'french toast'},
{name: 1, value: 'pancakes'}
]
var obj1 = {};
$.each(arr, function(idx, item) {
if (obj1[item.name]) {
obj1[item.name].push(item.value);
} else {
obj1[item.name] = [item.value];
}
});
var result = [];
for(var prop in obj1) {
result.push({
name: prop,
value: obj1[prop]
});
}
console.log(result);
This seems to work:
var output = _.chain(input)
.groupBy(function(x){ return x.name; })
.map(function(g, k) { return { name: k, value: _.pluck(g, 'value') }; })
.value();
Demonstration
I am one of those guys that uses native functions:
var food = [
{name: 0, value: 'waffles'},
{name: 0, value: 'pancakes'},
{name: 0, value: 'french toast'},
{name: 1, value: 'pancakes'}
];
var result = food.reduce(function(res,dish){
if (!res.some(function(d){return d.name === dish.name })){
var values = food.filter(function(d){ return d.name === dish.name }).map(function(d){ return d.value; });
res.push({name: dish.name, value : values});
}
return res;
}, []);
console.log(result);
I'd probably do something like this:
function them_into_groups(m, h) {
m[h.name] || (m[h.name] = [ ]);
m[h.name].push(h.value);
return m;
}
function them_back_to_objects(v, k) {
return {
name: +k,
value: v
};
}
var output = _(input).chain()
.reduce(them_into_groups, { })
.map(them_back_to_objects)
.value();
Using _.chain and _.value makes things flow nicely and using named functions make things less messy and clarifies the logic.
Demo: http://jsfiddle.net/ambiguous/G7qwM/2/

Merge 2 arrays of objects

Lets have a look at an example.
var arr1 = new Array({name: "lang", value: "English"},
{name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'},
{name: "lang", value: "German"});
I need to merge those 2 arrays of objects and create the following array:
var arr3 = new Array({name: "lang", value: "German"},
{name: "age", value: "18"},
{name : "childs", value: '5'});
Is there any JavaScript or jQuery function to do this?
$.extend doesn't suit me. It returns
var arr4 = new Array({name : "childs", value: '5'},
{name: "lang", value: "German"});
If you want to merge 2 arrays of objects in JavaScript. You can use this one line trick
Array.prototype.push.apply(arr1,arr2);
For Example
var arr1 = [{name: "lang", value: "English"},{name: "age", value: "18"}];
var arr2 = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];
Array.prototype.push.apply(arr1,arr2);
console.log(arr1); // final merged result will be in arr1
Output:
[{"name":"lang","value":"English"},
{"name":"age","value":"18"},
{"name":"childs","value":"5"},
{"name":"lang","value":"German"}]
With ES6 you can do it very easy as below:
var arr1 = new Array({name: "lang", value: "German"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var arr3 = [...arr1, ...arr2];
Output:
arr3 = [
{"name":"lang","value":"German"},
{"name":"age","value":"18"},
{"name":"childs","value":"5"},
{"name":"lang","value":"German"}
]
For those who are experimenting with modern things:
var odd = [{
name: "1",
arr: "in odd"
},
{
name: "3",
arr: "in odd"
}
];
var even = [{
name: "1",
arr: "in even"
},
{
name: "2",
arr: "in even"
},
{
name: "4",
arr: "in even"
}
];
// ----
// ES5 using Array.filter and Array.find
function merge(a, b, prop) {
var reduced = a.filter(function(aitem) {
return !b.find(function(bitem) {
return aitem[prop] === bitem[prop];
});
});
return reduced.concat(b);
}
console.log("ES5", merge(odd, even, "name"));
// ----
// ES6 arrow functions
function merge(a, b, prop) {
var reduced = a.filter(aitem => !b.find(bitem => aitem[prop] === bitem[prop]))
return reduced.concat(b);
}
console.log("ES6", merge(odd, even, "name"));
// ----
// ES6 one-liner
var merge = (a, b, p) => a.filter(aa => !b.find(bb => aa[p] === bb[p])).concat(b);
console.log("ES6 one-liner", merge(odd, even, "name"));
// Results
// ( stuff in the "b" array replaces things in the "a" array )
// [
// {
// "name": "3",
// "arr": "in odd"
// },
// {
// "name": "1",
// "arr": "in even"
// },
// {
// "name": "2",
// "arr": "in even"
// },
// {
// "name": "4",
// "arr": "in even"
// }
// ]
// for posterity, here's the old skool version
function merge(a, b, prop) {
var reduced = [];
for (var i = 0; i < a.length; i++) {
var aitem = a[i];
var found = false;
for (var ii = 0; ii < b.length; ii++) {
if (aitem[prop] === b[ii][prop]) {
found = true;
break;
}
}
if (!found) {
reduced.push(aitem);
}
}
return reduced.concat(b);
}
Update 12 Oct 2019
New version based only on newer Javascript and without the need of any 3rd party library.
const mergeByProperty = (target, source, prop) => {
source.forEach(sourceElement => {
let targetElement = target.find(targetElement => {
return sourceElement[prop] === targetElement[prop];
})
targetElement ? Object.assign(targetElement, sourceElement) : target.push(sourceElement);
})
}
var target /* arr1 */ = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
var source /* arr2 */ = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];
mergeByProperty(target, source, 'name');
console.log(target)
This answer was getting old, libs like lodash and underscore are much less needed these days.
In this new version, the target (arr1) array is the one we’re working with and want to keep up to date.
The source (arr2) array is where the new data is coming from, and we want it merged into our target array.
We loop over the source array looking for new data, and for every object that is not yet found in our target array we simply add that object using target.push(sourceElement)
If, based on our key property ('name'), an object is already in our target array - we update its properties and values using Object.assign(targetElement, sourceElement).
Our “target” will always be the same array and with updated content.
Old answer using underscore or lodash
I always arrive here from google and I'm always not satisfy from the answers. YOU answer is good but it'll be easier and neater using underscore.js
DEMO: http://jsfiddle.net/guya/eAWKR/
Here is a more general function that will merge 2 arrays using a property of their objects. In this case the property is 'name'
var arr1 = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
var arr2 = [{name : "childs", value: '5'}, {name: "lang", value: "German"}];
function mergeByProperty(arr1, arr2, prop) {
_.each(arr2, function(arr2obj) {
var arr1obj = _.find(arr1, function(arr1obj) {
return arr1obj[prop] === arr2obj[prop];
});
arr1obj ? _.extend(arr1obj, arr2obj) : arr1.push(arr2obj);
});
}
mergeByProperty(arr1, arr2, 'name');
console.log(arr1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.core.min.js"></script>
[{name: "lang", value: "German"}, {name: "age", value: "18"}, {name : "childs", value: '5'}]
Very simple using ES6 spread operator:
const array1 = [{a: 'HI!'}, {b: 'HOW'}]
const array2 = [{c: 'ARE'}, {d: 'YOU?'}]
const mergedArray = [ ...array1, ...array2 ]
console.log('Merged Array: ', mergedArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Merged Array: [ {a: 'HI!'}, {b: 'HOW'} {c: 'ARE'}, {d: 'YOU?'} ]
Note: The above solution is to just merge two arrays using ES6 spread operator.
Edit on 07 January 2020 by #bh4r4th :
As the context changed due to edits after my initial solution. I would like to update my solution to match current criteria. i.e.,
Merger array objects without creating duplicate objects and,
update the value if the name property already exists in the prior array
const arr1 = [
{ name: "lang", value: "English" },
{ name: "age", value: "18" }
]
const arr2 = [
{ name: "childs", value: '2' },
{ name: "lang", value: "German" }
]
const arr3 = [
{ name: "lang", value: "German" },
{ name: "age", value: "28" },
{ name: "childs", value: '5' }
]
// Convert to key value dictionary or object
const convertToKeyValueDict = arrayObj => {
const val = {}
arrayObj.forEach(ob => {
val[ob.name] = ob.value
})
return val
}
// update or merge array
const updateOrMerge = (a1, a2) => {
const ob1 = convertToKeyValueDict(a1)
const ob2 = convertToKeyValueDict(a2)
// Note: Spread operator with objects used here
const merged_obj = {...ob1, ...ob2}
const val = Object.entries(merged_obj)
return val.map(obj => ({ name: obj[0], value: obj[1] }))
}
const v1 = updateOrMerge(arr1, arr2)
const v2 = updateOrMerge(v1, arr3)
console.log(`Merged array1 and array2: ${JSON.stringify(v1)} \n\n`)
console.log(`Merged above response and array3: ${JSON.stringify(v2)} \n\n`)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
var arr3 = [];
for(var i in arr1){
var shared = false;
for (var j in arr2)
if (arr2[j].name == arr1[i].name) {
shared = true;
break;
}
if(!shared) arr3.push(arr1[i])
}
arr3 = arr3.concat(arr2);
Merging two arrays:
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var result=arr1.concat(arr2);
// result: [{name: "lang", value: "English"}, {name: "age", value: "18"}, {name : "childs", value: '5'}, {name: "lang", value: "German"}]
Merging two arrays without duplicated values for 'name':
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var i,p,obj={},result=[];
for(i=0;i<arr1.length;i++)obj[arr1[i].name]=arr1[i].value;
for(i=0;i<arr2.length;i++)obj[arr2[i].name]=arr2[i].value;
for(p in obj)if(obj.hasOwnProperty(p))result.push({name:p,value:obj[p]});
// result: [{name: "lang", value: "German"}, {name: "age", value: "18"}, {name : "childs", value: '5'}]
The easiest way is with some ES6 magic:
Merge two with duplicates:
const a = [{a: 1}, {b: 2}]
const b = [{a: 1}]
const result = a.concat(b) // [{a: 1}, {b: 2}, {a: 1}]
Without duplicates it is same as the above plus:
const distinct = [...new Set(result.map(item => item.YOUR_PROP_HERE))]
I'd merge two arrays with duplicates and then use my this answer to remove duplicates. This looks like shortest way.
const arr1 = [{
name: "lang",
value: "English"
},
{
name: "age",
value: "18"
}
];
const arr2 = [{
name: "childs",
value: '5'
},
{
name: "lang",
value: "German"
}
];
const mergedArray = [...arr1, ...arr2];
const uniqueData = [...mergedArray.reduce((map, obj) => map.set(obj.name, obj), new Map()).values()];
console.log(uniqueData)
With lodash:
_.uniqBy([...arr1, ...arr2], 'name')
Yet another version using reduce() method:
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var arr = arr1.concat(arr2).reduce(function(prev, current, index, array){
if(!(current.name in prev.keys)) {
prev.keys[current.name] = index;
prev.result.push(current);
}
else{
prev.result[prev.keys[current.name]] = current;
}
return prev;
},{result: [], keys: {}}).result;
document.getElementById("output").innerHTML = JSON.stringify(arr,null,2);
<pre id="output"/>
This is how I've tackled a similar issue in an ES6 context:
function merge(array1, array2, prop) {
return array2.map(function (item2) {
var item1 = array1.find(function (item1) {
return item1[prop] === item2[prop];
});
return Object.assign({}, item1, item2);
});
}
Note: This approach will not return any items from array1 that don't appear in array2.
EDIT: I have some scenarios where I want to preserve items that don't appear in the second array so I came up with another method.
function mergeArrays(arrays, prop) {
const merged = {};
arrays.forEach(arr => {
arr.forEach(item => {
merged[item[prop]] = Object.assign({}, merged[item[prop]], item);
});
});
return Object.values(merged);
}
var arr1 = [
{ name: 'Bob', age: 11 },
{ name: 'Ben', age: 12 },
{ name: 'Bill', age: 13 },
];
var arr2 = [
{ name: 'Bob', age: 22 },
{ name: 'Fred', age: 24 },
{ name: 'Jack', age: 25 },
{ name: 'Ben' },
];
console.log(mergeArrays([arr1, arr2], 'name'));
jsut using vanilla js (ES6 version)
// no need new Array constructor, just using an array literal
const arr1 = [{name: "lang", value: "English"}, {name: "age", value: "18"}];
const arr2 = [{name: "childs", value: '5'}, {name: "lang", value: "German"}];
// 1. create a map
const map = new Map();
// 2. concat array
// arr1.concat(arr2) === [...arr1, ...arr2]
const arr3 = [...arr1, ...arr2];
// 3. for ... of, iterator array
for(const obj of arr3) {
if(!map.has(obj.name)) {
// add
map.set(obj.name, obj);
} else {
// update
map.set(obj.name, {
...map.get(obj.name),
...obj,
});
}
}
// 4. get new merged unqiue array
const arr4 = [...map.values()];
console.log(`result array =`, JSON.stringify(arr4, null, 4));
/*
result array = [
{
"name": "lang",
"value": "German"
},
{
"name": "age",
"value": "18"
},
{
"name": "childs",
"value": "5"
}
]
*/
test ✅ (Chrome)
refs
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#array_literals
Simple solution
var tx = [{"id":1},{"id":2}];
var tx1 = [{"id":3},{"id":4}];
var txHistory = tx.concat(tx1)
console.log(txHistory);
// output
// [{"id":1},{"id":2},{"id":3},{"id":4}];
You could use an object to collect up your properties while replacing duplicates and then expand/flatten that object back to an array. Something like this:
function merge(args) {
args = Array.prototype.slice.call(arguments);
var o = { };
for(var i = 0; i < args.length; ++i)
for(var j = 0; j < args[i].length; ++j)
o[args[i][j].name] = args[i][j].value;
return o;
}
function expand(o) {
var a = [ ];
for(var p in o)
if(o.hasOwnProperty(p))
a.push({ name: p, value: o[p]});
return a;
}
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
var arr3 = expand(merge(arr1, arr2));
I don't know if this is the fastest way but it works for any number of input arrays; for example, this:
var a = expand(
merge(
[{name: "lang", value: "English"}, {name: "age", value: "18"}],
[{name: "childs", value: '5'}, {name: "lang", value: "German"}],
[{name: 'lang', value: 'Pancakes'}]
)
);
Gives you the same thing in a that was in arr3 with "German" replaced by "Pancakes".
This approach does assume that your objects all have the same {name: ..., value: ...} form of course.
You can see it working here (open your console please): http://jsfiddle.net/ambiguous/UtBbB/
Solution utilizing JS Map:
const merge = (arr1, arr2, prop) => {
const resultMap = new Map(arr1.map((item) => [item[prop], item]));
arr2.forEach((item) => {
const mapItem = resultMap.get(item[prop]);
if (mapItem) Object.assign(mapItem, item);
else resultMap.set(item[prop], item);
});
return [...resultMap.values()];
};
const arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"});
const arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"});
console.log(merge(arr1, arr2, "name"));
Which produces:
you could use following function
const merge = (a, b, key = "id") =>
a.filter(elem => !b.find(subElem => subElem[key] === elem[key]))
.concat(b);
and try
merge(arr1, arr2, 'name');
What about jQuery Merge?
http://api.jquery.com/jQuery.merge/
jsFiddle example here: http://jsfiddle.net/ygByD/
I was facing the same problem and based on guya answer I have extended the underscore library and also added a bit more of functionality that I was requiring. Here's the Gist.
/**
* Merges two object-like arrays based on a key property and also merges its array-like attributes specified in objectPropertiesToMerge.
* It also removes falsy values after merging object properties.
*
* #param firstArray The original object-like array.
* #param secondArray An object-like array to add to the firstArray.
* #param keyProperty The object property that will be used to check if objects from different arrays are the same or not.
* #param objectPropertiesToMerge The list of object properties that you want to merge. It all must be arrays.
* #returns The updated original array.
*/
function merge(firstArray, secondArray, keyProperty, objectPropertiesToMerge) {
function mergeObjectProperties(object, otherObject, objectPropertiesToMerge) {
_.each(objectPropertiesToMerge, function (eachProperty) {
object[eachProperty] = _.chain(object[eachProperty]).union(otherObject[eachProperty]).compact().value();
});
}
if (firstArray.length === 0) {
_.each(secondArray, function (each) {
firstArray.push(each);
});
} else {
_.each(secondArray, function (itemFromSecond) {
var itemFromFirst = _.find(firstArray, function (item) {
return item[keyProperty] === itemFromSecond[keyProperty];
});
if (itemFromFirst) {
mergeObjectProperties(itemFromFirst, itemFromSecond, objectPropertiesToMerge);
} else {
firstArray.push(itemFromSecond);
}
});
}
return firstArray;
}
_.mixin({
merge: merge
});
Hope it to be useful!
Regards!
I was recently stumped with this problem and I came here with the hope to have an answer but the accepted answer uses 2 for in loops which I wouldn't prefer. I finally managed to make my own. Doesn't depend on any library whatsoever:
function find(objArr, keyToFind){
var foundPos = objArr.map(function(ob){
return ob.type;
}).indexOf(keyToFind);
return foundPos;
}
function update(arr1,arr2){
for(var i = 0, len = arr2.length, current; i< len; i++){
var pos = find(arr1, arr2[i].name);
current = arr2[i];
if(pos !== -1) for(var key in arr2) arr1[pos][key] = arr2[key];
else arr1[arr1.length] = current;
}
}
This also maintains the order of arr1.
var arr1 = [{ name: "lang", value: "English" }, { name: "age", value: "18" }];
var arr2 = [{ name: "childs", value: '5' }, { name: "lang", value: "German" }];
function mergeArrayByProperty(arr1, arr2, prop) {
var newArray =
arr1.map(item => {
if (typeof (item[prop]) !== "undefined") {
var nItems = arr2.filter(ni => { if (typeof (ni[prop]) !== "undefined" && ni[prop] === item[prop]) return ni; });
if (nItems.length > 0) {
item = Object.assign({}, item, nItems[0]);
}
return item;
}
});
var arr2nd = arr2.flatMap(item => { return item[prop] });
var arr1nd = arr1.flatMap(item => { return item[prop] });
var nonDupArr = arr2nd.map(p => { if (arr1nd.includes(p) === false) return arr2.filter(i2 => { if (i2[prop] === p) return Object.assign({}, i2) })[0]; });
return newArray.concat(nonDupArr).filter(i=>{if(i !== null)return i})
}
var arr = mergeArrayByProperty(arr1, arr2, 'name');
console.log(arr)
I know this has been answered a lot, but I thought I would share.
This finds the duplicate key in the first array and merges the second arrays object having the same key value. If no value is found in the second array, it uses the original object. As you can see, lang is only found once in the result set; having german for the value.
Using the traditional for loop
const merge = (first, second) => {
for(let i=0; i<second.length; i++) {
first.push(second[i]);
}
return first;
}
console.log(merge([1,2,3], [4,5,6])); // [1,2,3,4,5,6]
console.log(merge(merge([1,2,3], [4,5,6]), [7,8,9])); // [1,2,3,4,5,6,7,8,9]
Using the Spread operator
const arr1 = [1,2,3];
const arr2 = [4,5,6];
// Merge arrays
const merged = [...arr1, ...arr2];
console.log(merged); // [1,2,3,4,5,6]
Using the concat() array method
const arr1 = [1,2,3];
const arr2 = [4,5,6];
// Merge arrays
const merged1 = arr1.concat(arr2); // bit confusing, seems like `arr1` itself is being modified but it's not
const merged2 = [].concat(arr1, arr2); // cleaner approach
console.log(merged1); // [1,2,3,4,5,6]
console.log(merged2); // [1,2,3,4,5,6]
Using the push() array method
const arr1A = [1,2,3];
const arr2A = [4,5,6];
const arr1B = [1,2,3];
const arr2B = [4,5,6];
const arr1C = [1,2,3];
const arr2C = [4,5,6];
const arr3C = [7,8,9];
// Merge arrays
const merged1 = arr1A.push(...arr2A);
// Merging without the ... on arr2B
const merged2 = arr1B.push(arr2B);
// Merge more than two arrays
arr1C.push(...[...arr2C, ...arr3C]);
console.log(arr1C); // [1,2,3,4,5,6,7,8,9]
console.log(merged1); // 6
console.log(arr1A); // [1,2,3,4,5,6]
console.log(arr2A); // [4,5,6]
console.log(merged2); // 4
console.log(arr1B); // [1,2,3,[4,5,6]]
console.log(arr2B); // [4,5,6]
Using the reduce() array method
const arr1 = [1,2,3];
const arr2 = [4,5,6];
const merged = arr2.reduce((arr, item) => {
arr.push(item);
return arr;
}, arr1);
console.log(merged); // [1,2,3,4,5,6]
To Summarize,
There are more than a couple of ways to merge two or more arrays into one in JavaScript.
Using the spread operator or the concat() method is the most optimal solution.
If you are sure that all inputs to merge are arrays, use spread operator. In case you are unsure, use the concat() method.
You can use the push() method to merge arrays when you want to change one of the input arrays to merge.
Using the reduce() method to merge arrays is a bit of overhead.
For more info refer the detailed blog here and video here
Off the top of my head - try jquery extend
var arr3 = jQuery.extend(arr1,arr2....)
Here I first filter arr1 based on element present in arr2 or not. If it's present then don't add it to resulting array otherwise do add. And then I append arr2 to the result.
arr1.filter(item => {
if (!arr2.some(item1=>item.name==item1.name)) {
return item
}
}).concat(arr2)
let mergeArray = arrA.filter(aItem => !arrB.find(bItem => aItem.name === bItem.name))
Posting this because unlike the previous answers this one is generic, no external libraries, O(n), actually filters out the duplicate and keeps the order the OP is asking for (by placing the last matching element in place of first appearance):
function unique(array, keyfunc) {
return array.reduce((result, entry) => {
const key = keyfunc(entry)
if(key in result.seen) {
result.array[result.seen[key]] = entry
} else {
result.seen[key] = result.array.length
result.array.push(entry)
}
return result
}, { array: [], seen: {}}).array
}
Usage:
var arr1 = new Array({name: "lang", value: "English"}, {name: "age", value: "18"})
var arr2 = new Array({name : "childs", value: '5'}, {name: "lang", value: "German"})
var arr3 = unique([...arr1, ...arr2], x => x.name)
/* arr3 == [
{name: "lang", value: "German"},
{name: "age", value: "18"},
{name: "childs", value: "5"}
]*/
const arr1 = [{ name: "lang", value: "English" }, { name: "age", value: "18" }];
const arr2 = [{ name: "childs", value: '5' }, { name: "lang", value: "German" }];
const mergeArrOfObjects = (dataset1, dataset2) => {
const map1 = new Map();
dataset1.map((d1, i) => {
map1.set(d1.name, i);
})
for (let d2 of dataset2) {
if (d2 && map1.has(d2.name)) {
dataset1[map1.get(d2.name)] = d2;
} else if(d2){
dataset1.push(d2);
}
}
return dataset1;
};
const arr3 = mergeArrOfObjects(arr1, arr2);
console.log(arr3);
Just use helprjs
const arr1 = [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jack'}];
const arr2 = [{ id: 2, name: 'Jane'}, { id: 3, name: 'Rod'}];
mergeArrays(arr1, arr2, "name");
// [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jane'}, { id: 3, name: 'Rod'}];
mergeArrays(arr1, arr2, "id");
// [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jack'}, { id: 3, name: 'Rod'}];
Check out the demo
Based on the question, I understand that there is a key that you want to use to override other attributes, not to merge them.
interface Foo {
name: string;
value: string;
}
var arr1: Foo[] = [
{ name: "lang", value: "English" },
{ name: "age", value: "18" },
];
var arr2: Foo[] = [
{ name: "childs", value: "5" },
{ name: "lang", value: "German" },
];
We can use combination of Map with Reduce to select the key that will be used to overwrite the record.
const merged: Foo[] = Array.from(
[...arr1, ...arr2].reduce(
(acc, curr) => acc.set(curr.name, curr),
new Map<Foo["name"], Foo>(),
)
.values(),
);
// [
// { name: "lang", value: "German" },
// { name: "age", value: "18" },
// { name: "childs", value: "5" },
// ];
var newArray = yourArray.concat(otherArray);
console.log('Concatenated newArray: ', newArray);

Categories