Объяснение Javascript для уменьшения аккумулятора

Кто может любезно объяснить приведенную ниже конструкцию аккумулятора acc простым английским языком?

return arr1.reduce(function(acc, curr){  
      var last = acc[acc.length-1];  
        if(acc.length > 0 && curr[1]===last[1]) {
        last[0] += curr[0];
      } else acc.push(curr);
      return acc;
      }, []);
    }

Этот reduce метод можно использовать для решения задачи FreeCodeCamp Inventory Update в рамках их расширенных уроков по написанию сценариев алгоритмов.

Требуется обновить существующие элементы в массиве инвентаря (arr1) новыми элементами в новом массиве доставки.

Два тестовых массива, текущий инвентарь curInv и новый доставленный newInv соответственно, могут быть следующими:

var curInv = [
    [21, "Bowling Ball"],
    [2, "Dirty Sock"],
    [1, "Hair Pin"],
    [5, "Microphone"]
];

var newInv = [
    [2, "Hair Pin"],
    [3, "Half-Eaten Apple"],
    [67, "Bowling Ball"],
    [7, "Toothpaste"]
];

Найдя несколько отличных статей о методе сокращения Javascript (например, этот пост и отличный видеокурс на egghead.io), и каким-то образом почувствовав силу, которую он исчерпывает, я бы прочитал этот метод следующим образом:

Уменьшите инвентарный массив, создав сначала пустой массив [] (начальное значение), а затем применив следующую функцию обратного вызова:

Если массив инвентаря в настоящее время не пуст (имеет длину больше нуля), а имя обрабатываемого в данный момент элемента (например, индекс 0 curr может читать Bowling Ball) идентично последнему элементу обновляемого массива инвентаря, тогда обновить количество этого предмета в массиве инвентаря.

Последний элемент определяется прямо над оператором if следующим образом: возьмите текущую длину накопленного массива до настоящего момента, вычтите 1 и используйте это значение для индексации накопленного массива. Затем элемент с этим индексом присваивается переменной last.

С другой стороны, если инвентарь пуст, добавьте новый предмет полностью, то есть: имя и количество предмета. Теперь верните только что накопленный массив. *

Каким образом использование length - 1 аккумулятора полезно для фактического накопления acc? (простите за аллитерацию)

Думаю, я понимаю большую часть того, как построен этот метод сокращения, но, пожалуйста, поправьте меня везде, где я ошибаюсь), кроме этого конкретного использования acc.length-1.

Ура, к.


person Juchtmans Chris    schedule 15.08.2017    source источник
comment
как этот редуктор используется для решения этой проблемы? вы можете поделиться настоящим звонком?   -  person thedude    schedule 15.08.2017
comment
@thedude: похоже, одно решение сочетает метод сокращения с concat и сортировку в алфавитном порядке, как это Repl.it link   -  person Juchtmans Chris    schedule 15.08.2017


Ответы (3)


Фактическое решение включает объединение и сортировку обоих массивов и только затем их уменьшение. В этом случае всякий раз, когда мы оцениваем новый элемент, если его имя не равно последнему элементу аккумулятора, это означает, что это новый элемент.

Используя ваш пример, мы сокращаем список:

[
  [ 21, 'Bowling Ball' ],
  [ 67, 'Bowling Ball' ],
  [ 2, 'Dirty Sock' ],
  [ 1, 'Hair Pin' ],
  [ 2, 'Hair Pin' ],
  [ 3, 'Half-Eaten Apple' ],
  [ 5, 'Microphone' ],
  [ 7, 'Toothpaste' ]
]

поэтому, когда мы встречаем второй элемент, последнее значение в аккумуляторе [21, 'Boweling Ball'], и когда мы сравниваем строки, мы переходим к первому условию.

person thedude    schedule 15.08.2017
comment
Верно! спасибо, увидев этот сокращающийся список, он щелкнул; «Шар для боулинга» в качестве «последнего» пункта означает, что когда появляется следующий «Шар для боулинга», функция добавляет 1 - person Juchtmans Chris; 16.08.2017

Вы спрашиваете об этом разделе?

var last = acc[acc.length-1]; 

Если это так, то в соответствии с длиной-1 это так, потому что в массиве

acc = [a,b,c,d]
acc.length is equal to 4

чтобы получить доступ к элементу d, вы получите к нему доступ через

acc[3];  //which equals d

Это потому, что мы считаем позиции от 0,1,2,3

person Kennedy Nyaga    schedule 15.08.2017

tl; dr: посмотрите этот пример REPL


Reduce может уменьшить исходный массив до значения, определенного в функции.

Например, если бы у нас был массив целых чисел, который мы хотели бы вычислить его общую сумму, мы могли бы использовать reduce для этого.

Конечно, вы можете спросить, зачем вам вообще использовать reduce вместо for loop?

Основная причина, по которой сокращение является лучшим выбором, заключается в том, что функция редуктора, которую мы передаем в качестве параметра, является чистой . Это означает, что для вычисления результата не нужны ни дополнительные, ни внешние переменные. В цикле for нам потребуется переменная totalSum для вычисления результата, тогда как в версии reduce этого не требуется.

Вернемся к примеру OP: вот как мы могли бы создать reduce для группировки по приглашениям с одинаковыми именами:

// reduce example
const reducer = (accumulator, currentValue) => {
    if (accumulator.length === 0) {
        accumulator.push(currentValue);
        return accumulator;
    }

    const index = accumulator.findIndex(el => el[1] === currentValue[1]);

    if (index !== -1) {
        accumulator[index][0] += currentValue[0];
        return accumulator;
    }
    accumulator.push(currentValue);
    return accumulator
};

Вот эквивалентная версия с циклом for:

// for loop example
let withForLoop = (input) => {
    let res = [];
    for (let i = 0; i < input.length; i++) {
        let currInput = input[i];
        const index = res.findIndex(el => el[1] === currInput[1]);
        if (index !== -1) {
            res[index][0] += currInput[0];
            continue;
        }
        res.push(currInput);
    }
    return res;
}

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

person Menelaos Kotsollaris    schedule 30.05.2021