How to merge each object within arrays by index? - javascript

How can I merge two arrays of objects of same length?
var array1 = [
{name: "lang", value: "English"},
{name: "age", value: "18"}
];
var array2 = [
{code: "EN", text: "English language"},
{code: "DE", value: "German", text: "German language"}
];
The goal is to create the following array:
var array3 = [
{name: "lang", value: "English", code: "EN", text: "English language"},
{name: "age", code: "DE", value: "German", text: "German language"}
];
The idea is to create a new array in which array1 is the base and array2 overrides the values if they share the same key, and otherwise adds to the base array. The merging should happen sequentially in the same order that the objects appear in each array.
In this example, the arrays contains two objects, but for my actual situation, I have a couple of dozens of objects.
This is what I’ve been trying to do, but this only merges the first set of objects:
var array3 = Object.assign(array1[0], array2[0]);
How can I loop through it or map it?

A simple map with object spread syntax will do it.
Instead of object spread, Object.assign can also be used.
Assign unto a new empty object in order to avoid mutating the existing objects.
var array1 = [
{ name: "lang", value: "English" },
{ name: "age", value: "18" }
];
var array2 = [
{ code: "EN", text: "English language" },
{ code: "DE", value: "German", text: "German language" }
];
var array3 = array1.map((obj, index) => ({
...obj,
...array2[index]
}));
var array3Alternative = array1.map((obj, index) => Object.assign({}, obj, array2[index]));
console.log(array3);
Note that the order of spread patterns and Object.assign’s arguments matters: latter properties overwrite former properties.
You can also add more properties, e.g. default properties: Object.assign({ code: "XX", value: "Default" }, obj, array2[index]) or ({ code: "XX", value: "Default", ...obj, ...array2[index] }).
If you want to mutate the objects in array1, so that the properties from array2 get added into array1, simply remove the {}, from Object.assign({}, obj, array2[index]).
Then it would also be advisable to change the map to a forEach if you’re not expecting to create a resulting variable like array3.
If you have an unknown number of arrays like this:
const arrays = [
array1,
array2,
array3,
// …
];
then you can use this approach:
const mergedArray = Array.from({
length: arrays[0].length
}, (_, index) => Object.assign({}, ...arrays.map(({[index]: obj}) => obj))));
See this in action with the following example:
const arrays = [
[
{ a: 3, b: 5 },
{ a: 7, b: 2 },
{ a: 1 }
],
[
{ b: 8, c: 42 },
{ a: 1, b: 12, c: 44 },
{ b: 0 }
],
[
{ d: 14, e: 15 },
{ d: 7 },
{ a: 10 }
]
];
console.log(Array.from({
length: arrays[0].length
}, (_, index) => Object.assign({}, ...arrays.map(({[index]: obj}) => obj))));
Note that this also works with arrays of different lengths.
The resulting value from accessing a non-existent array index is undefined and spread syntax and Object.assign take care of this by ignoring it.
Just make sure to start with the longest array, so the result isn’t missing any objects.
This is easier with the snippet where you have an unknown number of arrays (even if it’s always two).
Instead of arrays[0].length, you just need to use Math.max(...arrays.map(({ length }) => length)).

Related

Can I assign a copy of an array of objects and add to those objects at the same time?

Say I have an array of objects:
const myArr = [
{name: 'one'},
{name: 'two'}
]
If I wanted to use this array of object as a base and add a custom property to the objects for each use case I might have, could I assign the array to a new variable and also change the contents of its objects at the same time?
I know this can be done in 2 steps, but I'm wondering if it's possible to do it all at once?
For example:
const copyArr = myArr;
copyArr.forEach(obj => obj.newProp = 'some-new-prop');
This would now be
[
{name: 'one', newProp: 'some-new-prop'},
{name: 'two', newProp: 'some-new-prop'}
]
You can use Array.map to iterate over each item and modify them.
Note that map will not modify your original array and therefore is immutable.
const myArr = [{ name: "one" }, { name: "two" }];
const copyArr = myArr.map((item) => ({
...item,
newProps: "some-new-prop",
}));
// [ { name: 'one', newProps: 'some-new-prop' },
// { name: 'two', newProps: 'some-new-prop' } ]

Find the best match in an array

I have got the following array:
let x = [
{ name: "Bad", value: 2 },
{ name: "Critical", value: 1 },
{ name: "High", value: 5 },
{ name: "Medium", value: 5 },
];
The expectation is to look for "Critical" first, if the array has it, return that, else look for "High" then look for "Medium" and so on.
You can store the priorities in an array and then loop over the array and for every priority check if there's an object and whenever an object is found, return it. If the entire priorities array is exhausted then return null (or whatever you want).
const arr = [
{ name: "Bad", value: 2 },
{ name: "Critical", value: 1 },
{ name: "High", value: 5 },
{ name: "Medium", value: 5 },
],
priorities = ["Critical", "High", "Medium", "Bad"],
search = (arr, priorities) => {
for (let p of priorities) {
const obj = arr.find(({ name }) => name === p);
if (obj) {
return obj;
}
}
return null;
};
console.log(search(arr, priorities));
You can also sort the array based on the priority.
Create a Map that stores the priorities.
Sort arr based on the priorities stored in the map.
const arr = [
{ name: "Bad", value: 2 },
{ name: "Critical", value: 1 },
{ name: "High", value: 5 },
{ name: "Medium", value: 5 },
],
priorities = new Map([
["Critical", 4],
["High", 3],
["Medium", 2],
["Bad", 1],
]),
sortedArr = [...arr].sort(
(a, b) => priorities.get(b.name) - priorities.get(a.name)
);
console.log(sortedArr);
let value = x.find(x => x.name == "Critical")
|| x.find(x => x.name == "High")
|| x.find(x => x.name == "Medium") ...
First, define the order in which you want to look for items, then use that to sort the items in the array. Lastly, find based on all the items in your sort order. The first match will be returned, since the items are sorted.
const x = [{name: 'Bad',value: 2}, {name: 'High', value: 5}, {name: 'Medium', value: 5}, {name: 'Critical', value: 1}],
order = ['Critical','High','Medium'],
//sort according to above order
output = x.sort(
({name:na},{name:nb}) =>
order.findIndex(v => v === na) - order.findIndex(v => v === nb)
)
//Now find items matching name in order
.find( ({name}) => order.includes(name) );
console.log( output );
NOTE
Since your data is already sorted in the desired order, I moved the element with name = "Critical" to the end of the array.
If the data will always be sorted according to the priority you want to find the items, then no sorting is needed.

