Related
I have an array of array of objects similar to this:
arr = [{
val: 1,
ts: 123
}, {
val: 2,
ts: 125
}, {
val: 3,
ts: 120
}, {
val: 4,
ts: 113
}, {
val: 5,
ts: 117
}, {
val1: 6,
ts: 143
}, {
val1: 7,
ts: 193
}, {
val1: 8,
ts: 187
}, {
val1: 9,
ts: 115
}, {
val1: 10,
ts: 116
}]
The length of the array is always an even number. Now basically I'd like to split them in 2 halves
split1 = [{ val:1}, {val :2}, ......{val:5}]
split2 = [{val1:6},{val1:7},.......{val1:10}]
Now I have to map over these arrays and combine their fields(first item of array1 with first item of array2 and so on) into one single object and add an extra id to it such that result will look like
final = [{val:1, val1:6 , id:1} , {val:2, val1:7,id:3} , {val:3, val1:8,id:3},{val:4, val1:9,id:4},{val: 5, val1:10, id:5}]
This should hold for all arrays of even length and I want to make it a dynamic one. Keys will never be repeated inside source array
What I have tried is:
var res= a.splice(0, arr.length/2);
var c = a.map((val, ind) => { {val, res[ind]} })
Can someone guide here?
You can try this solution
const arr = [{ val: 1},{val: 2},{val: 3}, {val: 4} , {val:5},{ val1: 6},{val1: 7},{val1: 8}, {val1: 9} , {val1:10}]
const split = arr => [arr.slice(0, arr.length/2), arr.slice(arr.length/2, arr.length)]
const splitedArr = split(arr);
const merge = arr => {
const merged = []
for(let i = 0; i < arr[0].length; i++){
let newObj = {...arr[0][i], ...arr[1][i], id:(i+1)}
merged.push(newObj);
}
return merged;
}
const mergedArr = merge(splitedArr);
console.log(mergedArr);
You're quite close, some minor fixes
wrap map function implicit return with () instead of {}
for adding computed value you need to assign key name explicitly
need to destructure val or need to access val.val
const arr = [{ val: 1}, {val: 2}, {val: 3}, {val: 4}, {val: 5}, {val1: 6}, {val1: 7}, {val1: 8}, {val1: 9}, {val1: 10}];
const firstHalf = arr.slice(0, arr.length / 2);
const secondHalf = arr.slice(arr.length / 2);
const final = secondHalf.map((val, ind) => ({
...val,
...firstHalf[ind],
id: ind,
}))
console.log(final);
I have tried
var res= a.splice(0, arr.length/2);
var c = a.map((val, ind) => { {val, res[ind]} })
You're quite close. To make the map callback syntactically valid and create the desired object, it should be
var res = a.splice(0, arr.length/2);
var c = a.map((val, ind) => {
return {val: val.val, val1: res[ind].val1, id: ind+1};
})
or
var c = a.map((val, ind) => {
return Object.assign({id: ind+1}, val, res[ind]);
})
I am trying to use array.filter() to compare two arrays and separate out values that the two arrays have in common, based on a certain property (id), vs. values they don't have in common. The common ids I want to push to a new array (recordsToUpdate). And I want to push the remaining elements from arr2 to a new array (recordsToInsert).
What I've tried is not working. How can I rework this to get the results I wanted? - (which in the example here should be one array of 1 common element {id: 3}, and another array of the remaining elements from arr2):
const arr1 = [{id: 1}, {id: 2}, {id: 3}];
const arr2 = [{id: 3}, {id: 4}, {id: 5}];
let recordsToUpdate = [];
let recordsToInsert = [];
recordsToUpdate = arr1.filter(e => (arr1.id === arr2.id));
recordsToInsert = ?
console.log('recordsToUpdate: ', recordsToUpdate);
console.log('recordsToInsert: ', recordsToInsert);
The desired result should be:
recordsToUpdate = [{id: 3}];
recordsToInsert = [{id: 4}, {id: 5}];
Try this, which uses Array.prototype.find to test for whether an object exists in arr2 with a given id:
const arr1 = [{id: 1}, {id: 2}, {id: 3}];
const arr2 = [{id: 3}, {id: 4}, {id: 5}];
const recordsToUpdate = arr1.filter(e => arr2.find(obj => obj.id === e.id) !== undefined);
const recordsToInsert = arr1.filter(e => arr2.find(obj => obj.id === e.id) === undefined);
console.log('recordsToUpdate: ', recordsToUpdate);
console.log('recordsToInsert: ', recordsToInsert);
Update to Robin post using some instead of find. It is just other way around.
const arr1 = [{id: 1}, {id: 2}, {id: 3}];
const arr2 = [{id: 3}, {id: 4}, {id: 5}];
const recordsToUpdate = arr1.filter(e => arr2.some(obj => obj.id === e.id));
const recordsToInsert = arr2.filter(e => !arr1.some(obj => obj.id === e.id));
console.log('recordsToUpdate: ', recordsToUpdate);
console.log('recordsToInsert: ', recordsToInsert);
I think this is what you are after... I added values to show the replacement. If you are doing any kind of state management, be careful as I am directly mutating the current array.
const arr1 = [
{ id: 1, v: "a" },
{ id: 2, v: "b" },
{ id: 3, v: "old" }
];
const arr2 = [
{ id: 3, v: "new" },
{ id: 4, v: "e" },
{ id: 5, v: "f" }
];
function updateRecords(currentArray, updatesArray) {
const currentIds = currentArray.map(item => item.id);
updatesArray.forEach(updateItem =>
currentIds.includes(updateItem.id)
? (currentArray[
currentIds.findIndex(id => id === updateItem.id)
] = updateItem)
: currentArray.push(updateItem)
);
return currentArray;
}
console.log(updateRecords(arr1, arr2))
This now gives the option below:
[
{
"id": 1,
"v": "a"
},
{
"id": 2,
"v": "b"
},
{
"id": 3,
"v": "new"
},
{
"id": 4,
"v": "e"
},
{
"id": 5,
"v": "f"
}
]
Putting it in a function is also something you likely want to do as you will likely use this multiple places in your code.
There are two arrays:
[
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1},
{"id": "344430b94t4t34rwefewfdff", "quantity": 5},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
]
and
[
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2},
{"id": "344430b94t4t34rwefewfdff", "quantity": 1}
]
How to combine them into one summing quantity?
[
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 3},
{"id": "344430b94t4t34rwefewfdff", "quantity": 6},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
]
One of them can be empty sometimes
You can do it with plain JavaScript.
Use Array.reduce() to make an intermediate dictionary by id and accumulate the quantities, then turn it into an array with Object.values():
const arr1 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1},
{"id": "344430b94t4t34rwefewfdff", "quantity": 5},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
];
const arr2 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2},
{"id": "344430b94t4t34rwefewfdff", "quantity": 1}
];
const result = Object.values([...arr1, ...arr2].reduce((acc, { id, quantity }) => {
acc[id] = { id, quantity: (acc[id] ? acc[id].quantity : 0) + quantity };
return acc;
}, {}));
console.log(result);
You can use lodash but modern vanilla JS is just as viable and performant. I would imagine the other answers will be using functional methods like reduce, so here's a version that uses a simple for/of loop with find rather than a dictionary lookup which might be longer, but it is a little easier to follow.
const arr1 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1}, {"id": "344430b94t4t34rwefewfdff", "quantity": 5}, {"id": "342343343t4t34rwefewfd53", "quantity": 3}];
const arr2 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2}, {"id": "344430b94t4t34rwefewfdff", "quantity": 1}];
function merge(arr1, arr2) {
// Merge the arrays, and set up an output array.
const merged = [...arr1, ...arr2];
const out = [];
// Loop over the merged array
for (let obj of merged) {
// Destructure the object in the current iteration to get
// its id and quantity values
const { id, quantity } = obj;
// Find the object in out that has the same id
const found = out.find(obj => obj.id === id);
// If an object *is* found add this object's quantity to it...
if (found) {
found.quantity += quantity;
// ...otherwise push a copy of the object to out
} else {
out.push({ ...obj });
}
}
return out;
}
console.log(merge(arr1, arr2));
You can just do this with reduce:
let a1 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2},
{"id": "344430b94t4t34rwefewfdff", "quantity": 1}
];
let a2 = [
{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1},
{"id": "344430b94t4t34rwefewfdff", "quantity": 5},
{"id": "342343343t4t34rwefewfd53", "quantity": 3}
];
let result = Object.values(a1.concat(a2).reduce((acc, v) => {
if (!acc[v.id]) {
acc[v.id] = {id: v.id, quantity: 0};
}
acc[v.id].quantity += v.quantity;
return acc;
}, {}));
console.log("Results: ", result);
You can use .reduce and .find methods to achieve this.
const arr1 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 1}, {"id": "344430b94t4t34rwefewfdff", "quantity": 5}, {"id": "342343343t4t34rwefewfd53", "quantity": 3}];
const arr2 = [{"id": "5c5030b9a1ccb11fe8c321f4", "quantity": 2}, {"id": "344430b94t4t34rwefewfdff", "quantity": 1}];
const result = [...arr1, ...arr2].reduce((accumulator, currentValue) => {
const element = accumulator.find(item => item.id === currentValue.id)
element ? element.quantity += currentValue.quantity : accumulator.push(currentValue)
return accumulator
},[])
console.log(result)
All these answers require you to know the object structure to select and sum.
lodash does actually allow you to do this without knowing the structure; by using the customizer parameter of _.mergeWidth;
let result = _.mergeWith(arr1, arr2, (objValue, srcValue, key, object, source, stack) =>{
//Add any conditions you need here. Ive added a few relevant examples.
//if(key.startsWith("num")) //Check for property name prefixes like num...
//if(propertyNamesToSum.Includes(key)) //check if your property is in your predefined list of property names
//This one below sums any properties that are numbers
if(_.isNumber(srcValue) && _.isNumber(objValue)){
return srcValue + objValue;
}
return undefined; //lodash will merge as usual if you return undefined.
});
Lodash docs - https://lodash.com/docs/4.17.15#mergeWith
Version with additional object keys.
The body of the function does not interfere with what object has properties.
So sum by "qty" and check by "prop"
var first = [
{quantity:100, id:1, variantId: 1},
{quantity:300, id:2, variantId: 2, propA: 'aaa'},
];
var second = [
{quantity:100, id:1, variantId: 1},
{quantity:200, id:2, variantId: 2, propB: true},
{quantity:300, id:3, variantId: 3, propC: 'ccc'},
]
function mergeArrays(arrayOfArrays, propToCheck, propToSum) {
let sum = [];
[].concat(...arrayOfArrays).map(function(o) {
let existing = sum.filter(function(i) { return i[propToCheck] === o[propToCheck] })[0];
if (!existing) {
sum.push(o);
} else {
existing[propToSum] += o[propToSum];
let copyProps = Object.keys(o).filter(obj => {
return existing[obj] !== o[obj]
}).map(val => (val !== propToSum) ? existing[val] = o[val] : null)
}
});
return sum;
}
console.log(mergeArrays([first, second], 'variantId', 'quantity'))
This function uses lodash reduce and mapValues to sum the specified keys of an array of objects into a single result object. It allows missing keys in each object.
const mergeAndSumObjVals = (objs, keys) => _.reduce(
objs,
(o, s) => _.mapValues(o, (v, k) => (v || 0) + (s[k] || 0)),
_.chain(keys).invert().mapValues(() => 0).value(),
)
const objs = [{
negative: 54,
positive: 2
}, {
inconclusive: 8,
positive: 1
}, {
negative: 26,
inconclusive: 5,
positive: 4
}]
const result = mergeAndSumObjVals(objs, ['negative', 'inconclusive', 'positive'])
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
I have an array, which contains array of objects. I need to extract the property value "id" of items that have objects.
Example of array:
let myArray = [
[ {id: "1"}, {id: "2"} ],
[],
[],
[ {id: "3"} ]
]
How can I extract and create an array like this:
["1", "2", "3"]
I tried this:
tagIds = myArray.map(id =>{id})
You can use reduce to flatten the array and use map to loop thru the array and return the id.
let myArray = [
[{id: "1"}, {id: "2"}],
[],
[],
[{id: "3"}],
];
let result = myArray.reduce((c, v) => c.concat(v), []).map(o => o.id);
console.log(result);
Another way with simple nested loops:
let myArray = [
[ {id: "1"}, {id: "2"} ],
[],
[],
[ {id: "3"} ]
]
//----------------------------------
let newArray=[];
for (let i=0;i<myArray.length;i++){
for (let j=0;j<myArray[i].length;j++){
newArray.push(myArray[i][j].id);
}
}
console.log(newArray); //outputs ['1','2','3']
You can use .concat() to create array of single objects and then .map() to extract ids:
let myArray = [
[{id: "1"}, {id: "2"}], [], [], [{id:"3"}]
];
let result = [].concat(...myArray).map(({ id }) => id);
console.log(result);
Docs:
Array.prototype.concat()
Array.prototype.map()
Spread Syntax
Here is my solution:
let a = myArray.flat(100) // you can put (3) or (10) in here, the higher the flatter the array
let b = a.map(
function(value){
return parseInt(value.id)
}
)
console.log(b)
You can also write a recursive function to make this work with any number of arrays, for example:
function extractIds(arr) {
return arr.reduce((a, item) => {
if (Array.isArray(item)) {
return [
...a,
...extractIds(item)
];
}
return [
...a,
item.id
];
}, [])
}
extractIds([{id: 1}, [{id: 2}], {id: 3}, [{id: 4}, [{id: 5}, [{id: 6}]]]])
the return of extractIds will be [1, 2, 3, 4, 5, 6].
Notice that without the recursive part you would end up with something like this: [1, 2, [3, {id: 4}, [{id: 5}]]] (Not exactly like this, but just as an example).
I have two arrays of objects:
array1 = [{id : 1, pop: 4000}, etc.]
array2 = [{id : 1, size: 2000}, etc.]
I would like to merge the two arrays (on id) for:
merged = [{id :1, pop: 4000, size: 2000}, etc.]
I saw extend and other options like concat but nothing quite what I was going for
No need for underscore, you can use Object.assign:
Working Example
var array1 = [{id : 1, pop: 4000}, {id : 2, pop: 4000}];
var array2 = [{id : 1, size: 2000}, {id : 1, blah: 4000}, {id : 2, size: 4000}];
var merged = [];
for (var i = 0; i < array1.length; i++) {
var obj = {};
for (var j = 0; j < array2.length; j++) {
if (array1[i].id === array2[j].id) {
obj = Object.assign(obj, array1[i], array2[j]);
merged.push(obj);
}
}
}
With lodash:
result = _(array1)
.concat(array2)
.groupBy('id')
.map(_.spread(_.extend))
.value();
Unlike map+find solutions, this is linear. Note that it's a "full outer" join, so the result will include non-matching elements from both arrays.
Using underscore JS :
var array1 = [{ id: 1, pop: 4000 }, { id: 2, pop: 7000 }];
var array2 = [{ id: 1, pop: 5000 }, { id: 2, pop: 6000 }];
var merge = _.map(array1, function(curr){
return _.extend(curr, _.findWhere(array2, { id: curr.id })); });
I think it should work..
A solution in plain Javascript with Array#forEach and Object.keys and an object for the reference to the array elements.
var array1 = [{ id: 1, pop: 4000 }, { id: 2, pop: 2000 }],
array2 = [{ id: 1, size: 2000 }, { id: 2, size: 0 }],
merged = [];
function getMerged(array1, array2) {
function iter(a) {
if (!obj[a.id]) {
obj[a.id] = { id: a.id };
result.push(obj[a.id]);
}
Object.keys(a).forEach(function (k) {
if (k !== 'id') {
obj[a.id][k] = a[k];
}
});
}
var result = [],
obj = Object.create(null);
array1.forEach(iter);
array2.forEach(iter);
return result;
}
console.log(getMerged(array1, array2));
You can use _.map, _.extend and _.findWhere in Underscore.
Example:
const array1 = [{
"id": 1,
"pop": 400
}, {
"id": 2,
"pop": 2000
}];
const array2 = [{
"id": 1,
"size": 5000
}, {
"id": 2,
"size": 500
}];
const mergeById = (a, b) => a.map(x => _.extend(x, _.findWhere(b, {"id": x.id})));
console.log(mergeById(array1, array2));
/* RESULT:
[
{
"id": 1,
"pop": 400,
"size": 5000
},
{
"id": 2,
"pop": 2000,
"size": 500
}
]
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>