Как сгладить часть многомерного массива только на один уровень? - PullRequest
0 голосов
/ 28 апреля 2018

У меня сложный объект 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() не подходит из-за отсутствия совместимости.

Может кто-нибудь посоветовать, как сплющить только второй уровень? Если возможно, я бы хотел сделать это путем изменения массива.

Ответы [ 2 ]

0 голосов
/ 28 апреля 2018

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. Call 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. Это либо списки произвольной длины элементов одного типа, либо списки фиксированной длины, с определенными типами в каждом индексе (иначе говоря, кортежи .)

Но ваша структура сочетает в себе оба из них. Это списки произвольной (по крайней мере, так кажется) длины, где первая запись - это имя, а последующие - спецификации. Хотя этому может быть оправдание, вы можете подумать о том, является ли эта структура особенно полезной.

7. Как я могу сделать это, не мутировав?

Если возможно, я бы хотел сделать это путем изменения массива.

Я отказываюсь принимать участие в таких ужасах.

Серьезно, однако, неизменяемые данные значительно упрощают кодирование. Есть ли реальная причина, по которой вы указали это в качестве требования?

0 голосов
/ 28 апреля 2018

Там вам не нужен редуктор - он только усложняет задачу. Сопоставьте attributes с их spec свойством, а затем используйте распространение:

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