DevAdvent 2022
Как сравнивать элементы массива в JavaScript
Использование функций для определения исхода дуэли
Проблема сегодняшнего DevAdvent касается сравнения элементов двух массивов. Интересно то, как проблема представлена на реальном примере. Представьте, что вы хотите создать игру, в которой две команды противостоят друг другу. Каждый участник бросает вызов соответствующему члену противоположной команды. В данном случае функция JavaScript для получения результата дуэлей из массивов — это именно то, что нам нужно.
Задача: пережить атаку
ссылка на ката
Даны два массива, в которых значения представляют собой силу каждого солдата, верните true, если вы выжили после атаки, или false, если погибнете.
УСЛОВИЯ
- Каждый солдат атакует солдата противника с тем же индексом массива. Выживший - это число с наибольшим значением.
- Если значение одинаковое, они оба погибают
- Если одно из значений пусто (различная длина массива), солдат с непустым значением выживает.
- Чтобы выжить, обороняющаяся сторона должна иметь больше выживших, чем атакующая сторона.
- В случае, если с обеих сторон одинаковое количество выживших, побеждает команда с наибольшей начальной силой атаки. Если общая сила атаки обеих сторон одинакова, верните true.
- Начальная сила атаки представляет собой сумму всех значений в каждом массиве.
ПРИМЕРЫ
attackers=[ 1, 3, 5, 7 ] defenders=[ 2, 4, 6, 8 ] //0 survivors 4 survivors //return true attackers=[ 1, 3, 5, 7 ] defenders=[ 2, 4 ] //2 survivors (16 damage) 2 survivors (6 damage) //return false attackers=[ 1, 3, 5, 7 ] defenders=[ 2, 4, 0, 8 ] //1 survivors 3 survivors //return true
Решение
Решение концептуально довольно простое. Я могу создать функцию JavaScript, подобную этой:
export const hasSurvived = (attackers, defenders) => { let attackingPower = attackers.reduce((acc, val) => acc + val, 0); let defendingPower = defenders.reduce((acc, val) => acc + val, 0); let attackingSurvivors = 0; let defendingSurvivors = 0; for (let i = 0; i < Math.max(attackers.length, defenders.length); i++) { if (i >= attackers.length) { defendingSurvivors++; } else if (i >= defenders.length) { attackingSurvivors++; } else if (attackers[i] > defenders[i]) { attackingSurvivors++; } else if (defenders[i] > attackers[i]) { defendingSurvivors++; } } return ( defendingSurvivors > attackingSurvivors || (defendingSurvivors === attackingSurvivors && defendingPower >= attackingPower) || (defendingSurvivors === attackingSurvivors && attackingPower === defendingPower) ); };
Эта функция сначала вычисляет начальную силу атаки как для атакующей, так и для обороняющейся команды. Затем он перебирает массивы и сравнивает значения по каждому индексу. Если значение текущего индекса в массиве attackers
больше, чем значение того же индекса в массиве defenders
, количество атакующих выживших увеличивается. Если значение текущего индекса в массиве defenders
больше, чем значение того же индекса в массиве attackers
, количество защищающихся выживших увеличивается. Если один из массивов короче другого, учитываются также выжившие солдаты из более длинного массива.
Наконец, функция возвращает true
, если количество выживших в обороне больше, чем количество выживших в нападении, или если количество выживших солдат такое же, но у обороняющейся команды более высокая начальная сила атаки, или если общая сила атаки обоих стороны одинаковые. Если ни одно из этих условий не выполняется, функция возвращает false
.
Однако есть одна вещь, которая мне не нравится. Этот ряд условий «если…тогда…иначе» затрудняет чтение кода и делает функцию менее элегантной. Со временем я заметил, что когда функция JavaScript выглядит уродливо, это означает, что есть что улучшить.
Давайте внимательнее посмотрим на код.
Что я хочу сделать, так это сравнить каждый элемент в массиве, а затем подсчитать, сколько элементов соответствует заданному условию.
Попробую перефразировать предложение по-другому: я хочу получить из каждого массива все элементы, удовлетворяющие определенному условию. Другими словами, я хочу отфильтровать все элементы, которые больше соответствующего элемента в другом массиве.
Здесь, переформулировав проблему, я получаю новую идею. Я могу использовать метод Array.filter(), чтобы сравнить различные элементы в каждом массиве и оставить только те, которые нас интересуют.
let defendersAfterFight = defenders.filter((e, i) => 0 < e - attackers[i]); let attackersAfterFight = attackers.filter((e, i) => 0 < e - defenders[i]);
Теперь все, что мне нужно сделать, это вычислить длину каждого массива, чтобы узнать, сколько выживших среди атакующих и защитников.
const hasSurvived = (attackers, defenders) => { let attackingPower = attackers.reduce((acc, val) => acc + val, 0); let defendingPower = defenders.reduce((acc, val) => acc + val, 0); let defendersAfterFight = defenders.filter((e, i) => 0 < e - attackers[i]); let attackersAfterFight = attackers.filter((e, i) => 0 < e - defenders[i]); return ( defendersAfterFight.length > attackersAfterFight.length || (defendersAfterFight.length === attackersAfterFight.length && defendingPower >= attackingPower) ); };
Я могу еще больше упростить код, непосредственно подсчитав длину различных массивов.
const hasSurvived = (attackers, defenders) => { let attackingPower = attackers.reduce((acc, val) => acc + val, 0); let defendingPower = defenders.reduce((acc, val) => acc + val, 0); let defendersSurvived = defenders.filter( (d, i) => 0 < d - attackers[i] ).length; let attackersSurvived = attackers.filter( (a, i) => 0 < a - defenders[i] ).length; return ( defendersSurvived > attackersSurvived || (defendersSurvived === attackersSurvived && defendingPower >= attackingPower) ); };
Если мы хотим преувеличить, мы можем еще больше сократить количество строк кода, необходимых для решения проблемы.
Но я не рекомендую это. Функцию JavaScript трудно читать. Нет никакой практической пользы от чрезмерного усложнения кода. Лучше писать чистый, легко читаемый код: наше будущее скажет нам спасибо.
const hasSurvived = (a, d) => d.filter((x, i) => 0 < x - a[i]).length > a.filter((x, i) => 0 < x - d[i]).length || (d.filter((x, i) => 0 < x - a[i]).length === a.filter((x, i) => 0 < x - d[i]).length && d.reduce((c, v) => c + v, 0) >= a.reduce((c, v) => c + v, 0));
Спасибо за прочтение! Оставайтесь с нами, чтобы узнать больше.
Не пропустите мою следующую статью — подпишитесь на мой средний список адресов электронной почты
Первоначально опубликовано на https://blog.stranianelli.com 22 декабря 2022 г.
Больше контента на PlainEnglish.io.
Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord.