Grouping array with map method - javascript

I want to group array of objects by its key,
The Original form;
data = [
{'id': 1, 'name': 'karthik'},
{'id': 1, 'age': 31},
{'id': 2, 'name': 'ramesh'},
{'id': 2, 'age': 22}
];
To transform in to,
groupedData = [
{'id': 1, 'name': 'karthik', 'age': 31},
{'id': 2, 'name': 'ramesh', 'age': 22}
];
What I tried,
this.data.map(item => item.id)
.filter((item, index, all) => all.indexOf(item) === index);
console.log(this.data);

Use reduce instead of map.
const groupedData = Object.values(this.data.reduce((a, { id, ...r }) => ({ ...a, [id]: { ...(a[id] || { id }), ...r }}), {}));
How this works:
Firstly, using reduce is far easier than any map solution because it allows us to have an accumulator value a.
Then, we extract the id, and the rest of the properties (because we don't know what they're called).
We return a, with the property keyed with the value of id being either the existing property or a new one with id, as well as the rest of the properties.

You can use reduce to create an object ( a table ) for each id.
const groupMap = data.reduce((group, currentData) => {
const id = currentData['id']
group[id] = { ...(group[id] || {}), ...currentData }
return group
} ,{})
what this returns is something like:
{
"1": {
"id": 1,
"name": "karthik",
"age": 31
},
"2": {
"id": 2,
"name": "ramesh",
"age": 22
}
}
group[id] = { ...(group[id] || {}), ...currentData } is basically "if you already saw this id, just merge the previous data with the current data"
then you can get the final result calling Object.values
const groupedData = Object.values(groupMap)
which just get the values of the object created above.

Try this
data = data.reduce((total, current, index) => {
for(let key in current) total[index][key] = current[key]
return total
}, [])

This function will help you)
function mergeobj(arrofobj) {
arrofobj.map((item, index) => {
for (let i = 0; i < arrofobj.length; i++) {
if(i==index)continue;
if (item['id'] == arrofobj[i]['id']) {
item = Object.assign(item, arrofobj[i]);
arrofobj.splice(i, 1);
--i;
}
}
return item;
})
return arrofobj; }

Related

How can i compare two arrays of different length and keys on the basis of matching key values?

I have tried this, but it is giving the correct result
Array1
Array1: [
{
id: 2,
city: 'washington',
code: 0099,
room: 5,
...
},
{
...
},
...
]
Array 2
Array2: [
{
"id": 2,
"name": "john"
"number": 727625,
"etage": 5,
"status": 0,
...
},
{
...
},
...
]
My Code
let Result = [];
if (Array1 && Array1.length > 0 && Array2 && Array2.length > 0) {
Array1.forEach((arr1, index) => {
Array2.forEach((arr2, index) =>{
if (arr1.id === arr2.id && arr1.room === arr2.etage) {
Result.push(arr1)
}
})
})
}
console.log(Result)
What I want ?
I want items(objects) of Array1 by comparing both arrays, where
both have same id's && room from Array1's object equal to the etage
from Array2's object.
Please guide me, how can I do this in ES6 style in React js?
The main problem with nested loops is the unnecessary iteration of each element of the first array and multiple iterations of the second array.
This approach takes two loops, one for generating all keys from array2 and the other to filter array1.
You could take a Set for compound key of id and etage and filte the array for getting the items with same id and room.
const
getKey = (...values) => values.join('|'),
keys = new Set(array2.map(({ id, etage }) => getKey(id, etage))),
result = array1.filter(({ id, room }) => keys.has(getKey(id, room));
With condition
room > etage
and a Map.
const
ids = array2.reduce(
(m, { id, etage }) => m.set(id, Math.min(etage, m.get(id) || 0)),
new Map
),
result = array1.filter(({ id, room }) => room > ids.get(id));
I'd do something like this:
Array1= [
{
id: 2,
city: 'washington',
code: 0099,
room: 5,
}
];
Array2= [
{
"id": 2,
"name": "john",
"number": 727625,
"etage": 5,
},
];
const result = Array1.filter(a1 => Array2.find(a2 => a1.id == a2.id) && Array2.find(a2 => a1.room == a2.etage));
console.log(result);
That will return a filtered array by room, etage and id.
You can use filter and some ES6 methods:
const arr1 = [
{
id: 1,
room: 1
},
{
id: 2,
room: 5
},
{
id: 3,
room: 3
}
];
const arr2 = [
{
id: 0,
etage: 0
},
{
id: 2,
etage: 5
},
{
id: 3,
etage: 3
}
];
const getTheSame = (arr1, arr2) => {
return arr1.filter(o1 =>
arr2.some(o2 => o1.id === o2.id && o1.room === o2.etage)
);
};
console.log("Result: ", getTheSame(arr1, arr2));

Get sub array based on array value

I have a categories array:
{id: 1, catName: "test", subCategories: Array(2)}
I need to retrieve the subCategories array based on the id of the category.
This return the entire category object, how can I change it to only return the subCategories array?
const subCategories = categoriesWithSub.filter(category => {
return category.id === departments.catId;
});
Destructure a find call:
const { subCategories } = categoriesWithSub.find(({ id }) => id === departments.catId);
Use Array#find to get the object and get the array using dot notation or bracket notation.
const subCategories = (categoriesWithSub.find(category => {
return category.id === departments.catId;
}) || {}).subCategories; // if find returns undefined then use an empty object to avoid error, alternately you can use if condition
You can use reduce.
const subCategories = categoriesWithSub.reduce((acc, category) => {
if (category.id === departments.catId) {
return acc.concat(category. subCategories)
}
return acc;
}, []);
Side note, reduce is a really powerful tool. find, map, forEach, filter are like shorthand versions of reduce for specific tasks.
Try this:
let categories = [ {id: 1, catName: "test", subCategories: ["test1","test2"]}, {id: 2, catName: "test", subCategories: Array(2)} ]
let departments = { catId: 1 }
const subCategories = categories.find(category => {
return category.id === departments.catId;
}).subCategories;
console.log( subCategories );
Try this
function getSubCategory() {
var categoriesWithSub = [{ id: 1, catName: "test", subCategories: ["test1","test2"] }, { id: 2, catName: "test", subCategories: ["test1","test2"] }]
var catId = 1;
for (var category = 0; category < categoriesWithSub.length; category++) {
if (categoriesWithSub[category].id == catId)
return categoriesWithSub[category].subCategories;
}
}

How do I recursively use Array.prototype.find() while returning a single object?

The bigger problem I am trying to solve is, given this data:
var data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4, children: [
{ id: 6 },
{ id: 7, children: [
{id: 8 },
{id: 9 }
]}
]},
{ id: 5 }
]
I want to make a function findById(data, id) that returns { id: id }. For example, findById(data, 8) should return { id: 8 }, and findById(data, 4) should return { id: 4, children: [...] }.
To implement this, I used Array.prototype.find recursively, but ran into trouble when the return keeps mashing the objects together. My implementation returns the path to the specific object.
For example, when I used findById(data, 8), it returns the path to { id: 8 }:
{ id: 4, children: [ { id: 6 }, { id: 7, children: [ { id: 8}, { id: 9] } ] }
Instead I would like it to simply return
{ id: 8 }
Implementation (Node.js v4.0.0)
jsfiddle
var data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4, children: [
{ id: 6 },
{ id: 7, children: [
{id: 8 },
{id: 9 }
]}
]},
{ id: 5 }
]
function findById(arr, id) {
return arr.find(a => {
if (a.children && a.children.length > 0) {
return a.id === id ? true : findById(a.children, id)
} else {
return a.id === id
}
})
return a
}
console.log(findById(data, 8)) // Should return { id: 8 }
// Instead it returns the "path" block: (to reach 8, you go 4->7->8)
//
// { id: 4,
// children: [ { id: 6 }, { id: 7, children: [ {id: 8}, {id: 9] } ] }
The problem what you have, is the bubbling of the find. If the id is found inside the nested structure, the callback tries to returns the element, which is interpreted as true, the value for the find.
The find method executes the callback function once for each element present in the array until it finds one where callback returns a true value. [MDN]
Instead of find, I would suggest to use a recursive style for the search with a short circuit if found.
var data = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4, children: [{ id: 6 }, { id: 7, children: [{ id: 8 }, { id: 9 }] }] }, { id: 5 }];
function findById(data, id) {
function iter(a) {
if (a.id === id) {
result = a;
return true;
}
return Array.isArray(a.children) && a.children.some(iter);
}
var result;
data.some(iter);
return result
}
console.log(findById(data, 8));
Let's consider the implementation based on recursive calls:
function findById(tree, nodeId) {
for (let node of tree) {
if (node.id === nodeId) return node
if (node.children) {
let desiredNode = findById(node.children, nodeId)
if (desiredNode) return desiredNode
}
}
return false
}
Usage
var data = [
{ id: 1 }, { id: 2 }, { id: 3 },
{ id: 4, children: [
{ id: 6 },
{ id: 7,
children: [
{ id: 8 },
{ id: 9 }
]}]},
{ id: 5 }
]
findById(data, 7 ) // {id: 7, children: [{id: 8}, {id: 9}]}
findById(data, 5 ) // {id: 5}
findById(data, 9 ) // {id: 9}
findById(data, 11) // false
To simplify the picture, imagine that:
you are the monkey sitting on the top of a palm tree;
and searching for a ripe banana, going down the tree
you are in the end and searches aren't satisfied you;
come back to the top of the tree and start again from the next branch;
if you tried all bananas on the tree and no one is satisfied you, you just assert that ripe bananas don't grow on this this palm;
but if the banana was found you come back to the top and get pleasure of eating it.
Now let's try apply it to our recursive algorithm:
Start iteration from the top nodes (from the top of the tree);
Return the node if it was found in the iteration (if a banana is ripe);
Go deep until item is found or there will be nothing to deep. Hold the result of searches to the variable (hold the result of searches whether it is banana or just nothing and come back to the top);
Return the searches result variable if it contains the desired node (eat the banana if it is your find, otherwise just remember not to come back down by this branch);
Keep iteration if node wasn't found (if banana wasn't found keep testing other branches);
Return false if after all iterations the desired node wasn't found (assert that ripe bananas doesn't grow on this tree).
Keep learning recursion it seems not easy at the first time, but this technique allows you to solve daily issues in elegant way.
I would just use a regular loop and recursive style search:
function findById(data, id) {
for(var i = 0; i < data.length; i++) {
if (data[i].id === id) {
return data[i];
} else if (data[i].children && data[i].children.length && typeof data[i].children === "object") {
findById(data[i].children, id);
}
}
}
//findById(data, 4) => Object {id: 4, children: Array[2]}
//findById(data, 8) => Object {id: 8}
I know this is an old question, but as another answer recently revived it, I'll another version into the mix.
I would separate out the tree traversal and testing from the actual predicate that we want to test with. I believe that this makes for much cleaner code.
A reduce-based solution could look like this:
const nestedFind = (pred) => (xs) =>
xs .reduce (
(res, x) => res ? res : pred(x) ? x : nestedFind (pred) (x.children || []),
undefined
)
const findById = (testId) =>
nestedFind (({id}) => id == testId)
const data = [{id: 1}, {id: 2}, {id: 3}, {id: 4, children: [{id: 6}, {id: 7, children: [{id: 8}, {id: 9}]}]}, {id: 5}]
console .log (findById (8) (data))
console .log (findById (4) (data))
console .log (findById (42) (data))
.as-console-wrapper {min-height: 100% !important; top: 0}
There are ways we could replace that reduce with an iteration on our main list. Something like this would do the same:
const nestedFind = (pred) => ([x = undefined, ...xs]) =>
x == undefined
? undefined
: pred (x)
? x
: nestedFind (pred) (x.children || []) || nestedFind (pred) (xs)
And we could make that tail-recursive without much effort.
While we could fold the two functions into one in either of these, and achieve shorter code, I think the flexibility offered by nestedFind will make other similar problems easier. However, if you're interested, the first one might look like this:
const findById = (id) => (xs) =>
xs .reduce (
(res, x) => res ? res : x.id === id ? x : findById (id) (x.children || []),
undefined
)
const data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{
id: 4,
children: [{ id: 6 }, { id: 7, children: [{ id: 8 }, { id: 9 }] }]
},
{ id: 5 }
];
// use Array.flatMap() and Optional chaining to find children
// then Filter undefined results
const findById = (id) => (arr) => {
if (!arr.length) return null;
return (
arr.find((obj) => obj.id === id) ||
findById(id)(arr.flatMap((el) => el?.children).filter(Boolean))
);
};
const findId = (id) => findById(id)(data);
console.log(findId(12)); /* null */
console.log(findId(8)); /* { id: 8 } */
Based on Purkhalo Alex solution,
I have made a modification to his function to be able to find the ID recursively based on a given dynamic property and returning whether the value you want to find or an array of indexes to recursively reach to the object or property afterwards.
This is like find and findIndex together through arrays of objects with nested arrays of objects in a given property.
findByIdRecursive(tree, nodeId, prop = '', byIndex = false, arr = []) {
for (let [index, node] of tree.entries()) {
if (node.id === nodeId) return byIndex ? [...arr, index] : node;
if (prop.length && node[prop].length) {
let found = this.findByIdRecursive(node[prop], nodeId, prop, byIndex, [
...arr,
index
]);
if (found) return found;
}
}
return false;
}
Now you can control the property and the type of finding and get the proper result.
This can be solved with reduce.
const foundItem = data.reduce(findById(8), null)
function findById (id) {
const searchFunc = (found, item) => {
const children = item.children || []
return found || (item.id === id ? item : children.reduce(searchFunc, null))
}
return searchFunc
}
You can recursively use Array.prototype.find() in combination with Array.prototype.flatMap()
const findById = (a, id, p = "children", u) =>
a.length ? a.find(o => o.id === id) || findById(a.flatMap(o => o[p] || []), id) : u;
const tree = [{id:1}, {id:2}, {id:3}, {id:4, children:[{id: 6}, {id:7, children:[{id:8}, {id:9}]}]}, {id:5}];
console.log(findById(tree, 9)); // {id:9}
console.log(findById(tree, 10)); // undefined
If one wanted to use Array.prototype.find this is the option I chose:
findById( my_big_array, id ) {
var result;
function recursiveFind( haystack_array, needle_id ) {
return haystack_array.find( element => {
if ( !Array.isArray( element ) ) {
if( element.id === needle_id ) {
result = element;
return true;
}
} else {
return recursiveFind( element, needle_id );
}
} );
}
recursiveFind( my_big_array, id );
return result;
}
You need the result variable, because without it, the function would return the top level element in the array that contains the result, instead of a reference to the deeply nested object containing the matching id, meaning you would need to then filter it out further.
Upon looking through the other answers, my approach seems very similar to Nina Scholz's but instead uses find() instead of some().
Here is a solution that is not the shortest, but divides the problem into recursive iteration and finding an item in an iterable (not necessarily an array).
You could define two generic functions:
deepIterator: a generator that traverses a forest in pre-order fashion
iFind: a finder, like Array#find, but that works on an iterable
function * deepIterator(iterable, children="children") {
if (!iterable?.[Symbol.iterator]) return;
for (let item of iterable) {
yield item;
yield * deepIterator(item?.[children], children);
}
}
function iFind(iterator, callback, thisArg) {
for (let item of iterator) if (callback.call(thisArg, item)) return item;
}
// Demo
var data = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4, children: [{ id: 6 }, { id: 7, children: [{ id: 8 }, { id: 9 }] }] }, { id: 5 }];
console.log(iFind(deepIterator(data), ({id}) => id === 8));
In my opinion, if you want to search recursively by id, it is better to use an algorithm like this one:
function findById(data, id, prop = 'children', defaultValue = null) {
for (const item of data) {
if (item.id === id) {
return item;
}
if (Array.isArray(item[prop]) && item[prop].length) {
const element = this.findById(item[prop], id, prop, defaultValue);
if (element) {
return element;
}
}
}
return defaultValue;
}
findById(data, 2);
But I strongly suggest using a more flexible function, which can search by any key-value pair/pairs:
function findRecursive(data, keyvalues, prop = 'children', defaultValue = null, _keys = null) {
const keys = _keys || Object.keys(keyvalues);
for (const item of data) {
if (keys.every(key => item[key] === keyvalues[key])) {
return item;
}
if (Array.isArray(item[prop]) && item[prop].length) {
const element = this.findRecursive(item[prop], keyvalues, prop, defaultValue, keys);
if (element) {
return element;
}
}
}
return defaultValue;
}
findRecursive(data, {id: 2});
you can use this function:
If it finds the item so the item returns. But if it doesn't find the item, tries to find the item in sublist.
list: the main/root list
keyName: the key that you need to find the result up to it for example 'id'
keyValue: the value that must be searched
subListName: the name of 'child' array
callback: your callback function which you want to execute when item is found
function recursiveSearch(
list,
keyName = 'id',
keyValue,
subListName = 'children',
callback
) {
for (let i = 0; i < list.length; i++) {
const x = list[i]
if (x[keyName] === keyValue) {
if (callback) {
callback(list, keyName, keyValue, subListName, i)
}
return x
}
if (x[subListName] && x[subListName].length > 0) {
const item = this.recursiveSearch(
x[subListName],
keyName,
keyValue,
subListName,
callback
)
if (!item) continue
return item
}
}
},
Roko C. Buljan's solution, but more readable one:
function findById(data, id, prop = 'children', defaultValue = null) {
if (!data.length) {
return defaultValue;
}
return (
data.find(el => el.id === id) ||
findById(
data.flatMap(el => el[prop] || []),
id
)
);
}

