Iterate Nested Value in Array in JavaScript/ES6/ES7 - javascript

I need to iterate a nested value in my javascript.
My wanted output should be like this
shows: ['food.order', 'drink.order', 'play.basketball', 'play.soccer']
const results = [
{
"ID": "shops",
"Shopping": [
{
"ID": "food.order",
"Name": "Food"
},
{
"ID": "drink.order",
"Name": "Drink"
}
]
},
{
"ID": "fun",
"Sports": [
{
"ID": "play.basketball",
"Name": "Basketball"
},
{
"ID": "play.soccer",
"Name": "Soccer"
},
]
}
];
console.log(results);
const final = { shows: results.map(data => data['key'].ID) }

Your question is not clear though, but I am assuming that you are searching for ID property and want to grab the value of the ID and make an array. You can try this way-
const results = [{"ID": "shops", "Shopping": [{ "ID": "food.order", "Name": "Food"},{ "ID": "drink.order", "Name": "Drink"}]},{"ID": "fun", "Sports": [{ "ID": "play.basketball", "Name": "Basketball"},{ "ID": "play.soccer", "Name": "Soccer"}]}];
const ans = results.reduce((acc, item) => {
// Iterate throw the each item's properties
Object.values(item).forEach(val => {
// Filter out the objects which has the `ID` property and get the value of the `ID`.
const ids = typeof val === 'object' && val instanceof Array
? val.filter(x => x.ID !== undefined).map(({ID}) => ID)
: [];
acc = [...acc, ...ids];
});
return acc;
}, []);
console.log(ans);
.as-console-wrapper {min-height: 100%!important; top: 0}

Are you looking for something like this?
const results = [{"ID": "shops", "Shopping": [{ "ID": "food.order", "Name": "Food"},{ "ID": "drink.order", "Name": "Drink"}]},{"ID": "fun", "Sports": [{ "ID": "play.basketball", "Name": "Basketball"},{ "ID": "play.soccer", "Name": "Soccer"}]}];
const final = results.reduce((p, n) => {
// Get all object's array props and then reduce their keys
const mapped = Object.keys(n).filter((key) => Array.isArray(n[key])).reduce((arr, key) => [
...arr,
// Get the array by using the object's key, filter out all objects which don't have
// an 'ID' key, and return a new array which only contains the x.ID property
...n[key].filter((x) => x.ID).map((x) => x.ID)
], []);
return [
...p,
...mapped,
];
}, []);
console.log('final', final);

const results=[{ID:"shops",Shopping:[{ID:"food.order",Name:"Food"},{ID:"drink.order",Name:"Drink"}]},{ID:"fun",Sports:[{ID:"play.basketball",Name:"Basketball"},{ID:"play.soccer",Name:"Soccer"}]}];
let arr = results.flatMap(e => Object.values(e).filter(n => Array.isArray(n))) // at this stage you have an array of arrays
.flat() // at this stage you have flat array from previous stage
.map(s => s.ID) // Now you pick the IDs
console.log(arr)

Related

flatten array of object with one loop

I have this array of object, I want to extract its ids.
const arr = [
{
"id": "1",
},
{
"id": "2",
"options": [
{
"id": "2.1",
}
]
},
]
I did this
const one = arr.map(ob => ob.id)
const two = arr.flatMap(ob => ob.options).map(ob => ob?.id).filter(Boolean)
console.log([...one, ...two])
which worked fine, it prints ['1', '2', '2.1'] which is what I wanted but is there any simpler or shorter way to do it?
Recursive with foreach
const arr = [{
"id": "1",
},
{
"id": "2",
"options": [{
"id": "2.1",
}]
},
]
const getResult = (array, result = []) => {
array.forEach(val => {
result.push(val.id)
if (val.options) {
result = getResult(val.options, result)
}
})
return result
}
console.log(getResult(arr))
Here's one possible approach - .concat onto an array of just the parent id property inside the mapper callback.
const arr = [
{
"id": "1",
},
{
"id": "2",
"options": [
{
"id": "2.1",
}
]
},
];
const result = arr.flatMap(obj => [obj.id].concat(obj.options?.map(o => o.id) ?? []));
console.log(result);
Another is
const arr = [
{
"id": "1",
},
{
"id": "2",
"options": [
{
"id": "2.1",
}
]
},
];
const toId = obj => obj.id;
const result = arr.map(toId).concat(arr.flatMap(o => o.options?.map(toId) ?? []));
console.log(result);
This generic approach will extract all id fields, without you needing to specify the structure (such as the options key):
const arr = [{"id":"1"},{"id":"2","options":[{"id":"2.1"}]}];
const f=o=>typeof o==='object'?Object.keys(o).flatMap(k=>k==='id'?[o[k]]:f(o[k])):[];
console.log(f(arr));

