Changing the keys of a nested object in an Array with JavaScript - javascript

I need to change the keys of my object. I could use the map function to change the keys of my outer object. Question is, how can I access the inner object which is in an array as well. In the code below, I need to change the team key to teamName. My structure has to be in the same order.
let myArray = [
{
id: 1,
name: "foo",
Organization: [{ team: "value1" }, { location: "value2" }],
},
{
id: 2,
name: "foo",
Organization: [{ team: "value1" }, { location: "value2" }],
},
];
I can change the keys of the outer array like this if I want to change id to userId.
const newArray = myArray.map((item) => {
return {
userId: item.id,
};
});
But trying to change the keys in the inner list of objects for Organization becomes a problem. What is the best way to modify the inner keys?

Option 1 - lodash mapKeys
import { mapKeys } from 'lodash';
const newArray = myArray.map(item => ({
...item,
Organization: item.Organization.map(org =>
mapKeys(org, (_, key) => (key === 'team' ? 'teamName' : key))
),
}));
Option 2 - object destruction
You can destruct each Organization and reconstruct it with teamName, as long as team exists.
const newArray = myArray.map(item => ({
...item,
Organization: item.Organization.map(({ team, ...rest }) =>
Object.assign(rest, team ? { teamName: team } : {})
),
}));
Result
[
{
id: 1,
name: 'foo',
Organization: [{ teamName: 'value1' }, { location: 'value2' }],
},
{
id: 2,
name: 'foo',
Organization: [{ teamName: 'value1' }, { location: 'value2' }],
},
];

If Organization is always an array with 2 elements. Where the first element is an object with the property team, and the second element is an object with the property location. Then the following code does job.
let myArray = [{
"id": 1,
"name": "foo",
"Organization": [{"team": "value1"}, {"location": "value2"}]
}, {
"id": 2,
"name": "foo",
"Organization": [{"team": "value1"}, {"location": "value2"}]
}];
const result = myArray.map((item) => {
const [{ team: teamName }, location] = item.Organization;
return { ...item, Organization: [{ teamName }, location] };
});
console.log(result);
This answer makes use of destructuring assignment. If you don't know what this is I would highly suggest checking out the linked documentation.

Couldn't make it more simpler.
console.log(
[{
"id": 1,
"name": "foo",
"Organization": [{
"team": "value1"
}, {
"location": "value2"
}]
},
{
"id": 2,
"name": "foo",
"Organization": [{
"team": "value1"
}, {
"location": "value2"
}]
},
].reduce((a, b) => {
b.Organization[0] = {
teamName: b.Organization[0].team
}
a.push(b)
return a
}, [])
)

Related

How to convert array of object property?

I'm trying to convert array of objects having value as a array -> into string. I'm facing blocker in this,
let peopleDetails = [
{
name: "raj",
favCar: [{ name: "audi", color: "white" }],
favFood: [{ color: "brown", name: "Idli" }],
},
{ name: "deepak", place: "India", favPlace: [{ name: "Tajmahal" }] },
];
I need structure like,
let peopleDetails = [
{ name: "raj", favCar: "audi", favFood: "Idli" },
{ name: "deepak", place: "India", favPlace: "Tajmahal" },
];
For what I understand you want the value of every property to become the name value of its first element, when the value is an array of object.
Here's an immutable solution that follows this concept:
let peopleDetails = [
{
name: 'raj',
favCar: [{ name: 'audi', color: 'white' }],
favFood: [{ name: 'Idli' }],
},
{ name: 'deepak', place: 'India', favPlace: [{ name: 'Tajmahal' }] },
];
const result = peopleDetails.map(obj =>
Object.fromEntries(Object.entries(obj).map(([key, value]) =>
[key, value?.[0]?.name ?? value]
))
);
console.log(result);
I'm not sure why you've got arrays of singular elements (favourites typically don't have second or third places) so if you just want to extract the name from each item that's an array, take it from the first element in each array:
let peopleDetails = [{
name: "raj",
favCar: [{
name: "audi",
color: "white"
}],
favFood: [{
name: "Idli"
}],
},
{
name: "deepak",
place: "India",
favPlace: [{
name: "Tajmahal"
}]
},
];
let peopleDetailsModified = peopleDetails.map(o => {
let retObj = {};
for (key in o) {
if (Array.isArray(o[key])) {
retObj[key] = o[key][0].name;
} else {
retObj[key] = o[key];
}
}
return retObj;
});
console.log(peopleDetailsModified);
I've made this code more verbose than it needs to be, it's quite easy to one-line it using reduce.
I'm also quite not sure what you're trying to achieve as the other answers but I tried to make a map of the current object to an object that is more to your liking. I formatted it a little so you can see what I did:
peopleDetails.map(item => {
return {
name: item.name,
place: item.place,
favCar: item.favCar ? item.favCar[0].name : "",
favPlace: item.favPlace ? item.favPlace[0].name : "",
favFood: item.favFood ? item.favFood[0].name : ""}});
I have ran the code and its a generic code for similar data
const _ = require('lodash')
const peopleDetails =[
{
"name": "raj",
"favCar": [
{
"name": "audi",
"color": "white"
}
],
"favFood": [
{
"name": "Idli"
}
]
},
{
"name": "deepak",
"place": "India",
"favPlace": [
{
"name": "Tajmahal"
}
]
}
]
const newPeopleDetails = peopleDetails.map(obj => {
const formObj = {
favCar: obj && obj.favCar && obj.favCar[0].name,
favFood: obj && obj.favFood && obj.favFood[0].name,
favPlace: obj && obj.favPlace && obj.favPlace[0].name
}
const finalObj = _.pickBy(formObj, _.identity);
return Object.assign({}, obj, finalObj);
});
console.log(newPeopleDetails)

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?

How do I combine 2 array of objects having common key value pair in node js [duplicate]

This question already has answers here:
How to merge multiple array of object by ID in javascript?
(5 answers)
Closed 1 year ago.
Input arrays
arr1=[{"CATALOG":"book1","ID":"1"},{"CATALOG":"book2","ID":"2"},{"CATALOG":"book3","ID":"3"},{"CATALOG":"book4","ID":"12"}]
arr2=[{"NAME":"TOM","ID":"1"},{"NAME":"STEVE","ID":"22"},{"NAME":"HARRY","ID":"2"},{"NAME":"TIM","ID":"3"},{"NAME":"DAVE","ID":"12"},{"NAME":"WIL","ID":"12"},{"NAME":"PETER","ID":"94"},{"NAME":"SAVANNAH","ID":"77"}]
Expected Output
[{"CATALOG":"book1","ID":"1","NAME":"TOM"},
{"CATALOG":"book2","ID":"2","NAME":"HARRY"},
{"CATALOG":"book3","ID":"3","NAME":"TIM"},
{"CATALOG":"book4","ID":"12","NAME":"WIL"}
expected output is that 2 arrays have to be combined based on id. If ID doesn't exist then that particular object is skipped
I tried using
[arr1,arr2].reduce((a, b) => a.map((c, i) => Object.assign({}, c, b[i])));
But not getting the desired output
You can use map and find
const arr1 = [
{ CATALOG: "book1", ID: "1" },
{ CATALOG: "book2", ID: "2" },
{ CATALOG: "book3", ID: "3" },
{ CATALOG: "book4", ID: "12" },
];
const arr2 = [
{ NAME: "TOM", ID: "1" },
{ NAME: "STEVE", ID: "22" },
{ NAME: "HARRY", ID: "2" },
{ NAME: "TIM", ID: "3" },
{ NAME: "DAVE", ID: "12" },
{ NAME: "WIL", ID: "12" },
{ NAME: "PETER", ID: "94" },
{ NAME: "SAVANNAH", ID: "77" },
];
const output = arr1.map(a => ({
...a,
NAME: arr2.find(x => x.ID === a.ID).NAME,
}));
console.log(output);
There may be cleverer solutions, but assuming arr2 always contains the corresponding ID, I'd do it simply with :
const arr1=[{"CATALOG":"book1","ID":"1"},{"CATALOG":"book2","ID":"2"},{"CATALOG":"book3","ID":"3"},{"CATALOG":"book4","ID":"12"}];
const arr2=[{"NAME":"TOM","ID":"1"},{"NAME":"STEVE","ID":"22"},{"NAME":"HARRY","ID":"2"},{"NAME":"TIM","ID":"3"},{"NAME":"DAVE","ID":"12"},{"NAME":"WIL","ID":"12"},{"NAME":"PETER","ID":"94"},{"NAME":"SAVANNAH","ID":"77"}];
const output = arr1.map(obj1 => {
const obj2 = arr2.find(o2 => o2.ID === obj1.ID);
return Object.assign(obj1, obj2)
});
console.log(output)
Or as a one-liner :
const output = arr1.map(obj1 => Object.assign(obj1, arr2.find(o2 => o2.ID === obj1.ID)))

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/

How to efficiently construct new object array using unique values from arrays of children inside an array of parent objects

The need is to take objects like this:
[ { "first":
{ "children" : [{ "name": "abc", "detail":"123"},
{ "name": "def", "detail":"456"}
]
}},
{ "second":
{ "children" : [{ "name": "ghi", "detail":"123"},
{ "name": "jkl", "detail":"456"}
]
}},
{ "third":
{ "children" : [{ "name": "mno", "detail":"123"},
{ "name": "pqr", "detail":"456"}
]
}},
{ "fourth":
{ "children" : [{ "name": "stu", "detail":"123"},
{ "name": "vwx", "detail":"456"}
]
}},
{ "fifth":
{ "children" : [{ "name": "yz", "detail":"123"},
{ "name": "abc", "detail":"456"}
]
}},
{ "sixth":
{ "children" : [{ "name": "def", "detail":"123"},
{ "name": "ghi", "detail":"456"}
]
}}
]
and then create a flattened array of unique values (options for a select) from the name field of the children that looks like this:
[{"value":"abc", "label":"abc"},
{"value":"def", "label":"def"},
{"value":"ghi", "label":"ghi"},
{"value":"jkl", "label":"jkl"},
{"value":"mno", "label":"mno"},
{"value":"pqr", "label":"pqr"},
{"value":"stu", "label":"stu"},
{"value":"vwx", "label":"vwx"},
{"value":"yz", "label":"yz"}
]
The code below is working, but it looks like it is inefficient because it appears to make many passes over the array:
[
...new Set(
[].concat.apply([], bases.map((base) => {
if (!base.children || base.children.length === 0) return;
return base.children}
)).map((child) => child.name)
)
].map((optName) => {return {value: optName, label: optName};})
If it is possible, how can this same result be achieved without as many iterations across the array.
Firstly, as a rule of thumb, you shouldn't worry too much about performance until you have a reason to do so.
Secondly, chaining the array prototype functions (e.g. map, forEach, filter) will require multiple iterations by design.
Thirdly, there's no reason to assume multiple iterations is slower than a single iteration if the work done within the iterations is the same anyways. I.e. incrementing an index and comparing it with an array length isn't going to be the bottleneck compared to pushing objects into arrays and check set entries.
Here's a (IMO) cleaner snippet to extract unique names from your array:
let bases = [{
children: [{
name: "abc",
detail: "123"
},
{
name: "def",
detail: "456"
}
]
}, {
children: [{
name: "abc" ,
detail: "123"
},
{
name: "xyz" ,
detail: "456"
}
]
},
{}
];
let output = bases
.flatMap(b => b.children || [])
.map(c => c.name)
.filter((v, i, a) => a.indexOf(v) === i) // filter unique values
.map(name => ({
value: name,
label: name,
}));
console.log(output);
Now if you really want to do all this in a single iteration, that too is possible, but harder to read:
let bases = [{
children: [{
name: "abc",
detail: "123"
},
{
name: "def",
detail: "456"
}
]
}, {
children: [{
name: "abc" ,
detail: "123"
},
{
name: "xyz" ,
detail: "456"
}
]
},
{}
];
let output = [];
let seenNames = {};
for (base of bases) {
if (!base.children)
continue;
for (child of base.children) {
let name = child.name;
if (seenNames[name])
continue;
seenNames[name] = true;
output.push({
value: name,
label: name,
});
}
}
console.log(output);
You could take Array#flatMap for getting a flat representation of data for using unique values and map new objects.
var data = [{ first: { children: [{ name: "abc", detail: "123" }, { name: "def", detail: "456" }] } }, { second: { children: [{ name: "ghi", detail: "123" }, { name: "jkl", detail: "456" }] } }, { third: { children: [{ name: "mno", detail: "123" }, { name: "pqr", detail: "456" }] } }, { fourth: { children: [{ name: "stu", detail: "123" }, { name: "vwx", detail: "456" }] } }, { fifth: { children: [{ name: "yz", detail: "123" }, { name: "abc", detail: "456" }] } }, { sixth: { children: [{ name: "def", detail: "123" }, { name: "ghi", detail: "456" }] } }],
result = Array.from(
new Set(data
.flatMap(Object.values)
.flatMap(({ children }) => children.map(({ name }) => name))
),
value => ({ value, label: value })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories