I have a complex JSON object. I'm trying to process it to create an array that looks like:
[
[ "Name", "Spec", "Spec" ],
[ "Name", "Spec", "Spec" ]
]
This is where I am stuck:
let array = products.map((product) => {
return [
product.name,
product.attributes
.map((attribute) => (
attribute.spec
))
.reduce((accumulator, currentValue) => {
return accumulator.concat(currentValue);
}, [])
];
});
That gives the result:
[
[ "Name", [ "Spec", "Spec" ] ],
[ "Name", [ "Spec", "Spec" ] ]
]
Admittedly, I don't entirely understand the reduce
method and it's initialValue
argument here. I know using that method can flatten the array at the top level use of map
, but at the deeper level, seems to do nothing.
I've searched online but have only found answers that involve completely flattening deep arrays. And the flatten()
method is not an option due to lack of compatibility.
Can someone please advise on how to flatten the second level only? If possible, I'd like to accomplish this through mutating the array.
You don't need the reducer there - it's only making things unnecessarily complicated. Map the attributes
to their spec
property, and then use spread:
const array = products.map(({ name, attributes }) => {
const specs = attributes.map(attribute => attribute.spec);
return [name, ...specs];
});
You put your reduce
in the wrong place. You're flattening the list of specs, which was already a flat array. You want to flatten the list that has the name and the list of specs. Here is one possibility:
const array = products.map(prod => [
prod.name,
prod.attributes.map(attr => attr.spec)
].reduce((acc, curr) => acc.concat(curr), []));
As CertainPerformance points out, there is a simpler version, which I might write slightly differently as
const array = products.map(({name, attributes}) =>
[name, ...attributes.map(attr => attr.spec)]
);
flatten
in other places? Extract it from the first solution as a reusable function. This is not a full replacement for the new Array flatten
method, but it might be all you need:
const flatten = arr => arr.reduce((acc, curr) => acc.concat(curr), [])
const array = products.map(prod => flatten([
prod.name,
prod.attributes.map(attr => attr.spec)
])
)
reduce
call flatten one level? We can think of [x, y, z].reduce(fn, initial)
as performing these steps
fn(initial, x)
, yielding value a
fn(a, y)
, yielding value b
fn(b, z)
, yielding value c
c
In other words [x, y, z].reduce(fn, initial)
returns fn(fn(fn(initial, x), y), z)
.
When fn
is (acc, val) => acc.concat(val)
, then we can think of ['name', ['spec1', 'spec2']].reduce(fn, [])
as fn(fn([], 'name'), ['spec1', 'spec2'])
, which is the same as ([].concat('name')).concat(['spec1', 'spec2'])
, which, of course is ['name', 'spec1', 'spec2']
.
I'm glad you asked. :-)
There was one significant failing. You didn't include any sample data. To help with this problem required one to try to reconstruct your data formats from your code. It would have been easy enough to give a minimal example such as:
const products = [
{name: 'foo', attributes: [{spec: 'bar'}, {spec: 'baz'}]},
{name: 'oof', attributes: [{spec: 'rab'}, {spec: 'zab'}]}
]
with a matching expected output:
[
["foo", "bar", "baz"],
["oof", "rab", "zab"]
]
Now that you mention it, this seems a strange structure. You might have good reasons for it, but it is odd.
Arrays generally serve two purposes in Javascript. They are either arbitrary-length lists of elements of the same type or they are fixed length lists, with specific types at each index (aka tuples .)
But your structure combines both of these. They are arbitrary- (at least so it seems) length lists, where the first entry is a name, and subsequent ones are specs. While there might be justification for this, you might want to think about whether this structure is particularly useful.
If possible, I'd like to accomplish this through mutating the array.
I refuse to take part in such horrors.
Seriously, though, immutable data makes for so much easier coding. Is there any real reason you listed that as a requirement?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.