filter an array of object based on a key and get only key value

I want to filter an array of objects based on a key selected. If any of the object has selected: true, I want its ID in return. For e.g. below:
Below is the array I have:
arr = [
{
"_id": "6311eb86cc42295428bb663a",
"name": "Southeast Asia",
"language": [
"English"
],
"selected": true
},
{
"_id": "6311eb86cc42295428bb663f",
"name": "USA",
"language": [
"English"
],
},
{
"_id": "6311eb86cc42295428bb6635",
"name": "MENA",
"language": [
"English"
],
"selected": true
}
]
Logic used to get an _id from it:
arr.filter(item => {
if(item.selected) {
retrun item._id;
}
});
Expected output:
[{
_id: '6311eb86cc42295428bb663a'
}, {
_id: '6311eb86cc42295428bb6635'
}]
But I got the whole array of object in return instead of just _id.
How can I work around this to get only _id?
The array.filter method only filters the input array without changing it's content. The callback you're passing in your code is only expected to return true or false. To reshape your result you need to use filter along with array.map
const arr = [
{
"_id": "6311eb86cc42295428bb663a",
"name": "Southeast Asia",
"language": [
"English"
],
"selected": true
},
{
"_id": "6311eb86cc42295428bb663f",
"name": "USA",
"language": [
"English"
],
},
{
"_id": "6311eb86cc42295428bb6635",
"name": "MENA",
"language": [
"English"
],
"selected": true
}
];
const result = arr.filter(item => item.selected).map(item => ({_id: item._id}));
console.log(result);
const data = arr = [
{
"_id": "6311eb86cc42295428bb663a",
"name": "Southeast Asia",
"language": [
"English"
],
"selected": true
},
{
"_id": "6311eb86cc42295428bb663f",
"name": "USA",
"language": [
"English"
],
},
{
"_id": "6311eb86cc42295428bb6635",
"name": "MENA",
"language": [
"English"
],
"selected": true
}
]
let result = data.filter(d => d.selected).map(d => {
let obj ={}
obj['_id'] = d._id
return obj
})
console.log(result)
Array.filter returns an array based on your function.
So you have to save that variable like this:
let arr2= arr.filter(element => {
if(element.selected == true)
return (element)
});
You need to use both filtered and map functions
const filteredArr = arr.filter((element)=>{
return element.selected != null
})
const reducedArr = filteredArr.map((element) => {
return {_id:element._id}
})
You can use map as below :
let filtered = arr.filter(item => { if(item.selected) { return item._id;}}).map(item => ({_id: item._id}));;
expected output :
[ { _id: '6311eb86cc42295428bb663a'}, { _id: '6311eb86cc42295428bb6635'}]
filter + map == reduce
so this will give you your output:
arr.reduce((acc,val)=>{
if(val.selected){
acc.push({_id: val._id})
}
return acc
},[])
Or, most likely you actually need just array of values:
arr.reduce((acc,val)=>{
if(val.selected){
acc.push(val._id)
}
return acc
},[])

How do I set value of nested object to key of current object?

This array has the key to substitute with nested key of 'name'
const arr = ['status', 'user', ...] <-- This array contains key to be replaced with name
This is what my current response object is
[
{
"id": 11,
"location": "Mumbai",
"status": {
"name": "NEW"
},
"user": {
"name": "Rakesh"
}
}
]
How do I modify the above array of objects to this below
[
{
"id": 11,
"location": "Mumbai",
"status": "NEW",
"user": "Rakesh"
}
]
can try below code
const keys = ['status', 'user']
let arr = [
{
"id": 11,
"location": "Mumbai",
"status": {
"name": "NEW"
},
"user": {
"name": "Rakesh"
}
}
]
arr.map(a => keys.forEach(k => {
if(a[k] && a[k].name) a[k] = a[k].name
}));
console.log(arr);
I'd try this one:
const results = [
{
"id": 11,
"location": "Mumbai",
"status": {
"name": "NEW"
},
"user": {
"name": "Rakesh"
}
}, {
"id": 12,
"location": "Helsinki",
"status": {
"name": "NEW"
},
"user": {
"name": "Samuli"
}
}
];
const flattenObject = ([key, value]) => ((typeof value === 'object') ? {[key] : value[Object.keys(value)[0]]} : {[key]: value});
const reduceToSingleObject = (acc, b) => ({...acc, ...b});
const actualResults = results.map((result) => Object.entries(result).map(flattenObject).reduce(reduceToSingleObject));
console.log(actualResults);
Explanation:
flattenObject is a function to flatten structure of object inside object. This only takes the first prop (without knowing the name of the key). If you, for some reason, would need to flatten several key-values, then it'd need whole different kind of helper function to sort that out.
reduceToSingleObject is a function to put all the key-value pairs into a single object. This could have been done already in flattenObject function, but for the clarity, I separated it to a normal map - reduce pattern.
actualResults is the outcome where we go through all the entries of your original results.

