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.

Хотите масштабировать свой запуск программного обеспечения? Посмотрите Цирк.