Merging of two arrays with conditions without using lodash - javascript

I am facing problem to merge the two arrays. I have two arrays of objects first is prev having old values and another with updated values. I would like to have result array with all the objects of prev array with its updated value in array next, and also have objects in next array.
Example:
var prev = [{id: 1, val: 'abc'}, {id: 2, val: 'pqr'}];
var next = [{id: 1, val: 'nextVal'}, {id: 3, val: 'xyz'}];
expected
mergeOutput = [
{id: 1, val: 'nextVal'}, // value is updated
{id: 2, val: 'pqr'},
{id: 3, val: 'xyz'}
]
Note: Array order do not matter.

You can use Map() to merge array.
var prev = [{id: 1, val: 'abc'}, {id: 2, val: 'pqr'}];
var next = [{id: 1, val: 'nextVal'}, {id: 3, val: 'xyz'}];
var hash = new Map();
prev.concat(next).forEach(function(obj) {
hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var mergedArray = Array.from(hash.values());
console.log(mergedArray);
Source : StackOverflow

Related

How to map through a deeply nested array of objects?

const my_arr = [
{id: 1, arr: [{subId: 1, value: 1}],
{id: 2, arr: [{subId: 2, value: 2}],
{id: 3, arr: [{subId: 3, value: 1}],
]
how do I map over this array my_arr and then map over arr to return an array like so:
[
{subId: 1, value: 1},
{subId: 3, value: 1},
]
basically filtering out only where values are 1 and then returning only that sub object
I've tried doing
my_arr.map((x) => x.map((y) => y.value === 1 ? y : null))
You can try this approach with flatMap and filter
const my_arr = [
{id: 1, arr: [{subId: 1, value: 1}]},
{id: 2, arr: [{subId: 2, value: 2}]},
{id: 3, arr: [{subId: 3, value: 1}]},
]
const result = my_arr.flatMap(item => item.arr).filter(item => item.value === 1)
console.log(result)
Your current approach maps over the outer array my_arr, and then uses an inner map to map over the inner array. Since .map() always returns an array, you'll end up mapping your objects from your outer array to other arrays, which you don't want. You can instead use .flatMap() which will combine/join the returned inner arrays into one array. However, rather than using .map() as your inner method though, you should use .filter() to create an array of objects that have a value of 1, which then gets merged into your resulting outer array created by the .flatMap() method:
const my_arr = [ {id: 1, arr: [{subId: 1, value: 1}]}, {id: 2, arr: [{subId: 2, value: 2}]}, {id: 3, arr: [{subId: 3, value: 1}]}, ];
const res = my_arr.flatMap(({arr}) => arr.filter(({value}) => value === 1));
console.log(res);
Since you are dealing with nested structure, you will have to get little creative.
First you will have to filter the array.
Inside it, you can use .some to check if your condition matches and return matching
Now you have the filtered list but you still need to format your output. You can use .reduce and concat arr of every item
This will be useful if you have multiple items in arr.
const my_arr = [
{id: 1, arr: [{subId: 1, value: 1}] },
{id: 2, arr: [{subId: 2, value: 2}] },
{id: 3, arr: [{subId: 3, value: 1}] },
]
const output = my_arr
.filter(({ arr }) =>
arr.some(({value}) => value === 1)
).reduce((acc, { arr }) => acc.concat(arr), [])
console.log(output)

Is there another way to solve this forEach problem using map, filter, reduce...?

If i have two arrays of objects like these two
const x = [{id: 1, a: 5}, {id: 2, a:10}, {id: 3, a: 12}];
const y = [{id: 4, a: 0}, {id: 2, a: 0}, {id: 3, a: 0}];
The output should be a new array represents y
but with some modifications, if an item in y has an id matches an item in x, a value should be the same as x, so the output should be
[{id: 4, a: 0}, {id: 2, a: 10}, {id: 3, a: 12}]
This is my solution
const z = [...y];
z.forEach(el => x.map(ele => el.a = el.id === ele.id ? ele.a : el.a));
This is a simple implementation of what i am doing in a project, i care about performance and i see the step of cloning the array z = [...y] may be expensive, so i am looking for a solution using functions that return a new array map, filter, reduce...,
I tried nested map and filter, find ... but i ended with complex solutions, so are there another solutions that would be more performant and simple in same time?
Instead of iterating over x for each value of y you can create a xObj from x by single iteration and in the loop over y just check if it's exist in the xObj (constant lookup O(1) ) if yes then update it otherwise use the existing value.
const x = [{id: 1, a: 5}, {id: 2, a:10}, {id: 3, a: 12}];
const y = [{id: 4, a: 0}, {id: 2, a: 0}, {id: 3, a: 0}];
var xObj = {};
x.forEach(function(val){
xObj[val.id] = val.a;
});
const newY = y.reduce(function(o,i){
if(xObj.hasOwnProperty(i.id)){
i.a = xObj[i.id];
}
o.push(i);
return o;
},[]);
console.log(newY);
Use Set to store the ids available in x array and use map over y array to check whether y's id exists in the Set or not.
const x = [{id: 1, a: 5}, {id: 2, a:10}, {id: 3, a: 12}];
const y = [{id: 4, a: 0}, {id: 2, a: 0}, {id: 3, a: 0}];
const mapped = Object.values(x).reduce((acc, {id}) => {
acc.add(id)
return acc;
}, new Set());
const result = y.map((obj, index) => mapped.has(obj.id) ? {...obj, a: x[index].a} : obj);
console.log(result);
Hope this will help!
For a problem like this you should care far less about the computational expense of creating a new array, and far more about the Big O expense of running through multiple arrays multiple times. Both your solution and the other one posted by #NitishNarang appear to be O(n^2) because as your arrays become larger, the number of steps required to solve increases exponentially.
Personally I would simply create a new Map() and go through each item one by one, adding it to the Map() only if the value of a corresponding to that ID is larger than the currently stored one. It's basically a slightly more complicated sorting exercise except with unique ID values thrown in.
const x = [{id: 1, a: 5}, {id: 2, a:10}, {id: 3, a: 12}];
const y = [{id: 4, a: 0}, {id: 2, a: 0}, {id: 3, a: 0}];
const myMap = new Map();
for (const ea of x) {
if (!myMap.has(ea.id) || ea.a >= myMap.get(ea.id).a) {
myMap.set(ea.id, ea);
}
}
for (const ea of y) {
if (!myMap.has(ea.id) || ea.a >= myMap.get(ea.id).a) {
myMap.set(ea.id, ea);
}
}
const result = [...myMap.values()];
This solution is O(n), i.e. it's linear, meaning that if you add 10 or 100 or 1000 more items to the array x or y or both, it will only add that many more steps to running the solution instead of 10^2 or 100^2 or 1000^2 because you have to check every single item in each array against every other item in the other array (like you do with your original solution.)
Edit: as #SZenC pointed out, the above solution is not quite correct as it combines both arrays. In order to selectively match items only if they originally exist in array y, simply iterate over y first, and then only replace values when iterating over x if they are already present in the map:
const x = [{id: 1, a: 5}, {id: 2, a:10}, {id: 3, a: 12}];
const y = [{id: 4, a: 0}, {id: 2, a: 0}, {id: 3, a: 0}];
const myMap = new Map();
for (const ea of y) {
if (!myMap.has(ea.id) || ea.a >= myMap.get(ea.id).a) {
myMap.set(ea.id, ea);
}
}
for (const ea of x) {
if (myMap.has(ea.id) && ea.a >= myMap.get(ea.id).a) {
myMap.set(ea.id, ea);
}
}
const result = [...myMap.values()];
Please try this. It uses "map" and "find"
const x = [{id: 1, a: 5}, {id: 2, a:10}, {id: 3, a: 12}];
const y = [{id: 4, a: 0}, {id: 2, a: 0}, {id: 3, a: 0}];
const result = y.map(yData => (xResult = x.find(xData => xData.id == yData.id), xResult && { ...yData, a: xResult.a } || yData))
console.log(result)

Add unique objects to array of objects in Javascript

I have arrays of objects that look like this:
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}]
const array2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}]
How can I add unique objects from array2 to array1 so it looks like this:
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}]
Lodash implementations are permitted. Thanks a lot.
You can use _.unionBy() function to merge unique objects from arrays.
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}];
const array2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}];
console.log(_.unionBy(array1, array2, 'id'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Using native array functions you can get the desired result as follows:
Concat both arrays first using .concat()
Use .reduce() to create the resultant object having ids as keys and values as relevant object. If already added an object then skip the others with same ids.
Use Object.values() to get an array of the objects from the resultant object.
Demo:
const array1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}],
array2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}];
const result = Object.values(
array1.concat(array2).reduce((r, c) => (r[c.id] = r[c.id] || c, r), {})
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can also do it in one line via native Map object and reduce:
const arr1 = [{id: 1, name: "John"}, {id: 2, name: "Mary"}]
const arr2 = [{id: 1, name: "John"}, {id: 3, name: "Phil"}, {id: 4, name: "Sarah"}]
const result = [...[...arr1, ...arr2]
.reduce((r, c) => (r.set(c.id, c), r), new Map()).values()]
console.log(result)

Finding a key in an object by using values from an array

I have an array which is dynamically created by selecting items from a list:
[2, 4]
I also have an array of objects:
[{id: 1, name: "Param1"}, {id: 2, name: "Param2"}, {id: 3, name: "Param3"}, {id: 4, name: "Param4"}]
What I need to do is use the values in the first array to match against the ids in the objects in the second array and return those objects.
Help with this would be much appreciated
Thanks for your time
You can use this ES6 code, which turns the first array to a Set to allow fast lookup, and then applies the Array filter method, specifically intended for this purpose:
var select = [2, 4];
var data = [{id: 1, name: "Param1"}, {id: 2, name: "Param2"},
{id: 3, name: "Param3"}, {id: 4, name: "Param4"}]
var selectSet = new Set(select);
var result = data.filter( obj => selectSet.has(obj.id) );
console.log(result);
You can just use for loop as Liam's comment, or you can use the filter method of array like this:
var keys = [2, 4];
var objs = [{id: 1, name: "Param1"}, {id: 2, name: "Param2"}, {id: 3, name: "Param3"}, {id: 4, name: "Param4"}];
function filterById(obj) {
return keys.indexOf(obj.id) != -1;
}
var newArr = objs.filter(filterById);
The newArr is the result you want.

Merge duplicates in JavaScript Array

I have a stupid problem that at first seems to be simple to solve, but turns out to be tricky.
I have an array of objects, each with two properties: id and value:
[
{id: 2, value: 10},
{id: 4, value: 3},
{id: 2, value: 2},
{id: 1, value: 15}
]
I want to write an algorithm that sums up the values of ones with similar id.
My end result should be a new array with only the merged objects:
[
{id: 2, value: 12},
{id: 4, value: 3},
{id: 1, value: 15}
]
I've tried the following, but it doesn't work:
var arr = [];
arr.push({id: 2, visit:10});
arr.push({id: 4, visit:3});
arr.push({id: 2, visit:2});
arr.push({id: 1, visit:15});
// Deep copy
var copy = jQuery.extend(true, [], arr);
var masterArr = [];
for (var i = 0; i < arr.length; i++) {
var objArr = [];
objArr.push(arr[i]);
for (var j = copy.length-1; j > -1; j--) {
if (arr[i].id === copy[j].id) {
var q = copy.splice(j,1);
}
}
masterArr.push(objArr);
}
My plan was to first gather all similar objects in separate arrays (objArr), sum them up and put them in an end array (masterArr). I use jquerys extend to make a deep copy (not a reference) and reverse iteration and splice to remove objects thats already been found as "duplicates".
This doesn't work! And it doesn't seem to be a very efficient mehtod to solve my problem.
How could I do this? Performance isn't top priority but rather "nice to have"!
Thanks!
You can do it like this:
// Assuming:
a = [{id: 2, value: 10}, {id: 4, value: 3}, {id: 2, value: 2}, {id: 1, value: 15}]
var b = {}, // Temporary variable;
c = []; // This will contain the result;
// Build a id:value object ( {1: 15, 2: 12, 4: 3} )
a.map(function(current){b[current.id] = (b[current.id] || 0) + current.value});
for(var key in b){ // Form that into the desired output format.
c.push({id: parseInt(key, 10), value: b[key]});
}
console.log(c);
/* [{id: 1, value: 15},
{id: 2, value: 12},
{id: 4, value: 3}] */
I'm using parseInt(key, 10), since the keys are strings, you'll probably want them converted to integers again.
// First group the data based on id and sum the values
var temp = data.reduce(function(result, current) {
result[current.id] = (result[current.id] || 0) + current.value;
return result;
}, {});
// then recreate the objects with proper id and value properties
var result = [];
for (var key in temp) {
result.push({
id: parseInt(key, 10),
value: temp[key]
});
}
console.log(result);
Output
[ { id: 1, value: 15 },
{ id: 2, value: 12 },
{ id: 4, value: 3 } ]
The quickest approach loops over the array only once using Array.prototype.filter():
var tmp = {},
result = arr.filter(function (el) {
if (tmp.hasOwnProperty(el.id)) {
tmp[el.id].visit += el.visit;
return false;
}
else {
tmp[el.id] = el;
return true;
}
});
It also reuses the objects, though this renders the original array to contain inaccurate values. If this is a problem, you can modify the example to copy each object property to a new object.

Categories

Resources