Merge 2 arrays of objects with different keys with lodash - javascript

I use Backbone/lodash for a project, and I wish merge 2 arrays of objects according to a specific value. With this example below, the merge is based on the same value with 2 different key (id and number).
Example
var people = [
{
id: "1",
name: "John"
},
{
id: "2",
name: "Jane"
}
];
var data = [
{
number: "2",
role: "Designer"
},
{
number: "1",
role: "Developer"
}
];
// Outpout
var merge = [
{
id: "1",
number: "1",
name: "John",
role: "Developer"
},
{
id: "2",
number: "2",
name: "Jane",
role: "Designer"
}
];

_.map(people, function(p){
return _.merge(
p,
_.find(data, {number: p.id})
)
})

I'm not aware of a lodash function that fulfills exactly this use case. However, your goal can be achieved with plain JavaScript and the lodash helpers _.assign() and _.values() pretty well:
var people = [{id: "1", name: "John"}, {id: "2", name: "Jane"}];
var data = [{number: "2", role: "Designer"}, {number: "1", role: "Developer"}];
var resultObj = {};
people.forEach(function(item) {
resultObj[item.id] = item;
});
data.forEach(function(item) {
resultObj[item.number] = _.assign({}, resultObj[item.number], item);
});
var result = _.values(resultObj);
console.log(result);
<script src='https://cdn.jsdelivr.net/lodash/4.17.1/lodash.min.js'></script>

Sort arrays by joining values.
zipWith to zip the arrays together.
defaults to merge each object iterated.
var people = [
{id: "1", name: "John"},
{id: "2", name: "Jane"}
];
var data = [
{number: "2", role: "Designer"},
{number: "1", role: "Developer"}
];
var result = _.zipWith(
_.sortBy(people, person => person.id),
_.sortBy(data, dataItem => dataItem.number),
(person, dataItem) => _.defaults(person, dataItem)
);
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.1/lodash.min.js"></script>

You can JavaScript to iterate over all your data with Array#map() and do a Array#find() to set all the p object properties where p.id === d.number:
var people = [{id: "1",name: "John"}, {id: "2",name: "Jane"}],
data = [{number: "2",role: "Designer"}, {number: "1",role: "Developer"}],
merge = data.map(d => {
var p = people.find(p => p.id === d.number);
p.number = d.number;
p.role = d.role;
return p;
});
// Outpout
console.log(merge);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

ES6 map.has is not a function when called in a reducer

I want to return an array of objects without any duplicate ids. If there are any, then take the first one we see. So, we should NOT see {id: "2", value: '10'}. Instead, the value should be "Italian". I have this code below, but I am getting an map.has is not a function error.
const arr1 = [{
id: "1",
value: "English"
},
{
id: "2",
value: "Italian"
}
];
const arr2 = [{
id: "2",
value: '10'
},
{
id: "3",
value: "German"
}
];
const concatArr = arr1.concat(arr2);
const mergedArr = [...concatArr.reduce((map, obj) => map.has(obj.id) ? "" : map.set(obj.id, obj), new Map()).values()];
console.log(mergedArr);
You need to always return a map not an empty string when the thing is already in the map.
const arr1 = [{
id: "1",
value: "English"
},
{
id: "2",
value: "Italian"
}
];
const arr2 = [{
id: "2",
value: '10'
},
{
id: "3",
value: "German"
}
];
const concatArr = arr1.concat(arr2);
const mergedArr = [...concatArr.reduce((map, obj) => map.has(obj.id) ? map : map.set(obj.id, obj), new Map()).values()];
console.log(mergedArr);
You can use array#reduce to uniquely identify each object with unique id in an object accumulator and then extract all values from this object using Object.values().
const arr1 = [{ id: "1", value: "English" }, { id: "2", value: "Italian" } ],
arr2 = [{ id: "2", value: '10' }, { id: "3", value: "German" } ],
result = Object.values(arr1.concat(arr2).reduce((r, o) => {
r[o.id] = r[o.id] || o;
return r;
},{}));
console.log(result);

Return new object by passing a callback function [duplicate]

