如何仅将多维数组的一部分展平一个级别?

How do I flatten part of a multidimensional array one level only?

我有一个复杂的 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() 方法不是一个选项。

有人可以请教如何仅展平第二层吗?如果可能的话,我想通过改变数组来完成这个。

那里不需要减速器 - 它只会让事情不必要地复杂化。将 attributes 映射到它们的 spec 属性,然后使用 spread:

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

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怎么办?

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

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. 由于数组用完,return值c

也就是说[x, y, z].reduce(fn, initial) returns 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 中通常有两个用途。它们要么是相同类型元素的任意长度列表,要么是固定长度列表,每个索引都有特定类型(又名元组。)

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

7。我怎样才能在不改变的情况下做到这一点?

If possible, I'd like to accomplish this through mutating the array.

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

说真的,不可变的数据使编码变得更加容易。您将其列为要求有什么真正的原因吗?