Suppose I got this array:
const users =[
{
id:1,
name:'bob',
},
{
id:2,
name:'sally',
},
{
id:3,
name:'bob',
age:30,
}
];
And I want to use any key(in this case 'name' ) to return an object :
{
bob:[
{
id:1,
name:'bob',
},
{
id:3,
name:'bob',
age:30,
}
],
sally:[
{
id:2,
name:'sally',
}
],
}
I tried this:
const go = (A,key) =>{
return A.reduce((o, key) => ({ ...o, [key]:o }), {})
}
export default go;
But this returns:
{ '[object Object]': { '[object Object]': { '[object Object]': {} } } }
If the key is not present omit from the result. It should not mutate the original array though. How can I perform this kind of conversion?
With the approach you have, a new array is not instantiated in case the key is not yet present in the object.
This will work:
const result = users.reduce((a, v) => {
a[v.name] = a[v.name] || [];
a[v.name].push(v);
return a;
}, {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => {
a[v[key]] = a[v[key]] || [];
a[v[key]].push(v);
return a;
}, {});
console.log(go(users, 'name'));
If you really want to cram it into a one-liner, this will also work, by either spreading the already existing array, or an empty one:
const result = users.reduce((a, v) => ({...a, [v.name]: [...a[v.name] || [], v]}), {});
Complete snippet wrapping this logic in a function:
const users = [{
id: 1,
name: 'bob',
}, {
id: 2,
name: 'sally',
}, {
id: 3,
name: 'bob',
age: 30,
}];
const go = (input, key) => input.reduce((a, v) => ({...a, [v[key]]: [...a[v[key]] || [], v]}), {});
console.log(go(users, 'name'));
You were close but the key attribute in this case was each value (eg: { id: 1, name: 'bob' }) so the string representation is [object Object] which is why all the keys are that. Based off what you said, you want to use key.name as the property and set it's value as [key]. (I renamed key to arr in my example since it's the array value).
So this would be something like { ...o, [arr.name]: [arr] }
Because there can be an existing value, it adds a bit of complexity which is what [...(obj[arr.name] || []), arr] is doing. It's looking up the existing value (or defaulting to an empty array) and spreading those values and adding the new value.
const users = [{
id: 1,
name: 'bob',
},
{
id: 2,
name: 'sally',
},
{
id: 3,
name: 'bob',
age: 30,
}
];
const transform = (input, keyName) => {
return input.reduce((obj, arr) => ({ ...obj,
[arr[keyName]]: [...(obj[arr[keyName]] || []), arr]
}), {})
}
console.log(transform(users, 'name'))
console.log(transform(users, 'id'))
Related
I have this array of objects, within it I have another array of objects:
[
{
id: 1,
country: [
{
id: "5a60626f1d41c80c8d3f8a85"
},
{
id: "5a6062661d41c80c8b2f0413"
}
]
},
{
id: 2,
country: [
{
id: "5a60626f1d41c80c8d3f8a83"
},
{
id: "5a60626f1d41c80c8d3f8a84"
}
]
}
];
How to get flat array of country like this:
[
{ id: "5a60626f1d41c80c8d3f8a85" },
{ id: "5a6062661d41c80c8b2f0413" },
{ id: "5a60626f1d41c80c8d3f8a83" },
{ id: "5a60626f1d41c80c8d3f8a84" }
];
without using a forEach and a temp variable?
When I did:
(data || []).map(o=>{
return o.country.map(o2=>({id: o2.id}))
})
I got the same structure back.
Latest edit
All modern JS environments now support Array.prototype.flat and Array.prototype.flatMap
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.flatMap(
(elem) => elem.country
)
)
Old answer
No need for any ES6 magic, you can just reduce the array by concatenating inner country arrays.
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => arr.concat(elem.country), []
)
)
If you want an ES6 feature (other than an arrow function), use array spread instead of the concat method:
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => [...arr, ...elem.country], []
)
)
Note: These suggestions would create a new array on each iteration.
For efficiency, you have to sacrifice some elegance:
const data=[{id:1,country:[{id:"5a60626f1d41c80c8d3f8a85"},{id:"5a6062661d41c80c8b2f0413"}]},{id:2,country:[{id:"5a60626f1d41c80c8d3f8a83"},{id:"5a60626f1d41c80c8d3f8a84"}]}];
console.log(
data.reduce(
(arr, elem) => {
for (const c of elem.country) {
arr.push(c);
}
return arr;
}, []
)
)
const raw = [
{
id: 1,
country: [
{
id: "5a60626f1d41c80c8d3f8a85"
},
{
id: "5a6062661d41c80c8b2f0413"
}
]
},
{
id: 2,
country: [
{
id: "5a60626f1d41c80c8d3f8a83"
},
{
id: "5a60626f1d41c80c8d3f8a84"
}
]
}
];
const countryIds = raw
.map(x => x.country)
.reduce((acc, curr) => {
return [
...acc,
...curr.map(x => x.id)
];
}, []);
console.log(countryIds)
This, works, just concat the nested arrays returned by your solution
let arr = [{ "id": 1,
"country": [{
"id": "5a60626f1d41c80c8d3f8a85",
},
{
"id": "5a6062661d41c80c8b2f0413",
}
]
},
{
"id": 2,
"country": [{
"id": "5a60626f1d41c80c8d3f8a83",
},
{
"id": "5a60626f1d41c80c8d3f8a84",
}
]
}
];
//If you want an array of country objects
console.log([].concat.apply(...(arr || []).map(o=> o.country)))
//If you can an array od country ids
console.log([].concat.apply(...(arr || []).map(o=> o.country.map(country => country.id))))
Ayush Gupta's solution will work for this case. But I would like to provide other solution.
const arr = [
{
id: 1,
country: [
{
id: '5a60626f1d41c80c8d3f8a85'
},
{
id: '5a6062661d41c80c8b2f0413'
}
]
},
{
id: 2,
country: [
{
id: '5a60626f1d41c80c8d3f8a83'
},
{
id: '5a60626f1d41c80c8d3f8a84'
}
]
}
];
const ids = arr.reduce(
(acc, {country}) => [
...acc,
...country.map(({id}) => ({
id
}))
],
[]
);
console.log(ids);
For JSON string data, it can be done during parsing too :
var ids = [], json = '[{"id":1,"country":[{"id":"5a60626f1d41c80c8d3f8a85"},{"id":"5a6062661d41c80c8b2f0413"}]},{"id":2,"country":[{"id":"5a60626f1d41c80c8d3f8a83"},{"id":"5a60626f1d41c80c8d3f8a84"}]}]';
JSON.parse(json, (k, v) => v.big && ids.push(v));
console.log( ids );
I am not sure why noone mentioned flat() (probably for large arrays, it might be less performant)
(data || []).map(o=>{
return o.country.map(o2=>({id: o2.id}))
}).flat()
I have an array of arrays below. With ES6, how can I get a count of each value Good, Excellent & Wow into a new array e.g [{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}] in dynamic style. I am attempting to use Object.assign but I am failing to "unique" out the count of the key plus instead, I need to use an array as I am trying to render this out on the front end. Do I need to use reduce? how?
let k = 0
const stats = {}
const remarks = [
[{name: "Good"}],
[{name: "Good"}, {name: "Excellent"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Excellent"}],
[{name: "Excellent"}]
]
remarks.forEach((arr) => {
arr.map((e) => {
Object.assign(stats, { [e.name]: k = k + 1 })
})
})
console.log(stats);
Output:
stats: {Good: 8, Excellent: 11, Wow: 9}
Which is Incorrect plus I need to use an array.
Expected output:
[{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}]
Flatten the array of arrays and reduce it starting with an object like : { Good: 0, Excellent: 0, Wow: 0}
then .map the Object.entries of the result to transform it to an array :
const remarks = [
[{ name: "Good" }],
[{ name: "Good" }, { name: "Excellent" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Excellent" }],
[{ name: "Excellent" }]
];
const result = Object.entries(
remarks.flat().reduce(
(all, { name }) => {
all[name] += 1;
return all;
},
{ Good: 0, Excellent: 0, Wow: 0 }
)
).map(([name, count]) => ({ name, count }));
console.log(result);
You can try below logic:
var data = [[{name: "Good"}],[{name: "Good"}, {name:"Excellent"}],[{name: "Good"}, {name:"Excellent"}, {name:"Wow"}],[{name: "Good"}, {name:"Excellent"}, {name:"Wow"}],[{name:"Excellent"}],[{name:"Excellent"}]]
var nData = [];
(data || []).forEach( e => {
(e || []).forEach(ei => {
var i = (index = nData.findIndex(d => d.name === ei.name)) >=0 ? index : nData.length;
nData[i] = {
name: ei.name,
count : (nData[i] && nData[i].count ? nData[i].count : 0)+1
}
});
});
console.log(nData);
Hope this helps!
You can use reduce, then convert the result into an array of objects:
const counts = remarks.reduce((result, list) => {
list.forEach(remark => {
result[remark.name] = (result[remark.name] || 0) + 1;
});
}, {});
const finalResult = [];
for (let name in counts) {
finalResult.push({name, count: counts[name]});
}
You could achieve this pretty easily by:
1) Flattening the nested array into 1 single level array.
2) Iterating over the flat array and create a "count map" by using Array.prototype.reduce
For example:
const remarks = [
[{
name: 'Good'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}, {
name: 'Wow'
}],
[{
name: 'Good'
}, {
name: 'Excellent'
}, {
name: 'Wow'
}],
[{
name: 'Excellent'
}],
[{
name: 'Excellent'
}]
]
const flatten = arr => arr.reduce((accum, el) => accum.concat(el), [])
const map = flatten(remarks).reduce((accum, el) => {
if (accum[el.name]) {
accum[el.name] += 1;
} else {
accum[el.name] = 1;
}
return accum;
}, {});
console.log(map)
First find the counts using reduce than pass that to another function to get the desired view structure:
const Good = 1,
Excellent = 2,
Wow = 3;
const remarks = [
[{name: Good}],
[{name: Good}, {name:Excellent}],
[{name: Good}, {name:Excellent}, {name:Wow}],
[{name: Good}, {name:Excellent}, {name:Wow}],
[{name:Excellent}],
[{name:Excellent}]
];
/*
[{name: Good, count: 4} {name: Excellent, count: 5}, {name:Wow, count:2}]
*/
function counts(remarks) {
return remarks.flat().reduce((acc, v) => {
const name = v.name;
let count = acc[name] || 0;
return {
...acc,
[name]: count + 1
}
}, {});
}
function view(counts) {
return Object.keys(counts).map(key => {
let count = counts[key];
return { name: key, count };
})
}
console.log(view(counts(remarks)));
Any time you are making a smaller set of data, or transforming data, in JavaScript reduce should be the first method you attempt to use. In this case, you may want to pair it with an indexer (hence preloading with an array of index and an array of result).
This works in one pass without needing to know the name values up front.
const remarks = [
[{name: "Good"}],
[{name: "Good"}, {name: "Excellent"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Good"}, {name: "Excellent"}, {name: "Wow"}],
[{name: "Excellent"}],
[{name: "Excellent"}]
];
const stats = remarks.reduce((p,c) => (
c.forEach( ({name}) => {
if(!p[0].hasOwnProperty(name)){
p[1].push({name:name,count:0});
p[0][name] = p[1].length - 1;
}
p[1][p[0][name]].count++;
}),p),[{},[]])[1];
console.log(stats);
A slightly more concise and definitely less readable approach (but it's worth to mention) could be:
const remarks = [
[{ name: "Good" }],
[{ name: "Good" }, { name: "Excellent" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Good" }, { name: "Excellent" }, { name: "Wow" }],
[{ name: "Excellent" }],
[{ name: "Excellent" }]
];
const stats = Object.entries(
remarks
.flat()
.reduce((acc, {name}) => (acc[name] = -~acc[name], acc), {})))
).map(([name, count]) => ({ name, count }));
console.log(stats);
It uses the comma operator in the reducer to returns the accumulator; and the bitwise operator NOT to create a counter without the needs to initialize the object upfront with all the names.
const flattenedRemarks = _.flatten(remarks);
const groupedRemarks = _.groupBy(flattenedRemarks, (remark) => remark.name);
const remarkCounts = _.mapValues(groupedRemarks, (group) => group.length);
const data = {
"mchale": {
"classes":["ESJ030", "SCI339"], // get the length
"faculty":["Hardy", "Vikrum"] // get the length
},
"lawerence":{
"classes":["ENG001"], // get the length
"faculty":["Speedman", "Lee", "Lazenhower"] // get the length
}
};
const count = Object.keys(data).map(campusName => {
const campus = data[campusName];
return Object.keys(campus).map(key => campus[key].length).reduce((p, c) => p + c, 0);
}).reduce((p, c) => p + c, 0);
console.log(count);
I am new to Javascript.
I need to write a function to covert an array of objects to an object with a given key.
The input is like this
convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id')
and output needs to be like this
{
'1': {id: 1, value: 'abc'},
'2': {id: 2, value: 'xyz'}
}
I have tried the below code.
When I am trying this directly in console it seems it is working.
var arr = [{ id: 1, name: 'John', role: 'Developer'},
{ id: 2, name: 'Jane', role: 'Lead'},
{ id: 3, name: 'Robbie', role: 'QA'}];
let res = arr.reduce((prev, current) => {
prev[current.v] = current;
return prev;
}, {})
console.log(res)
But, when I am trying do it from the function it is not working.
function f(k, v) {
//console.log(k);
k.reduce((prev, current) => {
prev[current.v] = current;
return prev;
console.log(prev)
}, {})
}
f(arr, 'role');
Any help will be highly appreciated.
You could take a dunctional approach by mapping an assigning new object.
function convert(array, key) {
return Object.assign(...array.map(o => ({ [o[key]]: o })));
}
console.log(convert([{ id: 1, value: 'abc' }, { id: 2, value: 'xyz' }], 'id'))
This solution works for me:
function convert(obj, key) {
var newObj = {};
obj.forEach(element => {
newObj[element[key]] = element;
});
return newObj;
}
var newObj = convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id');
console.log(newObj);
Its simple. Why complicating by reduce and etc.,
function convert(arr, key) {
output = {};
arr.forEach(function(item) {
output[item[key]] = item;
})
console.log(output)
return output
}
convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id')
https://jsfiddle.net/cvydtL7p/
You're close, but you need nested bracket notation to get to the proper key name, eg
prev[current[v]]
or
a[item[keyName]] // as in code below
const convert = (arr, keyName) => arr.reduce((a, item) => {
a[item[keyName]] = item;
return a;
}, {});
console.log(
convert([{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}], 'id')
);
Your code is pretty much working, the only errors are to use bracket notation to access a variable key; for example:
obj[v] will evaluate to obj.id if v was id
The other error is that you are simply missing to return from your function, resulting in an undefined result
var arr = [{ id: 1, name: 'John', role: 'Developer'},
{ id: 2, name: 'Jane', role: 'Lead'},
{ id: 3, name: 'Robbie', role: 'QA'}];
function f(k, v) {
//console.log(k);
return k.reduce((prev, current) => {
prev[current[v]] = current;
return prev;
}, {})
}
console.log(f(arr, 'role'));
Also note that nothing after return will happen, so the console.log line in your reducer should be before that, otherwise it's ignored.
You can use reduce and spread like this:
var arr = [{id: 1, value: 'abc'}, {id: 2, value: 'xyz'}];
const res = arr.reduce((total, curr) => {
return {...total, [curr.id]: curr };
}, {});
console.log(res);
refs:
reduce
Spread syntax
How would I find all values by specific key in a deep nested object?
For example, if I have an object like this:
const myObj = {
id: 1,
children: [
{
id: 2,
children: [
{
id: 3
}
]
},
{
id: 4,
children: [
{
id: 5,
children: [
{
id: 6,
children: [
{
id: 7,
}
]
}
]
}
]
},
]
}
How would I get an array of all values throughout all nests of this obj by the key of id.
Note: children is a consistent name, and id's won't exist outside of a children object.
So from the obj, I would like to produce an array like this:
const idArray = [1, 2, 3, 4, 5, 6, 7]
This is a bit late but for anyone else finding this, here is a clean, generic recursive function:
function findAllByKey(obj, keyToFind) {
return Object.entries(obj)
.reduce((acc, [key, value]) => (key === keyToFind)
? acc.concat(value)
: (typeof value === 'object')
? acc.concat(findAllByKey(value, keyToFind))
: acc
, [])
}
// USAGE
findAllByKey(myObj, 'id')
You could make a recursive function like this:
idArray = []
function func(obj) {
idArray.push(obj.id)
if (!obj.children) {
return
}
obj.children.forEach(child => func(child))
}
Snippet for your sample:
const myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
}
idArray = []
function func(obj) {
idArray.push(obj.id)
if (!obj.children) {
return
}
obj.children.forEach(child => func(child))
}
func(myObj)
console.log(idArray)
I found steve's answer to be most suited for my needs in extrapolating this out and creating a general recursive function. That said, I encountered issues when dealing with nulls and undefined values, so I extended the condition to accommodate for this. This approach uses:
Array.reduce() - It uses an accumulator function which appends the value's onto the result array. It also splits each object into it's key:value pair which allows you to take the following steps:
Have you've found the key? If so, add it to the array;
If not, have I found an object with values? If so, the key is possibly within there. Keep digging by calling the function on this object and append the result onto the result array; and
Finally, if this is not an object, return the result array unchanged.
Hope it helps!
const myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
}
function findAllByKey(obj, keyToFind) {
return Object.entries(obj)
.reduce((acc, [key, value]) => (key === keyToFind)
? acc.concat(value)
: (typeof value === 'object' && value)
? acc.concat(findAllByKey(value, keyToFind))
: acc
, []) || [];
}
const ids = findAllByKey(myObj, 'id');
console.log(ids)
You can make a generic recursive function that works with any property and any object.
This uses Object.entries(), Object.keys(), Array.reduce(), Array.isArray(), Array.map() and Array.flat().
The stopping condition is when the object passed in is empty:
const myObj = {
id: 1,
anyProp: [{
id: 2,
thing: { a: 1, id: 10 },
children: [{ id: 3 }]
}, {
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{ id: 7 }]
}]
}]
}]
};
const getValues = prop => obj => {
if (!Object.keys(obj).length) { return []; }
return Object.entries(obj).reduce((acc, [key, val]) => {
if (key === prop) {
acc.push(val);
} else {
acc.push(Array.isArray(val) ? val.map(getIds).flat() : getIds(val));
}
return acc.flat();
}, []);
}
const getIds = getValues('id');
console.log(getIds(myObj));
Note: children is a consistent name, and id's wont exist outside
of a children object.
So from the obj, I would like to produce an array like this:
const idArray = [1, 2, 3, 4, 5, 6, 7]
Given that the question does not contain any restrictions on how the output is derived from the input and that the input is consistent, where the value of property "id" is a digit and id property is defined only within "children" property, save for case of the first "id" in the object, the input JavaScript plain object can be converted to a JSON string using JSON.stringify(), RegExp /"id":\d+/g matches the "id" property and one or more digit characters following the property name, which is then mapped to .match() the digit portion of the previous match using Regexp \d+ and convert the array value to a JavaScript number using addition operator +
const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]};
let res = JSON.stringify(myObject).match(/"id":\d+/g).map(m => +m.match(/\d+/));
console.log(res);
JSON.stringify() replacer function can alternatively be used to .push() the value of every "id" property name within the object to an array
const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]};
const getPropValues = (o, prop) =>
(res => (JSON.stringify(o, (key, value) =>
(key === prop && res.push(value), value)), res))([]);
let res = getPropValues(myObject, "id");
console.log(res);
Since the property values of the input to be matched are digits, all the JavaScript object can be converted to a string and RegExp \D can be used to replace all characters that are not digits, spread resulting string to array, and .map() digits to JavaScript numbers
let res = [...JSON.stringify(myObj).replace(/\D/g,"")].map(Number)
Using recursion.
const myObj = { id: 1, children: [ { id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7, } ] } ] } ] }, ]},
loop = (array, key, obj) => {
if (!obj.children) return;
obj.children.forEach(c => {
if (c[key]) array.push(c[key]); // is not present, skip!
loop(array, key, c);
});
},
arr = myObj["id"] ? [myObj["id"]] : [];
loop(arr, "id", myObj);
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can make a recursive function with Object.entries like so:
const myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
};
function findIds(obj) {
const entries = Object.entries(obj);
let result = entries.map(e => {
if (e[0] == "children") {
return e[1].map(child => findIds(child));
} else {
return e[1];
}
});
function flatten(arr, flat = []) {
for (let i = 0, length = arr.length; i < length; i++) {
const value = arr[i];
if (Array.isArray(value)) {
flatten(value, flat);
} else {
flat.push(value);
}
}
return flat;
}
return flatten(result);
}
var ids = findIds(myObj);
console.log(ids);
Flattening function from this answer
ES5 syntax:
var myObj = {
id: 1,
children: [{
id: 2,
children: [{
id: 3
}]
},
{
id: 4,
children: [{
id: 5,
children: [{
id: 6,
children: [{
id: 7,
}]
}]
}]
},
]
};
function findIds(obj) {
const entries = Object.entries(obj);
let result = entries.map(function(e) {
if (e[0] == "children") {
return e[1].map(function(child) {
return findIds(child)
});
} else {
return e[1];
}
});
function flatten(arr, flat = []) {
for (let i = 0, length = arr.length; i < length; i++) {
const value = arr[i];
if (Array.isArray(value)) {
flatten(value, flat);
} else {
flat.push(value);
}
}
return flat;
}
return flatten(result);
}
var ids = findIds(myObj);
console.log(ids);
let str = JSON.stringify(myObj);
let array = str.match(/\d+/g).map(v => v * 1);
console.log(array); // [1, 2, 3, 4, 5, 6, 7]
We use object-scan for a lot of our data processing needs now. It makes the code much more maintainable, but does take a moment to wrap your head around. Here is how you could use it to answer your question
// const objectScan = require('object-scan');
const find = (data, needle) => objectScan([needle], { rtn: 'value' })(data);
const myObj = { id: 1, children: [{ id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7 } ] } ] } ] }] };
console.log(find(myObj, '**.id'));
// => [ 7, 6, 5, 4, 3, 2, 1 ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
import {flattenDeep} from 'lodash';
/**
* Extracts all values from an object (also nested objects)
* into a single array
*
* #param obj
* #returns
*
* #example
* const test = {
* alpha: 'foo',
* beta: {
* gamma: 'bar',
* lambda: 'baz'
* }
* }
*
* objectFlatten(test) // ['foo', 'bar', 'baz']
*/
export function objectFlatten(obj: {}) {
const result = [];
for (const prop in obj) {
const value = obj[prop];
if (typeof value === 'object') {
result.push(objectFlatten(value));
} else {
result.push(value);
}
}
return flattenDeep(result);
}
Below solution is generic which will return all values by matching nested keys as well e.g for below json object
{
"a":1,
"b":{
"a":{
"a":"red"
}
},
"c":{
"d":2
}
}
to find all values matching key "a" output should be return
[1,{a:"red"},"red"]
const findkey = (obj, key) => {
let arr = [];
if (isPrimitive(obj)) return obj;
for (let [k, val] of Object.entries(obj)) {
if (k === key) arr.push(val);
if (!isPrimitive(val)) arr = [...arr, ...findkey(val, key)];
}
return arr;
};
const isPrimitive = (val) => {
return val !== Object(val);
};
I want to use array reduce to group items in my array by a key.
When I use the groupByIDErr function below I get obj[item[key]] is not iterable so to get it working I use the groupByID function which had an undefined check as shown below.
Does array reduce have a better way to handle this? What I have done here works ok for the example I have provided but I find it harder to maintain when I have to reduce a more complex array.
function groupByIDErr(arr,key){
return arr.reduce((obj, item) => {
return {
...obj,
[item[key]]: [
...obj[item[key]],
item
]
}
}, {})
}
function groupByID(arr,key){
return arr.reduce((obj, item) => {
return {
...obj,
[item[key]]: obj[item[key]] !== undefined ? [
...obj[item[key]],
item
]:[item]
}
}, {})
}
const people = [
{gender:'male', name: 'Tom'},
{gender:'male', name: 'Richard'},
{gender:'male', name: 'Harry'},
{gender:'female', name: 'Sarah'},
{gender:'female', name: 'Kate'}
]
console.log(groupByID(people,'gender'))
You can use obj[item[key]] || [] in order to make it use an empty array if obj[item[key]] is undefined (since undefined is falsy).
function groupByID(arr, key) {
return arr.reduce((obj, item) => {
return {
...obj,
[item[key]]: [
...obj[item[key]] || [],
item
]
};
}, {});
}
const people = [
{ gender: 'male', name: 'Tom' },
{ gender: 'male', name: 'Richard' },
{ gender: 'male', name: 'Harry' },
{ gender: 'female', name: 'Sarah' },
{ gender: 'female', name: 'Kate' }
];
console.log(groupByID(people, 'gender'));
.as-console-wrapper { min-height: 100%; }
You need a recursion function to go through the depth of an array first and then return object value.
function recurCheck(your array,key){
//if array inside array
recurCheck(newArray,key)
//else
add your reduce function here and return
}