This question already has answers here:
group objects based on callback function [duplicate]
(3 answers)
Closed 5 years ago.
I need to implement a function in which given a array of multiple objects:
var list = [
{id: "102", name: "Alice"},
{id: "205", name: "Bob", title: "Dr."},
{id: "592", name: "Clyde", age: 32}];
must return whatever the object property callback function asks for:
{
"3": [{id: "205", name: "Bob", title: "Dr."}],
"5": [{id: "102", name: "Alice"},
{id: "592", name: "Clyde", age: 32}]
}
groupBy(list, function(i) { return i.name.length; });
For example: This callback is asking to return the name.length of each object plus the original object as its values and if they are the same, return it in the same array.
var list = [
{id: "102", name: "Alice"},
{id: "205", name: "Bob", title: "Dr."},
{id: "592", name: "Clyde", age: 32}];
function groupBy(arr, cb) {
var newObj = {};
var newArr = [];
for (key of arr) {
newObj = cb(key);
}
return newObj;
}
groupBy(list, function(i) { return i.id; });
I don't have much going for my own solution and I've hit a wall. I would really appreciate it if you guys/gals would help me out.
Cheers
You need to create an empty array for each new key, you like to group.
The push the actual object to this array.
function groupBy(array, cb) {
var result = {},
key;
for (object of array) {
key = cb(object);
result[key] = result[key] || [];
result[key].push(object);
}
return result;
}
var list = [{ id: "102", name: "Alice" }, { id: "205", name: "Bob", title: "Dr." }, { id: "592", name: "Clyde", age: 32 }];
console.log(groupBy(list, function(o) { return o.name.length; }));
console.log(groupBy(list, function(o) { return o.id; }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to get distinct properties value from array? [duplicate]

This question already has answers here:
Remove duplicates form an array
(17 answers)
Closed 5 years ago.
I have this array?
var arr = [{id:"1",Name:"Tom"},
{id:"2",Name:"Jon"},
{id:"3",Name:"Tom"},
{id:"4",Name:"Jack"}]
From array above I need to fecth all existing Names distinct.
var result = getNamesDistinct(arr);
The result should contain result is:
["Tom","Jon","Jack"];
My question is how to get all existing Names from arr distinct?
If Set is available, you can simply do
new Set(arr.map(obj => obj.Name))
(pass the set to Array.from if you need an array)
You can do it via Set object
const arr = [
{ id: "1", Name: "Tom" },
{ id: "2", Name: "Jon" },
{ id: "3", Name: "Tom" },
{ id: "4", Name: "Jack" }
];
const uniqueNames = [...new Set(arr.map(item => item.Name))];
console.log(uniqueNames);
Or you can iterate over the array and add condition to get only unique names.
const arr = [
{ id: "1", Name: "Tom" },
{ id: "2", Name: "Jon" },
{ id: "3", Name: "Tom" },
{ id: "4", Name: "Jack" }
];
const uniqueNames = arr.reduce(function(arr, item) {
if(arr.indexOf(item.Name) === -1) {
arr.push(item.Name);
}
return arr;
}, []);
console.log(uniqueNames);
you can try this
var array = [{
id: "1",
Name: "Tom"
}, {
id: "2",
Name: "Jon"
}, {
id: "3",
Name: "Tom"
}, {
id: "4",
Name: "Jack"
}]
function uniqueNames(array) {
var newArray = [];
array.forEach((value, key) => {
newArray.push(value.Name)
});
return newArray
}
var myNewArray = uniqueNames(array)

Dynamically convert array to tree-like structure

Can someone show me the most efficient way to convert an array to a tree-like structure?
var array= [
{id: "1", name: "header1"},
{id: "2", name: "header2"},
{id: "1.1", name: "subheader1.1"},
{id: "1.2", name: "subheader1.2"},
{id: "2.1", name: "subheader2.1"},
{id: "2.2", name: "subheader2.2"},
{id: "1.1.1", name: "subheader1detail1"},
{id: "2.1.1", name: "subheader2detail2"}
];
Result array must be like this:
var array = [{
id: "1",
name: "header1",
items: [{
id: "1.1",
name: "subheader1.1",
items: [{
id: "1.1.1",
name: "subheader1detail1",
}]
}, {
id: "1.2",
name: "subheader1.2"
}]
}, {
id: "2",
name: "header2",
items: [{
id: "2.1",
name: "subheader2.1",
items: [{
id: "2.1.1",
name: "subheader2detail2",
}]
}, {
id: "2.2",
name: "subheader2.2"
}]
}]
Thanks in advance
You could use a tree and build the nested array upon. This proposal needs a sorted list.
Basically it looks for the parent of a node and if a node has no parent, then a root node is found and inserted into the result array. If a parent is found, then the actual node is inserted into the items property of the parent.
var array = [{ id: "1", name: "header1" }, { id: "2", name: "header2" }, { id: "1.1", name: "subheader1.1" }, { id: "1.2", name: "subheader1.2" }, { id: "2.1", name: "subheader2.1" }, { id: "2.2", name: "subheader2.2" }, { id: "1.1.1", name: "subheader1detail1" }, { id: "2.1.1", name: "subheader2detail2" }],
result = [];
array.forEach(function (a) {
var parent = a.id.split('.').slice(0, -1).join('.');
this[a.id] = { id: a.id, name: a.name };
if (parent) {
this[parent] = this[parent] || {};
this[parent].items = this[parent].items || [];
this[parent].items.push(this[a.id]);
} else {
result.push(this[a.id]);
}
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can try something like this:
I have added comments to explain the logic
var array= [
{id: "1", name: "header1"},
{id: "2", name: "header2"},
{id: "1.1", name: "subheader1.1"},
{id: "1.2", name: "subheader1.2"},
{id: "2.1", name: "subheader2.1"},
{id: "2.2", name: "subheader2.2"},
{id: "1.1.1", name: "subheader1detail1"},
{id: "2.1.1", name: "subheader2detail2"},
];
var result = {};
// Sort in case values are not in order.
// This is to ensure parent is rendered before child
array.sort(function(a, b) {
return a.id > b.id ? 1 : a.id - b.id ? -1 : 0
})
// Loop over sorted array to parse
.forEach(function(el) {
// Check if element does not exists to prevent duplicate
if (!result[el.id]) {
// if parent, push it
if (el.id.indexOf('.') === -1)
result[el.id] = el;
// If child, compute depth and search object to push to
else {
var ids = el.id.split('.');
var _id = '';
// temp variable to hold position to push
var r = result[ids[0]];
for (var i = 1; i < ids.length; i++) {
// Compute the object id
_id = (_id ? _id + '.' : _id) + ids[i - 1];
// initialize items
r.items = r.items || [];
// search in items to get object if exist
var o = r.items.find(x => x.id === _id);
// if object exists, assign it to temp variable
// If not, push to parent
if (o) r = o;
}
if (r) {
r.items = r.items || [];
r.items.push(el);
}
}
}
})
console.log(result)
Note: I have changed structure to hold an object instead of an array
Was fiddling with the problem here - sharing across a solution using Array.prototype.reduce and a local hash table - also sorting the result. Cheers!
var array=[{id:"2",name:"header2"},{id:"1",name:"header1"},{id:"1.1",name:"subheader1.1"},{id:"1.2",name:"subheader1.2"},{id:"2.2",name:"subheader2.2"},{id:"2.1",name:"subheader2.1"},{id:"1.1.1",name:"subheader1detail1"},{id:"2.1.1",name:"subheader2detail2"}];
var result = array.sort(function(a,b) {
return a.id - b.id;
}).reduce(function(hash) {
return function(prev, curr) {
var keys = curr.id.split('.');
hash[curr.id] = hash[curr.id] || {};
hash[curr.id] = {id: curr.id,name: curr.name};
if (keys && keys.length > 1) {
keys.pop();
var key = keys.join('.');
hash[key].items = hash[key].items || [];
hash[key].items.push(hash[curr.id]);
} else {
prev.push(hash[curr.id]);
}
return prev;
};
}(Object.create(null)), []);
console.log(result);
.as-console-wrapper {top: 0;max-height: 100%!important;}

Merge nested array in object in Javascript

I need to merge 2 objects with nested arrays
var dest = {
id: "865",
arr: [{
id: "123",
value: "First" }]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second" }]
};
to produce
merge = {
id: "865",
arr: [{id: "123",
value: "First"},
{id: "456",
value: "Second"}]
};
I tried using _.merge(dest, src) (using Lodash) and a couple of other methods, but seems like the 2nd object is overwriting the first one because it doesn't handle the nested array the way I want.
What is the best way to do this?
Thanks,
You can use Lodash _.mergeWith method:
var dest = {
id: "865",
arr: [{
id: "123",
value: "First"
}]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second"
}]
};
var merge = _.mergeWith({}, src, dest, function(a, b) {
if (_.isArray(a)) {
return b.concat(a);
}
});
console.log(merge);
It allows you to pass a customizer in order to merge the array in a "custom" way.
Here's the fiddle. Hope it helps.
You can use Object.assign()
var dest = {
id: "865",
arr: [{
id: "123",
value: "First" }]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second" }]
};
var merge = Object.assign({}, dest);
merge.arr.push(Object.assign({}, src.arr[0]));
src.arr[0].id = 789; // should not affect `merge.arr`
console.log(merge);
Without any libraries.
var dest = {
id: "865",
arr: [{
id: "123",
value: "First"
}]
};
var src = {
id: "865",
arr: [{
id: "456",
value: "Second"
}]
};
// 1
var resultOne = {
id: dest.id,
arr: src.arr.concat(dest.arr)
};
// 2
var resultTwo = Object.assign({}, src, {
arr: src.arr.concat(dest.arr)
});
// 3
var merge = function(obj1, obj2) {
return Object.keys(obj1).reduce(function(result, next) {
if (Array.isArray(obj1[next]) && Array.isArray(obj2[next])) {
result[next] = obj1[next].concat(obj2[next]);
} else if (obj1[next] && obj2[next]) {
result[next] = obj2[next];
}
return result;
}, {});
}
console.log(merge(src, dest));

Categories

Resources