merge and group object properties in javascript - javascript

I have an array of objects:
var test = [{name: 'lorem', age: 20, color:'red'}, {name: 'lorem', weight: 1, height:5} , {name: 'hello', ipsum : 'dolor'}]
I would like to merge and group them. Expected result is:
var test = [{name: 'lorem', age : 20, color: 'red', weight : 1, height : 5}, {name: 'hello', ipsum : 'dolor'}]
Solution can be in vanilla, lodash or JQuery ...
EDIT:
Sorry guys, I forgot to say that it has to be written in ES5
EDIT:
I almost rewrote gorak's propostion to ES5. I tried _.clone to avoid using spread opearator but it doesn't work
var r = _.values(_.reduce(test,function (acc, e) {
acc[e.name] = {...(acc[e.name] || {}), ...e}; // this line is still in ES6
return acc;
},{}));

You can make use of reduce and take Object.values. Here is a working example:
var test = [{name: 'lorem', age: 20, color:'red'}, {name: 'lorem', weight: 1, height:5} , {name: 'hello', ipsum : 'dolor'}];
var result = Object.values(test.reduce((acc, e)=>{
acc[e.name] = {...(acc[e.name] || {}), ...e};
return acc;
},{}));
console.log(result);
Also another approach could be to take Set values(unique names) and then use Object.assign to merge the filtered array. Give this a try:
var test = [{name: 'lorem', age: 20, color:'red'}, {name: 'lorem', weight: 1, height:5} , {name: 'hello', ipsum : 'dolor'}];
var result = [...new Set(test.map(({name})=>name))].map(n=>Object.assign(...test.filter(p=>p.name==n)));
console.log(result);

You can use below method
var test = [
{ name: 'lorem', age: 20, color: 'red' },
{ name: 'lorem', weight: 1, height: 5 },
{ name: 'hello', ipsum: 'dolor' },
]
const finalResult = test.reduce((result, obj) => {
if (result[obj.name]) {
result[obj.name] = {
...result[obj.name],
...obj,
}
} else {
result[obj.name] = { ...obj }
}
return result
}, {})
console.log(Object.values(finalResult))

