Array.reduce () в ES6, неявные возвраты и синтаксический сахар. Что на самом деле происходит? - PullRequest
2 голосов
/ 24 июня 2019

Мне интересно понять, что происходит внутри этого алгоритма.Давайте рассмотрим следующий набор данных:

const data = [
  {
    emp_code: "a001",
    company_code: "company_a",
    name: "abx",
    details: [],
    details_dtypes: []
  },
  {
    emp_code: "b002",
    company_code: "company_b",
    name: "xbz ",
    details: [],
    details_dtypes: []
  },
  {
    emp_code: "a002",
    company_code: "company_a",
    name: "xbz ",
    details: [],
    details_dtypes: []
  },
  {
    emp_code: "b003",
    company_code: "company_b",
    name: "xbz ",
    details: [],
    details_dtypes: []
  }
];

Теперь, если бы я хотел сжать эти данные до object {}, где каждый ключ является уникальным company_code, а соответствующее ему значение - array [] из emp_codes для этой компании я мог бы сделать что-то вроде:

let result = data.reduce((r, c) => {
  r[c.company_code] = [...(r[c.company_code] || []), c.emp_code];
  return r;
}, {});

Выше мы явно возвращаем конечный объект, который кажется очень ясным.

Теперь я недавно обнаружилчто вы можете сократить это с помощью некоторого синтаксического сахара, например, так:

let result = data.reduce(
  (r, c) =>
    (r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r,
  {}
);

Результаты идентичны, но я не уверен, насколько это возможно.Насколько я понимаю, reduce() состоит в том, что существует внутренний цикл, который перебирает каждый элемент и позволяет вам применять определенную логику для формулирования вашего конечного результата.Я вижу, что они используют оператор &&.Моя гипотеза состоит в том, что логика между круглыми скобками () выполняется как традиционный метод reduce(), и когда она завершена, мы просто возвращаем результат, следовательно && r.

Но что-то в этом просто кажется неправильным.Внутренняя петля все еще происходит?Если это так, как они могут изолировать его таким образом, не выполняя условие после первой итерации, возвращая, таким образом, r.Они возвращают r для каждой итерации цикла?

Вот песочница, которая показывает оба алгоритма: https://codesandbox.io/s/hooks-with-reduce-nested-data-goee1. Большое спасибо за ваш вклад:)

Ответы [ 3 ]

3 голосов
/ 24 июня 2019

С &&, если все задействованные выражения являются правдивыми, оно оценивается как значение конечного выражения - того, которое следует за &&.Итак, если у вас есть код вроде:

function foo() {
  <some expression>;
  return bar;
}

, вы также можете написать его как

function foo() {
  return <some expression> && bar;
}

или с неявным возвратом функции стрелки:

const foo = () => <some expression> && bar;

, что эквивалентно

const foo = () => {
  <some expression>;
  return bar;
};

Так что да, для вашего кода с && r в конце функции стрелки, это означает, что r неявно возвращается на каждой итерации.

Все, что сказано, это в значительной степени напротив синтаксического сахара.Это то, что вы часто видите, если запускаете код через minifier , цель которого - максимально уменьшить количество текста в Javascript.Но минимизированный код часто очень трудно читать, и, как показывает ваш вопрос, это не исключение.Если при использовании неявного return требуется оператор запятой или использование && для оценки чего-то другого , чем логическое значение, код, вероятно, слишком запутанный, чтобы его можно было легко прочитать, и явный return, вероятно, является лучшим выбором..

Вы также можете рассмотреть возможность добавления к существующему массиву в объекте-аккумуляторе, тем самым избегая необходимости создавать много ненужных промежуточных массивов, которые распределяются только позже:

let result = data.reduce((r, c) => {
  if (!r[c.company_code]) {
    r[c.company_code] = [];
  }
  r[c.company_code].push(c.emp_code);
  return r;
}, {});

Это код, который я хотел быпредпочитают.Он несколько длиннее, но его легче читать на много , когда каждый оператор / логическая ветвь находится в отдельной строке.

1 голос
/ 24 июня 2019

Проблема здесь в краткости. Когда вы слишком сильно уменьшаете свой код, вы теряете читабельность. Это проблема. Давайте изменим процесс и сделаем ваш код более традиционным.

let result = data.reduce(
  (r, c) => (r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r,
  {}
);

Строки 1, 3 и 4 довольно просты. Хитрая часть - строка 2.

(r, c) => (r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r,

Это эквивалентно:

function processData(accumulator, currentItem) {
  if ( accumulator[currentItem.compony_code] === undefined ) {
    accumulator[currentItem.compony_code] = [];
  }

  accumulator[currentItem.compony_code].push(currentItem.emp_code);
  return accumulator;
}

Теперь перейдем к вашей гипотезе о (), это хак, который создает выражение и позволяет вам выполнять задачи без выражения ( в данном случае ).

1 голос
/ 24 июня 2019

Давайте разберем эту строку:

(r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r

Присвоение в JS всегда возвращает присвоенный элемент , а логический оператор AND (&&) следует правилам:

expr1 && expr2

Если expr1 можно преобразовать в true, возвращает expr2; иначе возвращает expr1.

Так что в основном это то же самое, что и

if(r[c.company_code] = [...(r[c.company_code] || []), c.emp_code])
  return r

И поскольку массивы ([...(r[c.company_code] || []), c.emp_code]) в JS преобразуются в true при разборе на логическое значение (как, впрочем, и объекты, функции и символы), всегда возвращается r.

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