Reputation: 3424
I have the following code and test data:
const getNestedObject = (nestedObj, pathArr) => {
return pathArr.reduce((obj, key) => {
return (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj;
});
}
const obj =
[
{
a: 1,
c: [
{
d: 1,
e: 'string',
f: [
{
value: 0,
},
{
value: 1,
}
],
},
],
},
{
a: 2,
c: [
{
d: 2,
e: 'string',
f: [
{
value: 3,
},
{
value: 4,
}
],
},
],
},
];
console.log(obj);
const fs = obj.map(o => getNestedObject(o, ['c', 'f']));
console.log(fs);
What I want to do is given the array of objects shown below, I want to get only the property called f
from every object in the array. So, basically end result should be array of f
values of every object. Since 'f' is an array, I would highly appreciate the end result to be just one array with elements from all 'f' properties, so kind of every of these 'f' to be spread out, so I have one array. My above getNestedObject
function does not seem to work, as when the console.log
statement below returns the whole object. Any ideas how to do this in JS?
So basically the end result should be:
[{ value: 0 }, { value: 1 }, { value: 3 }, {value: 4 }]
Upvotes: 4
Views: 250
Reputation: 28970
You can recursively traverse any objects and arrays to fetch a given property. This works at any depth and doesn't care about the structure of the objects:
const obj=[{a:1,c:[{d:1,e:"string",f:[{value:0},{value:1}]}]},{a:2,c:[{d:2,e:"string",f:[{value:3},{value:4}]}]}];
//curried function to grab a property by name off some object or array
function grab(prop) {
//naming the inner function means it can be called recursively by name
return function recursiveGrab(target) {
if (Array.isArray(target)) {
const arrayResult = target
.filter(x => typeof x === "object") //remove non-objects (and non-arrays)
.filter(Boolean) //remove null
.map(recursiveGrab); //recursively call for the remaining objects
return flatten(arrayResult); //return a single dimensional array
}
//an object has the property - return it
if (prop in target) {
return target[prop];
}
//object doesn't have the property - check all values
return recursiveGrab(Object.values(target));
}
}
//small helper function. It's separated only to keep the logic for the traversal clear
function flatten(arr) {
return arr.reduce((acc, curr) => acc.concat(curr), [])
}
const grabF = grab('f');
console.log(grabF(obj));
Upvotes: 1
Reputation: 92440
You can combine reduce()
with map()
. Basically reduce your main array into an flattened array of all the c.f
items. This checks for the c
property just in case the object doesn't have it:
const obj = [{a: 1,c: [{d: 1,e: 'string',f: [{value: 0,},{value: 1,}],},],},{a: 2,c: [{d: 2,e: 'string',f: [{value: 3,},{value: 4,}],},],},];
let Fs = obj.reduce((arr, item) =>
item.c
? arr.concat(...item.c.map(itemc => itemc.f )) // concat for flattened rather than nested arrays
: arr
, []);
console.log(Fs)
Upvotes: 4
Reputation: 56875
Here's a fast iterative solution that won't overflow the stack, makes no assumptions about target result values being arrays (only spreads if they are) and doesn't hard-code child key names (it'll explore any values that are arrays).
This can also work if the target has children matching the key that you'd like to include in the search (swap else if
with if
).
const get = (data, target) => {
const result = [];
const stack = [data];
while (stack.length) {
const curr = stack.pop();
for (const o of curr) {
for (const k in o) {
if (k === target) {
if (Array.isArray(o[k])) {
result.push(...o[k]);
}
else {
result.push(o[k]);
}
}
else if (Array.isArray(o[k])) {
stack.push(o[k]);
}
}
}
}
return result;
};
const obj =
[
{
a: 1,
c: [
{
d: 1,
e: 'string',
f: [
{
value: 0,
},
{
value: 1,
}
],
},
],
},
{
a: 2,
c: [
{
d: 2,
e: 'string',
f: [
{
value: 3,
},
{
value: 4,
}
],
},
],
},
];
console.log(get(obj, "f"));
Upvotes: 1
Reputation: 4636
I did not notice that f
was always inside c
. I have this recursive and dirty looking solution that works with f
being inside any of the fields
const objArray = [
{
a: 1,
c: [
{
d: 1,
e: 'string',
f: [
{
value: 0,
},
{
value: 1,
}
],
},
],
d: [
{
d: 1,
e: 'string',
f: [
{
value: 'd',
},
{
value: 'd1',
}
],
},
],
},
{
a: 2,
c: [
{
d: 2,
e: 'string',
f: [
{
value: 3,
},
{
value: 4,
}
],
},
],
e: [
{
d: 1,
e: 'string',
f: [
{
value: 'e',
},
{
value: 'e1',
}
],
},
],
}
]
const getFObject = (obj) => {
let fObj = [];
Object.keys(obj).some(key => {
if (key === 'f') {
fObj = obj[key];
return true;
}
if (Array.isArray(obj[key])) {
obj[key].forEach(nestedObj => {
fObj = fObj.concat(getFObject(nestedObj))
});
}
return false;
});
return fObj;
}
const newArray = objArray.reduce((acc, obj) => {
return acc.concat(getFObject(obj))
}, []);
console.log(newArray)
Upvotes: 0