How to concatenate arrays in the way the objects inside are assigned? - javascript

Okay it was a bit hard to explain what I want. Let me explain in more detail. I have an array of objects. Because the "only" identifier of the arrays elements are their indexes, if we want to change an element we need to know which is the target index. But even if we have the index, I don't want to change the whole object, just assign the new one and "merge" them together.
I have a solution, which is ugly, but at least makes more sense what I want:
const users = [
{
id: 0,
name: "John",
hobby: "soccer"
},
{
id: 1,
name: "Alice",
hobby: "squash"
},
{
id: 2,
name: "Greg",
hobby: "guitar"
}
]
const newUsers = [
{
id: 0,
work: "developer"
},
{
id: 2,
work: "musician"
},
{
id: 3,
name: "Roger",
work: "accountant"
}
]
const concatArray = (newArray, oldArray) => {
const objectFromArray = array => array.reduce((object, user) => {
object[user.id] = user;
return object
}, {});
const objectOfOldArray = objectFromArray(oldArray);
const objectOfNewArray = objectFromArray(newArray);
const allIds = Object.keys({...objectOfOldArray, ...objectOfNewArray})
return allIds.map(id => {
const oldProps = objectOfOldArray[id] || {};
const newProps = objectOfNewArray[id] || {};
return {id, ...oldProps, ...newProps}
})
}
console.log(concatArray(newUsers, users))
It works fine, but there should be a more sufficient solution for this. I mean it's a very small operation, adding some properties to the specified objects, but my solution is too over-complicated to earn this. There should be an easier way to earn this.

You can try below approach of Array.forEach
const users = [
{
id: 0,
name: "John",
hobby: "soccer"
},
{
id: 1,
name: "Alice",
hobby: "squash"
},
{
id: 2,
name: "Greg",
hobby: "guitar"
}
]
const newUsers = [
{
id: 0,
work: "developer"
},
{
id: 2,
work: "musician"
},
{
id: 3,
name: "Roger",
work: "accountant"
}
]
let updatedUsers = {};
[...users, ...newUsers].forEach(d => updatedUsers[d.id] = { ...(updatedUsers[d.id] || {}), ...d })
console.log(Object.values(updatedUsers))

Related

Compare two arrays of objects and merge some fields

I'm working with Angular and RxJs and I have two arrays of objects. I need to change one specific field of the first array, if the second one has the field with the same value (all of the four fields has different names). I did it with nested loops, but I need to find a better solution, my code is down below
My solution is working, but it's not the best, because arrays can be really large - so the code will work slow. If there's 1000 items in each array, it will be 1000000 iterations - that's why I need to find a better solution. I got advice to use multiple consecutive loops, but I don't really get how to use it here
this.api
.getFirstArray()
.pipe(
mergeMap((firstArray) =>
this._secondApi.getSecondArray().pipe(
map((secondArray) => {
for (const item2 of secondArray) {
for (const item1 of firstArray) {
if (item1.someField === item2.otherField)
item1.someOtherField = item2.anotherField;
}
}
return firstArray;
}),
),
),
)
.subscribe((value) => {
this.gridApi?.setRowData(value);
});
So for example my data is
firstArray: [
{ id: 445; name: 'test' },
{ id: 4355; name: 'test1' },
{ id: 234_234; name: 'test2' },
];
secondArray: [
{ firstName: 'test3'; newId: 445 },
{ firstName: 'test5'; newId: 2 },
{ firstName: 'test6'; newId: 234_234 },
];
And the result should be
result: [{ id: 445; name: 'test3' }, { id: 4355; name: 'test1' }, { id: 234_234; name: 'test6' }];
Note: the ids of the first array objects may be repeated - all of the objects names need to be updated
here is the working example of your problem, may be it will help you.
let firstArray = [
{ id: 445, name: 'test' },
{ id: 4355, name: 'test1' },
{ id: '234_234', name: 'test2' },
];
let secondArray = [
{ firstName: 'test3', newId: 445 },
{ firstName: 'test5', newId: 2 },
{ firstName: 'test6', newId: '234_234' },
];
secondArray.forEach(sec => {
let see = firstArray.findIndex(first => first.id === sec.newId);
if (see > -1) {
firstArray[see].name = sec.firstName
}
})
console.log(firstArray)
You still end up with O(N²) complexity (there are two nested loops that he wants to avoid).
Instead, You can use map
const firstArray = [
{ id: 445, name: 'test' },
{ id: 4355, name: 'test1' },
{ id: '234_234', name: 'test2' },
];
const secondArray = [
{ firstName: 'test3', newId: 445 },
{ firstName: 'test5', newId: 2 },
{ firstName: 'test6', newId: '234_234' },
];
const secondMap = new Map();
secondArray.forEach((item) => {
secondMap.set(item.newId, item.firstName);
});
for (const item of firstArray) {
if (secondMap.has(item.id)) {
item.name = secondMap.get(item.id);
}
}
console.log(firstArray)

JavaScript Array Filtering in Nested Arrays

I have an array that looks something like this:
const arrayObj = [
{
id: 1,
itemsList: [
{
name: "Paul",
},
{
name: "Newman",
},
],
},
{
id: 2,
itemsList: [
{
name: "Jack",
},
{
name: "Man",
},
],
},
]
What I want is to filter the objects whose itemsList contain an object with the name of a certain value. For example, I want to be able to filter out an array with objects whose inner objects with names that contain "ul" (in this case the name Paul contains "ul"), it should give me an output as such:
const outputArray = [
{
id: 1,
itemsList: [
{
name: "Paul",
},
{
name: "Newman",
},
]
}
]
So far, I've only been able to filter out a simple flat array of objects with this function:
function filterByName(array: any, string: any) {
return array.filter((obj: any) =>
["name"].some((key: any) =>
String(obj[key]).toLowerCase().includes(string.toLowerCase())
)
);
}
but I don't know how to apply it to my case.
Here you can use the some method combined with the includes method
const arrayObj = [{
id: 1,
itemsList: [{
name: "Paul",
},
{
name: "Newman",
},
],
},
{
id: 2,
itemsList: [{
name: "Jack",
},
{
name: "Man",
},
],
},
]
const getFilterArray = (name) => {
return arrayObj.filter(obj => obj.itemsList.some(x => x.name.toLowerCase().includes(name.toLowerCase())))
}
console.log(getFilterArray("ul"))
const result = arrayObj.filter(({ itemsList }) =>
itemsList.some(({ name }) => name.toLowerCase().includes('ul')));
Can you try this?

compare and filter 2 arrays

I have 2 arrays. I need to show only data which does not match with the second array.
array1 = [
{
country: "usa",
child: [
{ id: 1, name: "fvsdfsd" },
{ id: 2, name: "hhghhhfhj" },
],
},
{
country: "CA",
child: [
{ id: 3, name: "adsada" },
{ id: 4, name: "hhghhhfhj" },
],
},
{
country: "AU",
child: [
{ id: 5, name: "seven" },
{ id: 6, name: "hhghhhfhj" },
],
},
];
array2 = [
{ id: 1, name: "fvsdfsd" },
{ id: 2, name: "hhghhhfhj" },
];
result:
[
{
country: "usa",
child: [],
},
{
country: "CA",
child: [
{ id: 3, name: "adsada" },
{ id: 4, name: "hhghhhfhj" },
],
},
{
country: "AU",
child: [
{ id:5, name: "seven" },
{ id:6, name: "hhghhhfhj" },
],
},
]
I try like this but its not working
array1.filter(data => !array2.includes(data.child));
Your code is not working because of how Ecma/Javascript does equality testing. Array.includes() uses the sameValueZero algorithm for determining if two things are "equal".
[And equality in Javascript is odd]
Object comparison is done by reference, so two object are equal if (and only if) they are the exact same object in memory. For instance
const areEqual = {a:1,b:2} === {a:1,b:2}
is false, as is
const areEqual = {a:1,b:2} == {a:1,b:2}
You need to do deep equality checking with something like lodash's isEqual():
const _ = require('lodash');
const areEqual = _.isEqual( {a:1,b:2} , {a:1,b:2} ) ; // returns true
So you should be able to say something like:
const _ = require('lodash');
const filtered = array1.map( o => {
return {
...o,
child: _.isEqual( o.child, array2 ) ? [] : o.child ),
}
});
Yeah, you can try like this.
array1.map(item1 => ({ ...item1, child: item1.child.filter(childItem => !array2.find(item2 => JSON.stringify(item2) === JSON.stringify(childItem))) }));
If the keys of the object are not in the same order, as Everett Glovier said, you can try like this.
array1.map(item1 => ({ ...item1, child: item1.child.filter(childItem => !array2.find(item2 => item2.id === childItem.id && item2.name === childItem.name)) }));
You cannot compare two different objects using includes in javascript, because includes uses ===. Only references to the same object will return true using ===. You'll need to write a custom function that runs through all of the keys of your object and compares their values between your two arrays.
This article explains some techniques for comparing two objects:
https://dmitripavlutin.com/how-to-compare-objects-in-javascript/

double nested array of object es6 filter