How can I get a unique array based on object property using underscore

I have an array of objects and I want to get a new array from it that is unique based only on a single property, is there a simple way to achieve this?
Eg.
[ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
Would result in 2 objects with name = bill removed once.
Use the uniq function
var destArray = _.uniq(sourceArray, function(x){
return x.name;
});
or single-line version
var destArray = _.uniq(sourceArray, x => x.name);
From the docs:
Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iterator function.
In the above example, the function uses the objects name in order to determine uniqueness.
If you prefer to do things yourself without Lodash, and without getting verbose, try this uniq filter with optional uniq by property:
const uniqFilterAccordingToProp = function (prop) {
if (prop)
return (ele, i, arr) => arr.map(ele => ele[prop]).indexOf(ele[prop]) === i
else
return (ele, i, arr) => arr.indexOf(ele) === i
}
Then, use it like this:
const obj = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
obj.filter(uniqFilterAccordingToProp('abc'))
Or for plain arrays, just omit the parameter, while remembering to invoke:
[1,1,2].filter(uniqFilterAccordingToProp())
If you want to check all the properties then
lodash 4 comes with _.uniqWith(sourceArray, _.isEqual)
A better and quick approach
var table = [
{
a:1,
b:2
},
{
a:2,
b:3
},
{
a:1,
b:4
}
];
let result = [...new Set(table.map(item => item.a))];
document.write(JSON.stringify(result));
Found here
You can use the _.uniqBy function
var array = [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'bill' },{ id: 2, name: 'bill' } ];
var filteredArray = _.uniqBy(array,function(x){ return x.id && x.name;});
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
In the above example, filtering is based on the uniqueness of combination of properties id & name.
if you have multiple properties for an object.
then to find unique array of objects based on specific properties, you could follow this method of combining properties inside _.uniqBy() method.
I was looking for a solution which didn't require a library, and put this together, so I thought I'd add it here. It may not be ideal, or working in all situations, but it's doing what I require, so could potentially help someone else:
const uniqueBy = (items, reducer, dupeCheck = [], currentResults = []) => {
if (!items || items.length === 0) return currentResults;
const thisValue = reducer(items[0]);
const resultsToPass = dupeCheck.indexOf(thisValue) === -1 ?
[...currentResults, items[0]] : currentResults;
return uniqueBy(
items.slice(1),
reducer,
[...dupeCheck, thisValue],
resultsToPass,
);
}
const testData = [
{text: 'hello', image: 'yes'},
{text: 'he'},
{text: 'hello'},
{text: 'hell'},
{text: 'hello'},
{text: 'hellop'},
];
const results = uniqueBy(
testData,
item => {
return item.text
},
)
console.dir(results)
In case you need pure JavaScript solution:
var uniqueProperties = {};
var notUniqueArray = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ];
for(var object in notUniqueArray){
uniqueProperties[notUniqueArray[object]['name']] = notUniqueArray[object]['id'];
}
var uniqiueArray = [];
for(var uniqueName in uniqueProperties){
uniqiueArray.push(
{id:uniqueProperties[uniqueName],name:uniqueName});
}
//uniqiueArray
unique array by id property with ES6:
arr.filter((a, i) => arr.findIndex(b => b.id === a.id) === i); // unique by id
replace b.id === a.id with the relevant comparison for your case

