Ошибка: acc.concat не является функцией для моей функции плоского массива js? - PullRequest
1 голос
/ 10 ноября 2019

Я пытаюсь написать плоскую функцию js, например Array.prototype.flat ()

, используя concat + рекурсию. Тем не менее, мой код всегда бросать acc.concat не является ошибкой функции. Я не знаю, что не так с моим кодом. Кто-нибудь может помочь мне с этим вопросом? Большое спасибо заранее!

   function flatten (arr) {
      return arr.reduce((acc, val) => {
          if (Array.isArray(val)) {
              acc.concat(flatten(val));
          } else {
              return val;
          }
         return acc;
      }, []);
   }

Ответы [ 2 ]

2 голосов
/ 10 ноября 2019

Я вижу пару проблем:

  • Array#concat - это не на месте функция. Он возвращает новый массив, который должен быть назначен или возвращен acc = acc.concat(flatten(val)); или return acc.concat(flatten(val));.
  • val s не добавляются в аккумулятор ни в одной точке. return val; нарушает обычный контракт функции обратного вызова, которая должна возвращать аккумулятор для следующего элемента, который будет использоваться.

const flatten = a =>
  a.reduce((acc, val) =>
    acc.concat(Array.isArray(val) ? flatten(val) : val)
  , [])
;
   
console.log(flatten([1,2,3,4,[5,6,[7,[8,9]]]]));

Кроме того, я думаю, reduce здесь не самая подходящая функция. Всякий раз, когда я обнаруживаю, что сокращаю массив и не фильтрую какие-либо элементы, я знаю, что могу рефакторинг в Array#map, который является более конкретным reduce. Предпочитайте reduce только тогда, когда filter или map трудно использовать, например, при создании объектов.

const flatten = a =>
  [].concat(...a.map(val => Array.isArray(val) ? flatten(val) : val))
;
   
console.log(flatten([1,2,3,4,[5,6,[7,[8,9]]]]));

Это позволяет избежать всего довольно неловкого бизнеса с аккумуляторами.

Но если у вас нет доступа к оператору распространения и вы ищете полную совместимость,тогда reduce снова полезен, потому что он позволяет нам вызывать concat для каждого элемента:

function flatten(a) {
  return a.reduce(function (acc, val) { 
    return acc.concat(Array.isArray(val) ? flatten(val) : val);
  }, []);
}
   
console.log(flatten([1,2,3,4,[5,6,[7,[8,9]]]]));
1 голос
/ 10 ноября 2019

Это потому, что вы делаете шаг, чтобы прекратить рекурсию, прежде чем делать рекурсию, и это сбивает вас с толку.

Когда вы делаете if (Array.isArray(val)), вы должны решить, следует ли обрабатывать val как массив и перейти к flatten снова или нет, и в этом случае вы можете просто продолжить обрабатывать его как обычно. Однако в обоих случаях вы хотите добавить val к аккумулятору, а в ветке else вы просто делаете return val, поэтому при следующем выполнении обратного вызова acc будет равно valкоторый мы уже знаем, это не массив.

Вместо этого в обоих случаях вы должны добавлять в массив. Разница лишь в том, что один раз вы должны рекурсивно flatten, а другой нет. Это может быть упрощено, если условие завершения обращается к flatten, а не к обратному вызову reduce:

function flatten (data) {
    if(!Array.isArray(data)) return data;
    
    return data.reduce((acc, val) => acc.concat(flatten(val)), []);
 }

const input = [[1], [2, [[3]]]];
console.log(flatten(input));

Итак, оба раза вы вызываете flatten, но если значение не является массивом, вы просто возвращаете его, поэтому вы рассматриваете его как обычный acc.concat(1) - добавлениепростая ценность. Если вы получаете массив, то вы рекурсивно разворачиваете и выравниваете его. И поскольку теперь flatten не гарантирует получение массива, я переименовал параметр в data, чтобы избежать путаницы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...