如何仅将多维数组的一部分展平一个级别?
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)
执行这些步骤
- 调用
fn(initial, x)
,产生值a
- 调用
fn(a, y)
,产生值b
- 调用
fn(b, z)
,产生值c
- 由于数组用完,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.
我拒绝参与这样的恐怖活动。
说真的,不可变的数据使编码变得更加容易。您将其列为要求有什么真正的原因吗?
我有一个复杂的 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)
执行这些步骤
- 调用
fn(initial, x)
,产生值a
- 调用
fn(a, y)
,产生值b
- 调用
fn(b, z)
,产生值c
- 由于数组用完,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.
我拒绝参与这样的恐怖活动。
说真的,不可变的数据使编码变得更加容易。您将其列为要求有什么真正的原因吗?