Рекомендации по тестированию / проверке строк в JavaScript

#javascript #arrays #regex #arrow-functions #boolean-expression

Вопрос:

Недавно я столкнулся с этим (по общему признанию) учебным упражнением на JavaScript, и у меня возникли проблемы с концептуальным рассмотрением двух решений, которые, как мне сказали, являются «лучшими практиками». Я понимаю, что это довольно простой материал; Я просто пытаюсь как можно лучше понять основные принципы здесь.

Само упражнение достаточно простое: напишите функцию, которая проверяет, содержит ли строка одинаковое количество двух уникальных символов (в данном случае » x » и «o»). Функция должна возвращать логическое значение и не учитывать регистр.

Первым решением «наилучшей практики», которое я частично понял, было следующее:

 function XO(string) {
let x = str.match(/x/gi);
let o = str.match(/o/gi);
return (x amp;amp; x.length) === (o amp;amp; o.length);
}
 

Я понимаю основную работу с регулярными выражениями, выполняемую в первых двух строках функции, но точная роль логического оператора amp;amp; в третьей и последней строке функции озадачивает меня. Мне трудно на простом английском языке объяснить себе (а), что именно он делает и (б), почему что-то подобное return x === y в одиночку не является достаточно надежным решением в этом случае.

Вторым решением «наилучшей практики», с которым я столкнулся, было следующее:

 const XO = str => {
  str = str.toLowerCase().split('');  
  return str.filter(x => x === 'x').length === str.filter(x => x === 'o').length;
}

 

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

Комментарии:

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

2. оба просто подсчитывают количество x и o (или y) в строке

3. Действительно, мне говорили, что они это делают. К сожалению, мне трудно объяснить себе простым английским языком, почему (x amp;amp; x.length) === (o amp;amp; o.length) это необходимо здесь. Для строки типа «xxxooo» не x будет =»xxx», o = «ооо», x.length = 3, amp; o.length = 3? Если да, то зачем включать все четыре значения в последнюю строку функции? Я предполагаю, что есть какое-то преимущество, которого мне не хватает? Или, может быть, лучшая практика JS, с которой я не знаком?

Ответ №1:

Во-первых, match возвращается null , если совпадений вообще нет. Если вы попытаетесь взять длину null , будет выдана ошибка.

x amp;amp; x.length работает из-за а) короткого замыкания оператора: если первое является «ложным», последнее не выполняется (это не изменит результат), и возвращается последнее значение истинности, б) любое значение вводится как логическое в логическом контексте (например, в качестве операнда в amp;amp;)

 function XO(string) {
  let x = str.match(/x/gi); 
  // x contains an array that has every match if any
  // e.g. ["x", "x", "x"] or null

  let o = str.match(/o/gi);
  // same for "o"

  return (x amp;amp; x.length) === (o amp;amp; o.length);
  // 
  // Given x === null, y === null : 
  //   (null amp;amp; null.length) === (null amp;amp; null.length)
  //   (false amp;amp; null.length) === (false amp;amp; null.length) <- boolean coercion
  //   false === false
  //   true
  // 
  // Given x === ["x", "x"], y === ["y", "y"] : 
  //   (["x", "x"] amp;amp; ["x", "x"].length) === (["y", "y"] amp;amp; ["y", "y"].length)
  //   (true amp;amp; ["x", "x"].length) === (true amp;amp; ["y", "y"].length)
  //   (true amp;amp; 2) === (true amp;amp; 2)
  //   2 === 2
  //   true
}
 

У второго есть и другие хитрости. Он использует входной аргумент в качестве переменной (что не слишком распространено, но допустимо). Кроме того, вместо обычных функций он использует «функции со стрелками». У них есть некоторые отличия, но в основном вы можете думать, что function a(x) { return 2*x; } это работает как const a = (x) => 2*x;

 const XO = str => {
  str = str.toLowerCase().split(''); 
  // replace the argument string with an array of characters
  //   str = "xXyY".toLowerCase().split('')
  //   str = "xxyy".split("")
  //   str = ["x", "x", "y", "y"]

  return str.filter(x => x === 'x').length === str.filter(x => x === 'o').length;
  //   ["x", "x", "y", "y"].filter(x => x === 'x').length === ["x", "x", "y", "y"].filter(x => x === 'y').length;
  //   ["x", "x"].length === ["y", "y"].length
  //   2 === 2
  //   true
}
 

Комментарии:

1. ГРМА Сами! Это именно то подробное объяснение / разбивка, которое я искал! Я действительно ценю, что вы нашли время, чтобы помочь мне заполнить недостающие части; это очень многое для меня прояснило.