首页 > 解决方案 > 如何仅将多维数组的一部分展平一层?

问题描述

我有一个复杂的 JSON 对象。我正在尝试处理它以创建一个如下所示的数组:

[
    [ "Name", "Spec", "Spec" ],
    [ "Name", "Spec", "Spec" ]
]

这是我卡住的地方:

let array = products.map((product) => {
    return [
        product.name,
        product.attributes
            .map((attribute) => (
                attribute.spec
            ))
            .reduce((accumulator, currentValue) => {
                return accumulator.concat(currentValue);
            }, [])
    ];
});

这给出了结果:

[
    [ "Name", [ "Spec", "Spec" ] ],
    [ "Name", [ "Spec", "Spec" ] ]
]

诚然,我并不完全理解这个reduce方法和它的initialValue论点。我知道使用该方法可以在顶层使用中展平数组map,但在更深层次上,似乎什么也没做。

我在网上搜索过,但只找到了涉及完全展平深度数组的答案。由于缺乏兼容性,该flatten()方法不是一种选择。

有人可以建议如何仅展平第二层吗?如果可能的话,我想通过改变数组来实现这一点。

标签: javascriptarraysjsonmultidimensional-arrayflatten

解决方案


1. 为什么会失败?

你把你reduce的放在错误的地方。您正在展平规格列表,这已经是一个平面阵列。您想要展平具有名称和规格列表的列表。这是一种可能性:

const array = products.map(prod => [
  prod.name,
  prod.attributes.map(attr => attr.spec)
].reduce((acc, curr) => acc.concat(curr), []));

2. 什么是更好的解决方案?

正如CertainPerformance 指出的那样,有一个更简单的版本,我可能会稍微不同地写成

const array = products.map(({name, attributes}) =>
  [name, ...attributes.map(attr => attr.spec)]
);

3. 如果需要flatten在其他地方重复使用怎么办?

从第一个解决方案中提取它作为可重用函数。这不是新 Arrayflatten方法的完全替代品,但它可能就是您所需要的:

const flatten = arr => arr.reduce((acc, curr) => acc.concat(curr), [])

const array = products.map(prod => flatten([
    prod.name,
    prod.attributes.map(attr => attr.spec)
  ])
)

4. 那调用如何reduce使一层变平?

我们可以认为[x, y, z].reduce(fn, initial)执行这些步骤

  1. 调用fn(initial, x),产生价值a
  2. 调用fn(a, y),产生价值b
  3. 调用fn(b, z),产生价值c
  4. 由于数组已用尽,返回值c

换句话说,[x, y, z].reduce(fn, initial)返回 fn(fn(fn(initial, x), y), z)

什么时候fn(acc, val) => acc.concat(val),那么我们可以认为是['name', ['spec1', 'spec2']].reduce(fn, [])fn(fn([], 'name'), ['spec1', 'spec2'])这与 相同([].concat('name')).concat(['spec1', 'spec2']),当然是['name', 'spec1', 'spec2']

5. 我的问题有什么问题吗?

我很高兴你问。:-)

有一个重大失败。您没有包含任何示例数据。为了帮助解决这个问题,需要尝试从代码中重建数据格式。举一个最小的例子就很容易了,例如:

const products = [
  {name: 'foo', attributes: [{spec: 'bar'}, {spec: 'baz'}]},
  {name: 'oof', attributes: [{spec: 'rab'}, {spec: 'zab'}]}
]

具有匹配的预期输出:

[
  ["foo", "bar", "baz"], 
  ["oof", "rab", "zab"]
]

6. 我的输出结构怎么样?

既然你提到它,这似乎是一个奇怪的结构。你可能有充分的理由,但这很奇怪。

数组在 Javascript 中通常有两个用途。它们要么是相同类型的任意长度的元素列表,要么是固定长度的列表,每个索引都有特定的类型(又名tuples。)

但是你的结构结合了这两者。它们是任意的(至少看起来如此)长度列表,其中第一个条目是名称,后续条目是规范。虽然这可能是有道理的,但您可能需要考虑这种结构是否特别有用。

7. 我怎样才能做到这一点而不发生变异?

如果可能的话,我想通过改变数组来实现这一点。

我拒绝参与这样的恐怖活动。

不过,说真的,不可变数据使编码变得更加容易。您将其列为要求是否有任何真正的原因?


推荐阅读