Продолжая покорение серии алгоритмов FreeCodeCamp… моя последняя задача: обновление 2D-массива данными из другого 2D-массива.

Что такое двумерный массив?

Двумерный массив (также известный как матрица) представляет собой массив массивов. Каждый из внутренних массивов содержит одинаковое количество элементов. Каждый индекс внутреннего массива соответствует имени так же, как каждый столбец в таблице соответствует имени. Все еще в замешательстве? Хорошо. Вот схема:

Скорее всего, у вас нет проблем с пониманием таблицы выше. Таблицы располагают вещи в строках и столбцах. Заголовок столбца именует все в соответствующем столбце. Каждая строка представляет один элемент и содержит атрибуты, описывающие этот элемент. Эти атрибуты располагаются в столбце, соответствующем их имени. Вы заметите, что столбцы всегда содержат элементы одного и того же типа. Например, «Количество» содержит только числа, а «Элемент» — только строки. Это важное ограничение в двумерных массивах и, по сути, требование.

Вот как это будет выглядеть в виде 2D-массива:

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

Не так уж сильно отличается от стола, верно? Однако вы заметите, что ничего не говорит вам, что означает каждый «столбец». Предполагается, что вы уже знаете, что означает каждый «столбец», и ваш код настроен в другом месте для работы с ним.

Еще несколько вещей, на которые стоит обратить внимание:

  1. Каждый элемент внешнего массива должен быть массивом.
  2. Каждый внутренний массив должен содержать одинаковое количество элементов.
  3. Индекс каждого внутреннего массива (столбца) должен быть одного типа.

Собираем все вместе

Итак, чтобы разбить его, вот процедуры, которые должны произойти:

  1. Проверьте два 2D-массива на основе вышеуказанных критериев.
  2. Прокрутите массив, содержащий новые данные, и определите, существует ли каждый элемент в массиве инвентаризации.
  3. Если это так, обновите столбец количества.
  4. Если это не так, создайте новый массив и добавьте его в массив инвентаризации.
  5. Отсортируйте массив по алфавиту.

Решение

Если вы используете эти данные…

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

и запустите его через функцию обновления…

updateInventory(curInv, newInv)

результат должен быть…

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

Давайте поговорим об объектах

updateInventory даже не нужно ничего возвращать, если вы передаете предопределенный массив в качестве первого параметра. Почему? Массивы являются объектами, и объекты ведут себя не так, как типы значений, когда вы передаете их функциям. Когда вы передаете тип значения (число или строка), он копирует это значение. Когда вы передаете объект, он копирует адрес памяти в этот объект, но сам объект не копируется. Например:

let mynum = 1;
const fun = (num) => {num += 1;};
fun(mynum);
console.log(mynum) //returns `1`

Как вы можете видеть выше, mynum не затрагивается, когда он передается в fun(), потому что mynum является типом значения. Однако если бы вы имели дело с объектами, это была бы другая история:

const arr = ['hello'];
const updateArray = (o) => {o[0] += ' world';};
updateArray(arr)
console.log(arr[0]) // returns `'hello world'`

Поскольку arr является объектом, мы копируем адрес памяти в arr, когда передаем его в updateArray(). Сам объект не копируется, поэтому updateArray() в конечном итоге обновляет исходный объект, а не просто его копию.

Итак, чтобы показать это, в updateInventory() мы могли бы опустить return arr1;, а curInv все равно было бы обновлено. Что еще более важно, если вы присвоили результатupdateInventory()новой переменной, у вас может возникнуть соблазн подумать, что он чем-то отличается от curInvхотя на самом деле это не так. Новая переменная будет указывать на то же место в памяти, что и curInv, и вы можете запутаться, если ожидаете, что оставьте curInv нетронутым.

Если вы действительно хотите сохранить curInv, я предлагаю вам сделать его копию, прежде чем запускать ее через updateInventory(). Есть несколько способов сделать это, но самый быстрый и грязный способ — преобразовать его в строку, а затем преобразовать обратно в объект:

const CLONE = (obj) => {
  return JSON.parse(JSON.stringify(obj));
};
const clonedCurInv = CLONE(curInv);

Есть более красноречивые (и, вероятно, более эффективные) способы сделать это, но я обнаружил, что в большинстве случаев это работает хорошо.