This function will do the trick!
(The name is placed at the bottom of each object, but that doesn't matter)
(pure JS)
function mergeList(list) {
var temp = {};
list.forEach(elem => {
var name = elem.name;
delete elem.name;
temp[name] = {
...temp[name],
...elem
}
});
var merged = [];
Object.keys(temp).forEach(key => {
var object = temp[key];
object.name = key;
merged.push(object);
});
return merged;
}
var test = [{
name: 'lorem',
age: 20,
color: 'red'
}, {
name: 'lorem',
weight: 1,
height: 5
}, {
name: 'hello',
ipsum: 'dolor'
}];
console.log(mergeList(test));

Related

How can I remove the name field from a json array?

I have a json array like this:
(3) [{…}, {…}, {…}]
0: {Id: 1, Name: "bask"}
1: {Id: 2, Name: "voll"}
2: {Id: 3, Name: "badminton"}
I want to turn it into something like this:
{1:"bask",2:"voll",3:"badminton"}
You can use reduce to loop through array and build a object of desired key/value pair
let data = [{Id: 1, Name: "bask"},{Id: 2, Name: "voll"},{Id: 3, Name: "badminton"}]
let output = data.reduce((op, {Id, Name}) => {
op[Id] = Name
return op
},{})
console.log(output)
You could take Object.fromEntries with the maped key/value pairs.
var array = [{ Id: 1, Name: "bask" }, { Id: 2, Name: "voll" }, { Id: 3, Name: "badminton" }],
object = Object.fromEntries(array.map(({ Id, Name }) => [Id, Name]));
console.log(object);
You can check out the reduce() function!
let array = [
{Id: 1, Name: "bask"},
{Id: 2, Name: "voll"},
{Id: 3, Name: "badminton"}
];
console.log(_.reduce(array, function(result, obj){
result[obj.Id] = obj.Name;
return result;
}, {}));
You can checkout lodash an awesome library with many other such utilities!
You can do this with reduce():
var a = [
{Id: 1, Name: "bask"},
{Id: 2, Name: "voll"},
{Id: 3, Name: "badminton"}
]
b = a.reduce((acc, item) => {
acc[item.Id] = item.Name;
return acc;
}
console.log(b);
You can do it in different ways, here one of them.
let dataArray = [
{id: 1, name: 'bask'},
{id: 2, name: 'voll'},
{id: 3, name: 'badminton'}
]
let ouputObject = {}
dataArray.map(data => {
ouputObject[`${data.id}`] = data.name
})
console.log(ouputObject)
outputObject will be
Object {
1: "bask",
2: "voll",
3: "badminton"
}
Using Array.reduce() :
var arr = [{
Id: 1,
Name: "bask"
}, {
Id: 2,
Name: "voll"
}, {
Id: 3,
Name: "badminton"
}];
var reduceObj = arr.reduce(function(result, currentElement) {
result[currentElement.Id] = currentElement.Name;
return result;
}, {});
console.log(reduceObj);
Using Array.map() :
var arr = [{
Id: 1,
Name: "bask"
}, {
Id: 2,
Name: "voll"
}, {
Id: 3,
Name: "badminton"
}];
var mapObject = {}
arr.map(obj => {
mapObject[obj.Id] = obj.Name
})
console.log(mapObject);

Get missing objects from 2 arrays of objects

let selected = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'}
];
let all = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'},
{id: 32, name: 'United States'},
{id: 40, name: 'China'}
]
How do I get non-selected countries from all objects and print it out in another variable? Based on id key of those which are in selected array?
You need to find all objects that aren't contained in selected and then do something with them:
let nonSelectedItems = all.filter(obj => selected.every(s => s.id !== obj.id));
//do stuff with non-selected items
You can use filter and find, so as soon as element with same id is found in selected it will filter out that element from all. You can also use some instead of find.
let selected = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'}
];
let all = [
{id: 15, name: 'Canada'},
{id: 25, name: 'Germany'},
{id: 32, name: 'United States'},
{id: 40, name: 'China'}
]
var r = all.filter(e => !selected.find(a => e.id === a.id));
console.log(r)
Generate an object which holds id as a property using Array#reduce method(which helps to speed up since you need to iterate over and over) and use Array#filter method to filter elements from all array.
// generate the object reference
let ref = selected.reduce(function(obj, o) {
// define property
obj[o.id] = true;
// return object property
return obj;
// set initial value as an object
}, {});
// filter out array elements
let res = all.filter(function(o) {
return !ref[o.id]
})
let selected = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}];
let all = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}, {
id: 32,
name: 'United States'
}, {
id: 40,
name: 'China'
}]
let ref = selected.reduce(function(obj, o) {
obj[o.id] = true;
return obj;
}, {});
console.log(
all.filter(function(o) {
return !ref[o.id]
})
)
With ES6 arrow function :
let ref = selected.reduce((obj, o) => (obj[o.id] = true, obj), {});
let res = all.filter(o => !ref[o.id]);
let selected = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}];
let all = [{
id: 15,
name: 'Canada'
}, {
id: 25,
name: 'Germany'
}, {
id: 32,
name: 'United States'
}, {
id: 40,
name: 'China'
}]
let ref = selected.reduce((obj, o) => (obj[o.id] = true, obj), {});
console.log(
all.filter(o => !ref[o.id])
)

Flatten a nested array of objects over a array field

