'flatten nested object with topairs in lodash
I have an object like this:
let order={
"john":{
"hamburger":4
},
"alice":{
"pizza":10,
"milkshake":2
},
"bob":{
}
}
that I would like to flatten like this:
let res=[
["john","hamburger",4],
["alice","pizza",4],
["alice","milkshake",4]
]
I know how to do it in reverse, using keyBy but how can I turn it from a recursive object to a recursive array? I tried using toPairs, but I can't figure out how to make it recursive
Solution 1:[1]
Use _.toPairs() to get an array of [name, items] pairs, which you iterate with _.flatMap(). Get the pairs from the items object, and use _.map() to create the arrays that include the info:
const { flatMap, toPairs, map } = _
const order = {"john":{"hamburger":4},"alice":{"pizza":10,"milkshake":2},"bob":{}}
const result = flatMap(
toPairs(order),
([name, items]) =>
map(
toPairs(items),
([product, price]) => [name, product, price]
)
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
In vanilla JS you can use Array.flatMap() with Object.entries() to get the same result:
const order = {"john":{"hamburger":4},"alice":{"pizza":10,"milkshake":2},"bob":{}}
const result = Object
.entries(order)
.flatMap(([name, items]) =>
Object.entries(items)
.map(([product, price]) => [name, product, price])
)
console.log(result)
Solution 2:[2]
This solution will work recursively and let you supply a callback that determines how to construct the accumulator. Note that it only works with objects, not a mix of nested objects and arrays.
const flatten = (tree, acc, branch = [], build) =>
Object.entries(tree).reduce(
(a, [key, val]) =>
typeof val !== 'object'
? build(a, branch, key, val)
: flatten(val, a, branch.concat(key), build),
acc
);
In your case you want the branch to be an array of nodes with the final leaf also being in the same array.
const source = {
a: {
b: {
c: 'c'
}
},
b: {
d: {
x: 'sdfdsgsf'
},
c: {
i: 'sdf',
f: {
one: {
two: {
three: 'deep'
}
}
}
}
}
};
const flatten = (tree, acc, branch = [], build) =>
Object.entries(tree).reduce(
(a, [key, val]) =>
typeof val !== 'object'
? build(a, branch, key, val)
: flatten(val, a, branch.concat(key), build),
acc
);
console.log(
flatten(source, [], [], (acc, branch, key, val) => [
...acc,
branch.concat(key, val)
])
);
output
[
[ 'a', 'b', 'c', 'c' ],
[ 'b', 'd', 'x', 'sdfdsgsf' ],
[ 'b', 'c', 'i', 'sdf' ],
[
'b', 'c',
'f', 'one',
'two', 'three',
'deep'
]
]
I need to concat the branch nodes into a string such as
{
'a.b.c.d': 'string',
'x.y.z': 'foo'
}
So I used
flatten(source, {}, [], (acc, branch, key, val) => ({
...acc,
[branch.concat(key).join('.')]: val
}))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ori Drori |
| Solution 2 | Daniel Lizik |