I want to filter out a nested array of objects but stuck at the filter part.
How to remove one of the mark?
this.state = {
data: [
{
id: 1,
name: "Main",
subs: [
{
id: "jay",
name: "Jay",
mark: [
{
id: "5a5d84b94a074c49ef2d4553",
name: 100
},
{
id: "5a5d84b94a074119ef2d4553",
name: 70
}
]
}
]
}
]
};
https://codesandbox.io/s/p39momxzp7
I try to use es6 as it's more readable.
expected output
data: [
{
id: 1,
name: "Main",
subs: [
{
id: "jay",
name: "Jay",
mark: [
{
id: "5a5d84b94a074119ef2d4553",
name: 70
}
]
}
]
}
]
Since there are multiple nested arrays in your data structure, you need to use forEach those many times
data.forEach( s => //iterate data
s.subs.forEach( t => //iterate subs
( t.mark = t.mark.slice( 1, 2 ) ) ) ); //slice the second value out
Demo
var data = [{
id: 1,
name: "Main",
subs: [{
id: "jay",
name: "Jay",
mark: [{
id: "5a5d84b94a074c49ef2d4553",
name: 100
},
{
id: "5a5d84b94a074119ef2d4553",
name: 70
}
]
}]
}];
data.forEach(s => s.subs.forEach(t => (t.mark = t.mark.slice(1,2))));
console.log(JSON.stringify(data, 0, 4))
In case the last value should be picked?
data.forEach( s => //iterate data
s.subs.forEach( t => //iterate subs
( t.mark = t.mark.slice( -1 ) ) ) ); //slice the last value out
If you are trying to filter a relevant mark by a given id,
you can combine Array#map and Array#filter to achieve it:
Note that i'm also using the Object Rest/Spread Properties proposal (stage 4)
Running example
const state = {
data: [{
id: 1,
name: "Main",
subs: [{
id: "jay",
name: "Jay",
mark: [{
id: "5a5d84b94a074c49ef2d4553",
name: 100
}, {
id: "5a5d84b94a074119ef2d4553",
name: 70
}]
}]
}]
};
const mark_id = '5a5d84b94a074119ef2d4553';
const nextState = {
...state,
data: state.data.map(obj => {
const filteredSubs = obj.subs.map(sub => {
const markById = sub.mark.filter(m => m.id === mark_id);
return {
...sub,
mark: markById
}
});
return {
...obj,
subs: filteredSubs
}
})
};
console.log(nextState);
You can even use lodash which contains many methods that can be handled easily.
Check if this is what you are looking for. (there is a good scope to refactor it but before that would like to understand if thats what you are looking for)
Below is the code that has been used there.
let inputId = "5a5d84b94a074c49ef2d4553";
let filteredData =_.each(_.cloneDeep(data), function(value, key1) {
_.each(value.subs, function(valueSubs, key2) {
var finalSubMark = _.find(valueSubs.mark, function(eachMark) {
return eachMark.id == inputId;
});
_.set(valueSubs, "mark", finalSubMark);
});
});
https://codesandbox.io/s/v065w05rly

Removing duplicate array values and then storing them [react]

I'm trying to strip the duplicate array values from my current array. And I'd like to store the fresh list (list without duplicates) into a new variable.
var names = ["Daniel","Lucas","Gwen","Henry","Jasper","Lucas","Daniel"];
const uniqueNames = [];
const namesArr = names.filter((val, id) => {
names.indexOf(val) == id; // this just returns true
});
How can I remove the duplicated names and place the non-duplicates into a new variable?
ie: uniqueNames would return...
["Daniel","Lucas","Gwen","Henry","Jasper"]
(I'm using react jsx) Thank you!
You can do it in a one-liner
const uniqueNames = Array.from(new Set(names));
// it will return a collection of unique items
Note that #Wild Widow pointed out one of your mistake - you did not use the return statement. (it sucks when we forget, but it happens!)
I will add to that that you code could be simplified and the callback could be more reusable if you take into account the third argument of the filter(a,b,c) function - where c is the array being traversed. With that said you could refactor your code as follow:
const uniqueNames = names.filter((val, id, array) => {
return array.indexOf(val) == id;
});
Also, you won't even need a return statement if you use es6
const uniqueNames = names.filter((val,id,array) => array.indexOf(val) == id);
If you want to remove duplicate values which contains same "id", You can use this.
const arr = [
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 2, name: "ra one" },
{ id: 3, name: "alex" },
{ id: 1, name: "devid" },
{ id: 7, name: "sam" },
];
function getUnique(arr, index) {
const unique = arr
.map(e => e[index])
// store the keys of the unique objects
.map((e, i, final) => final.indexOf(e) === i && i)
// eliminate the dead keys & store unique objects
.filter(e => arr[e]).map(e => arr[e]);
return unique;
}
console.log(getUnique(arr,'id'))
Result :
[
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 7, name: "sam" }
]
you forgot to use return statement in the filter call
const namesArr = duplicatesArray.filter(function(elem, pos) {
return duplicatesArray.indexOf(elem) == pos;
});
Since I found the code of #Infaz 's answer used somewhere and it confused me greatly, I thought I would share the refactored function.
function getUnique(array, key) {
if (typeof key !== 'function') {
const property = key;
key = function(item) { return item[property]; };
}
return Array.from(array.reduce(function(map, item) {
const k = key(item);
if (!map.has(k)) map.set(k, item);
return map;
}, new Map()).values());
}
// Example
const items = [
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 2, name: "ra one" },
{ id: 3, name: "alex" },
{ id: 1, name: "devid" },
{ id: 7, name: "sam" },
];
console.log(getUnique(items, 'id'));
/*Output:
[
{ id: 2, name: "sumit" },
{ id: 1, name: "amit" },
{ id: 3, name: "rahul" },
{ id: 4, name: "jay" },
{ id: 7, name: "sam" }
]
*/
Also you can do this
{Array.from(new Set(yourArray.map((j) => j.location))).map((location) => (
<option value={`${location}`}>{location}</option>
))}

Categories