Have an object a1 =
[{name:'x',age:21, addr:[{flat:1,add:'xyz'},{flat:2,add:'xsr'}]},
{name:'y',age:22, addr:[{flat:3,add:'xyz1'},{flat:4,add:'xsr1'}]]
Desired output:
[{name:'x',age:21, addr:{flat:1,add:'xyz'}},
{name:'x',age:21, addr:{flat:2,add:'xsr'}},
{name:'y',age:22, addr:{flat:3,add:'xyz1'},
{name:'y',age:22, addr:{flat:4,add:'xsr1'}]
Please suggest! I am trying to accomplish this using lodash/underscore.
Map every item in the original array to a new array, with a number of items according to the number of addr fields. Using concat flatten everything to a new array.
You can create new items using Object.assign() if you're transpiling to ES5 with babel or similar, as it's not supported by IE. However, since you're using angular, you can use angular.extend instead.
ES5:
var arr = [
{name:'x',age:21, addr:[{flat:1,add:'xyz'},{flat:2,add:'xsr'}]},
{name:'y',age:22, addr:[{flat:3,add:'xyz1'},{flat:4,add:'xsr1'}]}
];
var result = [].concat.apply([], arr.map(function(item) {
return item.addr.map(function(addr) {
return angular.extend({}, item, { addr: addr });
});
}));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
ES6:
const arr = [
{name:'x',age:21, addr:[{flat:1,add:'xyz'},{flat:2,add:'xsr'}]},
{name:'y',age:22, addr:[{flat:3,add:'xyz1'},{flat:4,add:'xsr1'}]}
];
const result = [].concat(...arr.map((item) => item.addr.map((addr) => Object.assign({}, item, { addr }))));
console.log(result);
You could iterate the array and the inner array for building the wanted result.
var array = [{ name: 'x', age: 21, addr: [{ flat: 1, add: 'xyz' }, { flat: 2, add: 'xsr' }] }, { name: 'y', age: 22, addr: [{ flat: 3, add: 'xyz1' }, { flat: 4, add: 'xsr1' }] }],
result = array.reduce(function (r, a) {
return r.concat(a.addr.map(function (b) {
return { name: a.name, age: a.age, addr: b };
}));
}, []);
console.log(result);
ES6
var array = [{ name: 'x', age: 21, addr: [{ flat: 1, add: 'xyz' }, { flat: 2, add: 'xsr' }] }, { name: 'y', age: 22, addr: [{ flat: 3, add: 'xyz1' }, { flat: 4, add: 'xsr1' }] }],
result = array.reduce((r, a) => r.concat(a.addr.map(b => ({ name: a.name, age: a.age, addr: b }))), []);
console.log(result);
You can use reduce(), forEach() and Object.assign() to return desired result.
var data = [{name:'x',age:21, addr:[{flat:1,add:'xyz'},{flat:2,add:'xsr'}]},
{name:'y',age:22, addr:[{flat:3,add:'xyz1'},{flat:4,add:'xsr1'}]}];
var result = data.reduce(function(r, o) {
o.addr.forEach(function(e) {
r.push(Object.assign({name: o.name, age: o.age}, e));
})
return r;
}, []);
console.log(result)

Underscore JS nested each merge objects

I have two arrays of objects that I'm trying to merge
var status = [
{name: 'status_1', pk:1 }, {name: 'status_2', pk:2 }
]
var to_be_merged = [
{'status_pk': 1, 'value': 10}, {'status_pk': 2, value: 20}
]
desired result
var status = [
{name:'status_1',
pk:1,
to_be_merged:{
'status_pk': 1, 'value': 10
}, {
name: 'status_2',
pk:2,
to_be_merged: {
'status_pk': 2, value: 20}
},
]
I've tried using nested _.each but I lost the scope of the above each
_.each(status, function(status) {
var objs = _.each(to_be_merged, function(x) {
if (x.status_pk == status.pk) {
// do something
}
})
})
map should help you. Combine it with merge and where to merge the two items together.
_.each(status, function(item) {
item['to_be_merged'] = _.find(to_be_merged, function(other) {
return other['status_pk'] === item.pk;
});
})
This is slightly more verbose, but so you can see what's going on:
var stat = {
s: [
{name: 'status_1', pk:1 }, {name: 'status_2', pk:2 }
],
m: [
{'status_pk': 1, 'value': 10}, {'status_pk': 2, value: 20}
]}
var merged = []
_(stat.s).each(function(s){
_(stat.m).each(function(m){
var obj = {};
if(s.pk == m.status_pk){
obj.name = s.name;
obj.pk = s.pk;
obj.to_be_meged = m
merged.push(obj);
}
})
})
console.log(merged);
DEMO

Merging/extend javascript object arrays based on join of a key property in each

I am wanting to merge the following object arrays, by first joining on the id property
var arr1 = [{
id: 1,
name: 'fred',
title: 'boss'
},{
id: 2,
name: 'jim',
title: 'nobody'
},{
id: 3,
name: 'bob',
title: 'dancer'
}];
var arr2 = [{
id: 1,
wage: '300',
rate: 'day'
},{
id: 2,
wage: '10',
rate: 'hour'
},{
id: 3,
wage: '500',
rate: 'week'
}];
So the result would be
[{
id: 1,
name: 'fred',
title: 'boss',
wage: '300',
rate: 'day'
},{
id: 2,
name: 'jim',
title: 'nobody',
wage: '10',
rate: 'hour'
},{
id: 3,
name: 'bob',
title: 'dancer',
wage: '500',
rate: 'week'
}]
I would like to avoid using js frameworks (if possible), although ExtJs is already part of the project.
AT the moment I have a loop with an inner loop that if the keys match it copies the properties and breaks out of the inner loop to start the next outer loop.
Any better suggestions?
Like this?
var combined = [];
function findSecond(id,second){
for (var i=0;i<second.length;i++){
if(second[i].id === id){
return second[i];
}
}
return null
}
while (el = arr1.pop()){
var getSec = findSecond(el.id,arr2);
if (getSec){
for (var l in getSec){
if (!(l in el)) {
el[l] = getSec[l];
}
}
combined.push(el);
}
}
If the arrays have the same length, and the id's are equal, a simpler merge will do:
function merge(a1,a2) {
var i = -1;
while ((i = i+1)<a1.length) {
for (var l in a2[i]) {
if (!(l in a1[i] )) {
a1[i][l] = a2[i][l];
}
}
}
return a1;
}
Here's a working example
[Edit 2016/07/30] Added a snippet using more functional approach and, based on #djangos comment, an extra method to combine both arrays.
(function() {
var alert = function(str) {document.querySelector('#result').textContent += str + '\n';};
var arrays = getArrays();
alert('Combine on id (shared id\'s):')
alert(JSON.stringify(combineById(arrays.arr1, arrays.arr2), null, ' '));
alert('\nCombine on id (all id\'s):')
alert(JSON.stringify(combineBothById(arrays.arr1, arrays.arr2), null, ' '));
// for combineBothById the parameter order isn't relevant
alert('\nCombine on id (all id\'s, demo parameter order not relevant):')
alert(JSON.stringify(combineBothById(arrays.arr2, arrays.arr1), null, ' '));
// combine first array with second on common id's
function combineById(arr1, arr2) {
return arr1.map(
function (el) {
var findInB = this.filter(function (x) {return x.id === el.id;});
if (findInB.length) {
var current = findInB[0];
for (var l in current) {
if (!el[l]) {el[l] = current[l];}
}
}
return el;
}, arr2);
}
// combine first array with second on all id's
function combineBothById(arr1, arr2) {
var combined = arr1.map(
function (el) {
var findInB = this.filter(function (x) {return x.id === el.id;});
if (findInB.length) {
var current = findInB[0];
for (var l in current) {
if (!el[l]) {el[l] = current[l];}
}
}
return el;
}, arr2);
combined = combined.concat(arr2.filter(
function (el) {
return !this.filter(function (x) {return x.id === el.id;}).length;
}, combined));
return combined;
}
function getArrays() {
return {
arr1: [{
id: 1,
name: 'fred',
title: 'boss'
}, {
id: 2,
name: 'jim',
title: 'nobody'
}, {
id: 3,
name: 'bob',
title: 'dancer'
}],
arr2: [{
id: 1,
wage: '300',
rate: 'day'
}, {
id: 2,
wage: '10',
rate: 'hour'
}, {
id: 4,
wage: '500',
rate: 'week'
}]
};
}
}());
<pre id="result"></pre>
You can merge two arrays by id column with Alasql library:
var res = alasql('SELECT * FROM ? arr1 JOIN ? arr2 USING id', [arr1,arr2]);
Try this example at jsFiddle.
try this...
var arr1 = [{
id: 1,
name: 'fred',
title: 'boss'
},{
id: 2,
name: 'jim',
title: 'nobody'
},{
id: 3,
name: 'bob',
title: 'dancer'
}];
var arr2 = [{
id: 1,
wage: '300',
rate: 'day'
},{
id: 2,
wage: '10',
rate: 'hour'
},{
id: 3,
wage: '500',
rate: 'week'
}];
let arr5 = arr1.map((item, i) => Object.assign({}, item, arr2[i]));
console.log(arr5)

Categories

Resources