Accessing duplicates in objects in the same array?

I have an array with multiple objects
arr = [
{name: 'xyz',
age: 13,
},
{name: 'abc',
age: 15,
},
{name: 'abc',
age: 15,
}]
how do I find the duplicate in this array and remove the object that is duplicated in the array? They are all in one array.
Apologies. I just realized what I am trying to do is, remove the object entirely if there's a duplicate in one key... so if the age is similar, I will remove object name "def". Is this possible?
arr = [
{name: 'xyz',
entry: 1,
age: 13,
},
{name: 'abc',
entry: 2,
age: 15,
},
{name: 'def',
age: 13,
entry: 3
}]
You could achieve this by the following steps:
transform each element into an object that is key-sorted, this will make objects consistent in terms of key-value pairs order, which will help us in the next step
map the array into JSON-stringified value, and store it into a Set(), which would help us store only unique stringified objects
turn the Set() back into array
map the array back into objects by JSON.parse() each element
const arr = [
{ name: "xyz", age: 13 },
{ age: 15, name: "abc" },
{ name: "abc", age: 15 },
]
const sortKeys = obj =>
Object.fromEntries(
Object.entries(obj).sort((keyValuePairA, keyValuePairB) =>
keyValuePairA[0].localeCompare(keyValuePairB[0])
)
)
const res = Array.from(
arr
.map(sortKeys)
.map(el => JSON.stringify(el))
.reduce((set, el) => set.add(el), new Set())
).map(el => JSON.parse(el))
console.log(res)
References
Set
Object.entries()
Object.fromEntries()

How to get all ordered values from immutable.js orderedmap without toJS?

I have such an orderedmap like this:
{
"200": { id: 200, name: "John" },
"120": { id: 120, name: "Mike" },
"350": { id: 350, name: "James" }
}
How to get all ordered values ​​without toJS method?
I've tried:
map.valueSeq().toArray(), Array.from(map.values())
but it returns an intermixed array.
You could do toList()to get all the values (e.i. not the keys) of the map in a List immutable structure you can further manipulate later, or do List(yourMap) if you want the key and value as a tuple array of sorts. Per the docs:
This is similar to List(collection), but provided to allow for chained expressions. However, when called on Map or other keyed collections, collection.toList() discards the keys and creates a list of only the values, whereas List(collection) creates a list of entry tuples.
const { Map, List } = require('immutable')
var myMap = Map({ a: 'Apple', b: 'Banana' })
List(myMap) // List [ [ "a", "Apple" ], [ "b", "Banana" ] ]
myMap.toList() // List [ "Apple", "Banana" ]
Aclaration:
Javascript will sort your source object keys if they are numeric or parseable as numeric, so you can do the following workaround:
const PeopleMap = new OrderedMap([
["200", { id: 200, name: "John" }],
["120", { id: 120, name: "Mike" }],
["350", { id: 350, name: "James" }]
]);
Here's a working example: https://jsfiddle.net/8kbcyfsn/
By declaring it as a key value pair array, the ordered map registers the right order.
Whereas if you declare it as an object,
const PeopleMap = new OrderedMap({
"200": { id: 200, name: "John" },
"120": { id: 120, name: "Mike" },
"350", { id: 350, name: "James"}
});
It will attempt to order it by the Number(key) value.
I'm not sure if I understood your question correctly, but if I did, you could try:
Object.keys(obj).map(key => obj[key]);

How to use spread syntax to return object and overwrite a property that reference to an array

let array = [{id: 3, value: 18},{id: 4, value: 20}]
let state = {array, someOtherProperty: [1,2,3]}
function f(){
return {...state, state.array.find(x=>x.id === 3)['value']: 20}
}
console.log(f())
Basically trying to replace the value 18 to 20 where id = 3 and leave alone all other property of state. Not sure how to use spread syntax in such case
Expected output as below:
state = {
array: [{id:3, value:20}, {id:4, value: 20}],
someOtherProperty: [1,2,3]
}
You could accomplish that by using map, the following way:
let array = [{
id: 3,
value: 18
}, {
id: 4,
value: 20
}]
let state = {
array,
someOtherProperty: [1, 2, 3]
}
function f() {
return { ...state,
array: array.map(item => item.id === 3 ? { ...item,
value: 20
} : item)
}
}
console.log(f())
This way you are using the original array and replacing only the elements that must be changed.

Categories