How to remove all dupes from an array of objects?

// This is a large array of objects, e.g.:
let totalArray = [
{"id":"rec01dTDP9T4ZtHL4","fields":
{"user_id":170180717,"user_name":"abcdefg","event_id":516575,
}]
let uniqueArray = [];
let dupeArray = [];
let itemIndex = 0
totalArray.forEach(x => {
if(!uniqueArray.some(y => JSON.stringify(y.fields.user_id) === JSON.stringify(x.fields.user_id))){
uniqueArray.push(x)
} else(dupeArray.push(x))
})
node.warn(totalArray);
node.warn(uniqueArray);
node.warn(dupeArray);
return msg;
I'm successfully deduping the array to produce only unique values. Problem is, I need to remove both duplicates, e.g.: if there are 2 objects with the same user_id key, I want to remove both of the objects from the array, not just one.
One option is to iterate over the array and put the current object being iterated over at a user_id property on the object. If the property already exists there, reassign it to null instead. At the end, take the values of the object and remove the null values:
const totalArray = [{
"id": "rec01dTDP9T4ZtHL4",
"fields": {
"user_id": 170180717,
"user_name": "abcdefg",
"event_id": 516575,
}
}, {
"id": "rec01dTDP9T4ZtHL4",
"fields": {
"user_id": 170180717,
"user_name": "abcdefg",
"event_id": 516575,
}
}, {
"id": "unique",
"fields": {
"user_id": 1234,
"user_name": "abcdefg",
"event_id": 516575,
}
}];
const uniques = {};
for (const item of totalArray) {
const prop = item.fields.user_id;
uniques[prop] = uniques.hasOwnProperty(prop)
? null
: item;
}
const output = Object.values(uniques).filter(Boolean);
console.log(output);
You could first group the objects based on user_id. Then partition them based on group size (if a group only contains a single record it is unique). Then flatten the groups so it'll end up as a normal array of records and not an array of groups of records.
function groupBy(iterable, fn) {
const groups = new Map();
for (const item of iterable) {
const key = fn(item);
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(item);
}
return groups;
}
function partition(iterable, fn) {
const truthy = [], falsy = [];
for (const item of iterable) {
(fn(item) ? truthy : falsy).push(item);
}
return [truthy, falsy];
}
const totalArray = [{
"id": "rec01dTDP9T4ZtHL4",
"fields": {
"user_id": 170180717,
"user_name": "abcdefg",
"event_id": 516575,
}
}, {
"id": "rec01dTDP9T4ZtHL4",
"fields": {
"user_id": 170180717,
"user_name": "abcdefg",
"event_id": 516575,
}
}, {
"id": "unique",
"fields": {
"user_id": 1234,
"user_name": "abcdefg",
"event_id": 516575,
}
}];
const [uniqueArray, dupeArray] =
partition(
groupBy(totalArray, record => record.fields.user_id).values(),
group => group.length == 1
)
.map(groups => groups.flat(1));
console.log("uniqueArray =", uniqueArray);
console.log("dupeArray =", dupeArray);
You can use Array.reduce && Array.some like this:
let totalArray = [
{
id: "rec01dTDP9T4ZtHL4",
fields:
{
"user_id":170180717,
"user_name":"abcdefg",
"event_id":516575,
}
},
{
id:"rec01dTDP9T4ZtHL4",
fields:
{
"user_id":170180717,
"user_name":"abcdefg",
"event_id":516575,
}
},
{
id:"rec01dTDP9T4ZtHL4",
fields:
{
"user_id":1470107417,
"user_name":"abcdefg",
"event_id":516575,
}
},
{
id:"rec01dTDP9T4ZtHL4",
fields:
{
"user_id":1470107417,
"user_name":"abcdefg",
"event_id":516575,
}
},
{
id:"rec01dTDP9T4ZtHL4",
fields:
{
"user_id":14701073417,
"user_name":"abcdefg",
"event_id":516575,
}
}
];
let removeDups = totalArray.reduce((result,item,index,original)=>{
if (original.some((o,i)=>o.fields.user_id===item.fields.user_id && i!==index)) return result;
result.push(item);
return result;
},[])
console.log(removeDups);

Create a new collection from child attributes in nestled array

I have a structure similar to this (only test data, so typos are irrelevant):
"People" : [
{
"name": "Bob",
"animals" : [{
"name" : "Scott",
"type" : "Cat"
},
{
"name" : "Eva",
"type" : "Cat"
}
{
"name" : "Sven",
"type" : "Dog"
}]
},
{
"name": "Bob",
"animals" : [{
"name" : "Chris",
"type" : "Dog"
},
{
"name" : "Greg",
"type" : "Cat"
},
{
"name" : "Ior",
"type" : "Horse"
}]
}
]
Basically what I want to do is to get all unique animal types from this collection. My problem is mapping into two arrays (People : [], Animals : []). The question:
How do I .map all the "type" attributes to get a new unique collection? like:
["Cat", "Dog", "Horse"]
Will update if I get an example done using C# LINQ.
let _animals = {}
People.forEach(person => {
person.animals.forEach(animal => {
_animals[animal.name] = true
})
})
let result = Object.keys(_animals)
Enhanced with more es6:
let _animals = {}
People.forEach(({animals}) =>
animals.forEach(({name}) =>
_animals[name] = true
)
)
let result = Object.keys(animals)
var unique = {}; // Create an associative array
for (j = 0; j < People.length; j++) { // Iterate through Parents
for(i = 0; i < People[j].animals.length; i++) { // Iterate through animals
var node = unique[People[j].animals[i].type] = unique[People[j].animals[i].type] || {};
// Here unique is built. If there is no entry with the current type as the id, a new entry in unique will be added.
}
}
Use reduce, map, concat and finally to Set to Array as shown below
var output = [...new Set( arr.reduce( (a,c) => a.concat( c.animals.map( s => s.type )) , []))]
Explanation
Use reduce to iterate array (people) to get all values for animals type
Use map to iterate animals and return their types, and concatenate result of map to accumulator a
Use Set to remove duplicates.
Use spread operator ... to convert back to Array.
Demo
var arr = [{
"name": "Bob",
"animals": [{
"name": "Scott",
"type": "Cat"
},
{
"name": "Eva",
"type": "Cat"
},
{
"name": "Sven",
"type": "Dog"
}
]
},
{
"name": "Bob",
"animals": [{
"name": "Chris",
"type": "Dog"
},
{
"name": "Greg",
"type": "Cat"
},
{
"name": "Ior",
"type": "Horse"
}
]
}
];
var output = [...new Set(arr.reduce((a, c) => a.concat(c.animals.map(s => s.type)), []))];
console.log(output);
See Set and Array.prototype.forEach() for more info on animalsFrom().
See Array.prototype.map() and Flattening Arrays for more info on the latter method (listAnimals()).
// Input.
const input = {"people": [{"name": "Bob","animals": [{"name": "Scott","type": "Cat"},{"name" : "Eva","type": "Cat"},{"name" : "Sven","type": "Dog"}]},{"name": "Bob","animals": [{"name" : "Chris","type" : "Dog"},{"name": "Greg","type": "Cat"},{"name": "Ior","type": "Horse"}]}]}
// Animals From.
const animalsFrom = ({people}) => {
const animals = new Set()
people.forEach(person => {
person.animals.forEach(animal => animals.add(animal.type))
})
return [...animals.values()]
}
// List Animals (Advanced).
const listAnimals = ({people}) => [...new Set([].concat.apply([], people.map(({animals}) => animals.map(({type}) => type))))]
// Proof.
console.log('Animals From', animalsFrom(input))
console.log('List Animals', listAnimals(input))
ES6
You can use reduce(), new Set([]) and the spread operator to get the required result.
DEMO
const arr = [{
"name": "Bob",
"animals": [{
"name": "Scott",
"type": "Cat"
}, {
"name": "Eva",
"type": "Cat"
}, {
"name": "Sven",
"type": "Dog"
}]
}, {
"name": "Bob",
"animals": [{
"name": "Chris",
"type": "Dog"
}, {
"name": "Greg",
"type": "Cat"
}, {
"name": "Ior",
"type": "Horse"
}]
}];
let result = arr.reduce((r, {animals}) =>[...r,...animals.map(({type})=>type)], []);
console.log([ ...new Set(result)]);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories