How to convert array of object property? - javascript

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)

Related

typescript/js comparing arrays of objects adding new key value

My goal is to compare 2 objects if there is a match between object 1 and 2 using if they have the same id then insert new key value to object 1 which isConfirmed = true to each object that has a match;
Any idea guys ? I provided my current code below. Thanks.
#objects - original data
const object1 = [
{
"id": 10691,
"city": "Morris",
},
{
"id": 10692,
"city": "NY",
]
const object2 = [
{
"id": 10691,
"city": "Morris",
{
"id": 10500,
"city": "JY",
}
]
#ts code
let result = object1.filter(o1 => object2.some(o2 => o1.id === o2.id));
#expected sample result
object1 = [
{
"id": 10691,
"city": "Morris",
"isConfirmed": true,
},
{
"id": 10692,
"city": "NY",
}
]
You can easily achieve the result using Set and map as:
const object1 = [
{
id: 10691,
city: 'Morris',
},
{
id: 10692,
city: 'NY',
},
];
const object2 = [
{
id: 10691,
city: 'Morris',
},
{
id: 10500,
city: 'JY',
},
];
const map = new Set(object2.map((o) => o.id));
const result = object1.map((o) =>
map.has(o.id) ? { ...o, isConfirmed: true } : { ...o },
);
console.log(result);
You can do it by using the snippet
let result = object1.map(o1 =>
object2.some(o2 => o1.id === o2.id)
? {...o1, isConfirmed: true}
: {...o1}
);
Check if object2 array has the object with any id from object1. If so, add isConfirmed: true to it.
const object1 = [
{
id: 10691,
city: 'Morris',
},
{
id: 10692,
city: 'NY',
},
];
const object2 = [
{
id: 10691,
city: 'Morris',
},
{
id: 10500,
city: 'JY',
},
];
let result = object1.map(o1 => object2.some(o2 => o1.id === o2.id) ? {...o1, isConfirmed: true} : {...o1});
console.log(result);

Changing the keys of a nested object in an Array with 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
}, [])
)

Recursively Transform my JSON data with JS