JavaScript merging objects by id [duplicate]

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
How to merge two arrays in JavaScript and de-duplicate items
(89 answers)
Closed 7 months ago.
What's the correct way to merge two arrays in Javascript?
I've got two arrays (for example):
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
I want to be able to end up with something like:
var a3 = [{ id : 1, name : "test", count : "1"},
{ id : 2, name : "test2", count : "2"}]
Where the two arrays are being joined based on the 'id' field and extra data is simply being added.
I tried to use _.union to do this, but it simply overwrites the values from the second array into the first one
Short ES6 solution
const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))
This should do the trick:
var mergedList = _.map(a1, function(item){
return _.extend(item, _.findWhere(a2, { id: item.id }));
});
This assumes that the id of the second object in a1 should be 2 rather than "2"
Assuming IDs are strings and the order does not matter, you can
Create a hash table.
Iterate both arrays and store the data in the hash table, indexed by the ID. If there already is some data with that ID, update it with Object.assign (ES6, can be polyfilled).
Get an array with the values of the hash map.
var hash = Object.create(null);
a1.concat(a2).forEach(function(obj) {
hash[obj.id] = Object.assign(hash[obj.id] || {}, obj);
});
var a3 = Object.keys(hash).map(function(key) {
return hash[key];
});
In ECMAScript6, if the IDs are not necessarily strings, you can use Map:
var hash = new Map();
a1.concat(a2).forEach(function(obj) {
hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var a3 = Array.from(hash.values());
ES6 simplifies this:
let merge = (obj1, obj2) => ({...obj1, ...obj2});
Note that repeated keys will be merged, and the value of the second object will prevail and the repeated value of the first object will be ignored.
Example:
let obj1 = {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj1Val"};
let obj2 = {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj2Val"};
merge(obj1, obj2)
// {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj2Val", uniqueObj2Key: "uniqueKeyValueObj2"}
merge(obj2, obj1)
// {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj1Val", uniqueObj1Key: "uniqueKeyValueObj1"}
Complete solution (with Lodash, not Underscore)
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
var merge = (obj1, obj2) => ({...obj1, ...obj2});
_.zipWith(a1, a2, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1"}
1: {id: 2, name: "test2", count: "2"}
If you have an array of arrays to merge you can do it like this:
var arrayOfArraysToMerge = [a1, a2, a3, a4]; //a3 and a4 are arrays like a1 and a2 but with different properties and same IDs.
_.zipWith(...arrayOfArraysToMerge, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1", extra1: "val1", extra2: 1}
1: {id: 2, name: "test2", count: "2", extra1: "val2", extra2: 2}
reduce version.
var a3 = a1.concat(a2).reduce((acc, x) => {
acc[x.id] = Object.assign(acc[x.id] || {}, x);
return acc;
}, {});
_.values(a3);
I think it's common practice in functional language.
Already there are many great answers, I'll just add another one which is from a real problem I needed to solve yesterday.
I had an array of messages with user ids, and one array of users containing users' names and other details. This is how I managed to add user details to the messages.
var messages = [{userId: 2, content: "Salam"}, {userId: 5, content: "Hello"},{userId: 4, content: "Moi"}];
var users = [{id: 2, name: "Grace"}, {id: 4, name: "Janetta"},{id: 5, name: "Sara"}];
var messagesWithUserNames = messages.map((msg)=> {
var haveEqualId = (user) => user.id === msg.userId
var userWithEqualId= users.find(haveEqualId)
return Object.assign({}, msg, userWithEqualId)
})
console.log(messagesWithUserNames)
Vanilla JS solution
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
const merge = (arr1, arr2) => {
const temp = []
arr1.forEach(x => {
arr2.forEach(y => {
if (x.id === y.id) {
temp.push({ ...x, ...y })
}
})
})
return temp
}
console.log(merge(a1, a2))
The lodash implementaiton:
var merged = _.map(a1, function(item) {
return _.assign(item, _.find(a2, ['id', item.id]));
});
The result:
[
{
"id":1,
"name":"test",
"count":"1"
},
{
"id":2,
"name":"test2",
"count":"2"
}
]
Wanted to add this answer which is derived from #daisihi answer above. Main difference is that this uses the spread operator.
Also, at the end I remove the id because it was not desirable in the first place.
const a3 = [...a1, ...a2].reduce((acc, x) => {
acc[x.id] = {...acc[x.id] || {}, ...x};
return acc;
}, {});
This part was taken from another post. removing a property from a list of objects in an array
const newArray = Object.values(a3).map(({id, ...keepAttrs}) => keepAttrs);
Found other solutions failing for some cases, so writing a better one here
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 3, count : "3"}, { id : 1, count : "1"}, {id : 2, count : "2"}]
const mergeHelper = new Map(a1.map(x => [x.id, x]));
for (const x of a2) {
if (mergeHelper.has(x.id)) {
const item = mergeHelper.get(x.id);
mergeHelper.set(x.id, {...item, ...x});
} else {
mergeHelper.set(x.id, x);
}
}
const mergedList = [...mergeHelper.values()];
// For sorted array
// const mergedSortedList = [...mergeHelper.values()].sort((a, b) => a.id - b.id);
console.log(mergedList)
Using js Map is way faster than other approaches, helps when array length is huge.
A working TypeScript version:
export default class Merge {
static byKey(a1: any[], a2: any[], key: string) {
const res = a1.concat(a2).reduce((acc, x) => {
acc[x[key]] = Object.assign(acc[x[key]] || {}, x);
return acc;
}, {});
return Object.entries(res).map(pair => {
const [, value] = pair;
return value;
});
}
}
test("Merge", async () => {
const a1 = [{ id: "1", value: "1" }, { id: "2", value: "2" }];
const a2 = [{ id: "2", value: "3" }];
expect(Merge.byKey(a1, a2, "id")).toStrictEqual([
{
id: "1",
value: "1"
},
{ id: "2", value: "3" }
]);
});
try this
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
let arr3 = a1.map((item, i) => Object.assign({}, item, a2[i]));
console.log(arr3);
How about this?
const mergeArrayObjects = (arr1: any[], arr2: any[], mergeByKey: string): any[] => {
const updatedArr = [];
for (const obj of arr1) {
const arr1ValueInArr2 = arr2.find(
a => a[mergeByKey] === obj[mergeByKey],
);
if (arr1ValueInArr2) {
updatedArr.push(Object.assign(obj, arr1ValueInArr2));
} else {
updatedArr.push(obj);
}
}
const mergeByKeyValuesInArr1 = arr1.map(a => a[mergeByKey]);
const remainingObjInArr2 = arr2.filter(a => !mergeByKeyValuesInArr1.includes(a[mergeByKey]) )
return updatedArr.concat(remainingObjInArr2)
}
You can write a simple object merging function like this
function mergeObject(cake, icing) {
var icedCake = {}, ingredient;
for (ingredient in cake)
icedCake[ingredient] = cake[ingredient];
for (ingredient in icing)
icedCake[ingredient] = icing[ingredient];
return icedCake;
}
Next, you need to do use a double-loop to apply it to your data structre
var i, j, a3 = a1.slice();
for (i = 0; i < a2.length; ++i) // for each item in a2
for (j = 0; i < a3.length; ++i) // look at items in other array
if (a2[i]['id'] === a3[j]['id']) // if matching id
a3[j] = mergeObject(a3[j], a2[i]); // merge
You can also use mergeObject as a simple clone, too, by passing one parameter as an empty object.
const a3 = a1.map(it1 => {
it1.test = a2.find(it2 => it2.id === it1.id).test
return it1
})
If you have exactly the same number of items in both array with same ids you could do something like this.
const mergedArr = arr1.map((item, i) => {
if (item.ID === arr2[i].ID) {
return Object.assign({}, item, arr2[i]);
}
});
function mergeDiffs(Schedulearray1, Schedulearray2) {
var secondArrayIDs = Schedulearray2.map(x=> x.scheduleid);
return Schedulearray1.filter(x=> !secondArrayIDs.includes(x.scheduleid)).concat(Schedulearray2);
}
None of them worked for me. I wrote own:
const formatteddata=data.reduce((a1,a2)=>{
for (let t=0; t<a1.length; t++)
{var id1=a1[t].id
for (let tt=0; tt<a2.length; tt++)
{var id2=a2[tt].id
if(id1==date2)
{a1[t]={...a1[t],...a2[tt]}}
}
}
return a1
})
works with any amount of arrays of objects in arrays, with varying length and not always coinsciding dates

Categories

Resources