I'm trying to figure out how to transform some JSON i'm getting back from a web service so i can easily parse it into a nice type-safe object. I want to transform this format from:
[{
"name": "AwesomePeople",
"value": [
[{
"name": "TypeId",
"value": 1
}, {
"name": "People",
"value": [
[{
"name": "id",
"value": 2
}, {
"name": "name",
"value": "Danno"
}
],
[{
"name": "id",
"value": 3
}, {
"name": "name",
"value": "Julio"
}
]
]
}
],
[{
"name": "TypeId",
"value": 2
}, {
"name": "People",
"value": [
[{
"name": "id",
"value": 4
}, {
"name": "name",
"value": "Jenna"
}
],
[{
"name": "id",
"value": 5
}, {
"name": "name",
"value": "Coolio"
}
]
]
}
]
]
}
]
To the following format:
[{
"AwesomePeople": [
[{
"TypeId": 1,
}, {
"People": [
[{
"id": 2
}, {
"firstName":"Danno"
}
],
[{
"id": 3,
}, {
"firstName": "Julio"
}
]
]
}
],
[{
"TypeId": 2
}, {
"People": [
[{
"id": 4
}, {
"firstName": "Jenna"
}
],
[{
"id": 5
}, {
"firstName": "Coolio"
}
]
]
}
]
]
}
];
Two main things need to happen, these stupid "name"/"value" pairs need to be swapped at any and all levels. For example, instead of "name": "id", "value": "3", it would be simply be "id":3. The values are sometimes are arrays, so they need to processed in a similar way...the depth is variable, so i can't assume a certain number of levels deep, so i need to keep processing everything recursively.
I have started playing with the following code...you'll see an empty "newResult" array that i'm trying to build as i traverse the original JSON, taking different action whether i'm currently looking at an object, an array, or a key/property.
let count = 0;
let result = <the original array above>
let newResult = [];
result.forEach(function(resObj) {
console.log("STARTING to TRAVERSE HIGHER LEVEL OBJECTS!");
traverse(resObj);
count++;
//we're done processing high level objects, so return from this function and enjoy the newResult!
if (count===result.length)
//return from this function
console.log(newResult);
console.log("FINISHED PROCESSING HIGHER LEVEL OBJECTS, SO DONE!");
});
//Below are the functions for traversing
function traverse(x, level) {
if (isArray(x)) {
console.log("array");
traverseArray(x);
} else if ((typeof x === 'object') && (x !== null)) {
console.log("object");
traverseObject(x);
} else {
console.log("property: "+x);
//console.log(level + x);
}
}
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]';
}
function traverseArray(arr, level) {
//console.log(level + "<array>");
arr.forEach(function(x) {
traverse(x);
});
}
function traverseObject(obj, level) {
var keyName, keyValue;
//console.log(level + "<object>");
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (key==="name"){
keyName = obj[key];
}else if (key==="value"){
keyValue = obj[key];
}
if (keyName && keyValue){
var newObj = {[keyName]: keyValue}
newResult.push(newObj);
//console.log("the KEY NAME IS: "+ keyName + ", and the VALUE is: "+keyValue);
}
//if we have a key value, but the value is an array, stop and
// if (isArray(newOj)
console.log("traversing..." +obj[key]);
traverse(obj[key]);
}//end if property
}//end foreach key in object
}//end traverseObject
thanks all...kudos to the person who can get their brain around this :)
You can do this with JSON.stringify and JSON.parse - with a reviver, check if the value has a name property, and if it does, return { [value.name]: value.value }:
const arr=[{name:"AwesomePeople",value:[[{name:"TypeId",value:1},{name:"People",value:[[{name:"id",value:2},{name:"name",value:"Danno"}],[{name:"id",value:3},{name:"name",value:"Julio"}]]}],[{name:"TypeId",value:2},{name:"People",value:[[{name:"id",value:4},{name:"name",value:"Jenna"}],[{name:"id",value:5},{name:"name",value:"Coolio"}]]}]]}];
const result = JSON.parse(JSON.stringify(arr, (key, value) => (
value?.name
? { [value.name]: value.value }
: value
)));
console.log(result);
If you also want to change the name values to firstName keys, add a conditional in the computed property:
const arr=[{name:"AwesomePeople",value:[[{name:"TypeId",value:1},{name:"People",value:[[{name:"id",value:2},{name:"name",value:"Danno"}],[{name:"id",value:3},{name:"name",value:"Julio"}]]}],[{name:"TypeId",value:2},{name:"People",value:[[{name:"id",value:4},{name:"name",value:"Jenna"}],[{name:"id",value:5},{name:"name",value:"Coolio"}]]}]]}];
const result = JSON.parse(JSON.stringify(arr, (key, value) => (
value?.name
? { [value.name === 'name' ? 'firstName' : value.name]: value.value }
: value
)));
console.log(result);
Manually:
const arr=[{name:"AwesomePeople",value:[[{name:"TypeId",value:1},{name:"People",value:[[{name:"id",value:2},{name:"name",value:"Danno"}],[{name:"id",value:3},{name:"name",value:"Julio"}]]}],[{name:"TypeId",value:2},{name:"People",value:[[{name:"id",value:4},{name:"name",value:"Jenna"}],[{name:"id",value:5},{name:"name",value:"Coolio"}]]}]]}];
const recurse = (val) => {
if (!val || typeof val !== 'object') return val;
if (Array.isArray(val)) return val.map(recurse);
return { [val.name === 'name' ? 'firstName' : val.name]: val.value };
};
const result = recurse(arr);
console.log(result);
I grabbed your data, quickly wrote a function to convert it, came to post it and realized that my output wasn't at all what you requested. I would just throw it away, except that it seems to me this output is much more useful than what you requested. So if you can use something like this:
{
AwesomePeople: [
{
TypeId: 1,
People: [
{id: 2, name: "Danno"},
{id: 3, name: "Julio"}
]
},
{
TypeId: 2,
People: [
{id: 4, name: "Jenna"},
{id: 5, name: "Coolio"}
]
}
]
}
then this function may help:
const convert = (xs) =>
Object .fromEntries (
xs .map (({name, value}) => [
name,
Array .isArray (value) ? value .map (convert) : value
])
)
const data = [{name: "AwesomePeople", value: [[{name: "TypeId", value: 1}, {name: "People", value: [[{name: "id", value: 2}, {name: "name", value: "Danno"}], [{name: "id", value: 3}, {name: "name", value: "Julio"}]]}], [{name: "TypeId", value: 2}, {name: "People", value: [[{name: "id", value: 4}, {name: "name", value: "Jenna"}], [{name: "id", value: 5}, {name: "name", value: "Coolio"}]]}]]}]
console .log (convert (data))
.as-console-wrapper {min-height: 100% !important; top: 0}
If not, well then maybe someone else might get some use out of it.
Here is an answer using object-scan. This code modifies the original input, which can be significantly faster than rebuilding the structure.
Note that in your input the data is a bit inconsistent: Where does firstName come from? So I've assumed consistency
// const objectScan = require('object-scan');
const data = [{ name: 'AwesomePeople', value: [ [{ name: 'TypeId', value: 1 }, { name: 'People', value: [ [{ name: 'id', value: 2 }, { name: 'name', value: 'Danno' } ], [{ name: 'id', value: 3 }, { name: 'name', value: 'Julio' } ] ] } ], [{ name: 'TypeId', value: 2 }, { name: 'People', value: [ [{ name: 'id', value: 4 }, { name: 'name', value: 'Jenna' } ], [{ name: 'id', value: 5 }, { name: 'name', value: 'Coolio' } ] ] } ] ] } ];
objectScan(['**[*].name'], {
filterFn: ({ parent }) => {
const { name, value } = parent;
delete parent.name;
delete parent.value;
parent[name] = value;
}
})(data);
console.log(data);
// => [ { AwesomePeople: [ [ { TypeId: 1 }, { People: [ [ { id: 2 }, { name: 'Danno' } ], [ { id: 3 }, { name: 'Julio' } ] ] } ], [ { TypeId: 2 }, { People: [ [ { id: 4 }, { name: 'Jenna' } ], [ { id: 5 }, { name: 'Coolio' } ] ] } ] ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan

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; }

filter array for unique items with item count

Helo,
I am using the following code to return a unique list of categories.
stadium_cats: function () {
let stadiums =[
{"name":"Elland road","category":"football","country":"England"},
{"name":"Principality stadiums","category":"rugby","country":"Wales"},
{"name":"Twickenham","category":"rugby","country":"England"},
{"name":"Eden Park","category":"rugby","country":"New Zealand"}
];
var categories = stadiums.map(function(obj) {return obj.category});
categories = categories.filter(function(v,i) {return categories.indexOf(v) == i; });
return categories;
}
When I run the above I get an array of unique stadium.category values.
Could someone help me extend it, so that instead of an array returned I get an array of objects as follows:
[{"name":"football","count":"1"}{"name":"rugby","count":"3"}]
I would like both the name and how many times it was featured?
Is this at all possible?
Many thanks,
Dave
You can do this with forEach loop and object as optional parameter.
let stadiums = [{"name":"Elland road","category":"football","country":"England"},{"name":"Principality stadiums","category":"rugby","country":"Wales"},{"name":"Twickenham","category":"rugby","country":"England"},{"name":"Eden Park","category":"rugby","country":"New Zealand"}];
var result = [];
stadiums.forEach(function(e) {
if(!this[e.category]) {
this[e.category] = {name: e.category, count: 0}
result.push(this[e.category]);
}
this[e.category].count++;
}, {});
console.log(result);
Just one line with ES6:
let stadiums = [{ name: 'Elland road', category: 'football', country: 'England' }, { name: 'Principality stadiums', category: 'rugby', country: 'Wales' }, { name: 'Twickenham', category: 'rugby', country: 'England' }, { name: 'Eden Park', category: 'rugby', country: 'New Zealand' }];
let result = [...new Set(stadiums.map(s => s.category))].map(c => ({ name: c, count: stadiums.filter(s => s.category === c).length }));
console.log(result);
function stadium_cats() {
let stadiums = [{
"name": "Elland road",
"category": "football",
"country": "England"
}, {
"name": "Principality stadiums",
"category": "rugby",
"country": "Wales"
}, {
"name": "Twickenham",
"category": "rugby",
"country": "England"
}, {
"name": "Eden Park",
"category": "rugby",
"country": "New Zealand"
}];
var cat_arr = [];
var cats = {};
var stadium;
for (var i = 0; i < stadiums.length; i++) {
stadium = stadiums[i];
if (typeof cats[stadium.category] == 'undefined') {
cats[stadium.category] = {
name: stadium.category,
count: 0
};
cat_arr.push(cats[stadium.category]);
}
cats[stadium.category].count++;
}
return cat_arr;
}
console.log(stadium_cats());
While you asked for an ES6 version, you could use a closure for this.
let stadiums = [{ name: "Elland road", category: "football", country: "England" }, { name: "Principality stadiums", category: "rugby", country: "Wales" }, { name: "Twickenham", category: "rugby", country: "England" }, { name: "Eden Park", category: "rugby", country: "New Zealand" }],
result = [];
stadiums.forEach((hash => a => {
if (!hash[a.category]) {
hash[a.category] = { name: a.category, count: 0 };
result.push(hash[a.category]);
}
hash[a.category].count++;
})(Object.create(null)));
console.log(result);

Categories