Если вы оказались здесь после выполнения задания по коду из моего предыдущего поста Испытание по коду: получение суммы всех элементов в нескольких массивах, то вы готовы к чему-то более сложному. Если вы здесь органично, читать предыдущий пост не обязательно, но рекомендуется. Давайте вернемся к исходному вопросу, уделяя особое внимание части, выделенной жирным шрифтом.

Напишите функцию, которая при наличии нескольких массивов целых чисел будет вычислять сумму всех элементов в массивах. Функция должна иметь возможность принимать любое количество массивов. Ради вопроса: функция будет передавать только массивы, и эти массивы будут содержать только целые числа.

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

Проверка аргументов

…функция будет передавать только массивы…

JavaScript не является статически типизированным языком, таким как Java и Scala. Обычно это хорошо, потому что нам не нужно тратить время на указание типов для наших данных. В данном случае это усложняет задачу, потому что мы не можем проверять наши аргументы по мере их передачи. Нам нужно проверить типы аргументов в теле функции. Давайте вернемся к нашей последней функции из предыдущего блога.

const sumOfArrays = (...arrays) => {
  //consolidate all potential arrays into one
  var merged = [].concat(...arrays)
 
  var count = 0 //initialize at 0
merged.forEach((x)=>{
    //Each iteration of consolidated array, increment count by x 
    count += x 
  })
//once all items have been added to count, return count
  return count 
}

Наша функция использует аргумент ...arrays, используя оператор спреда. Использование оператора распространения, как вы помните, позволяет нашей функции принимать любое количество аргументов.

Чтобы получить доступ к элементам в ...arrays, мы можем просто сослаться на arrays без оператора распространения. Давайте посмотрим, что это возвращает.

func1 = (...arrays) =>{
 console.log(arrays[0]);
 console.log(arrays[1]);
}
func1(1, 2);
> 1
> 2

Опуская ..., мы можем получить доступ к массиву аргументов. Затем мы можем отфильтровать аргументы, которые соответствуют нашему требованию к массиву, и опустить все, что не является массивом. Как узнать, является ли элемент массивом? Ну, мы могли бы попробовать метод массива. Если он выдает результат, то это массив. Например, если мы можем выполнить .map(), это может быть массив. Однако это может быть и Карта. Мы могли бы сделать element.length. Но ждать! Это также работает для строк. И что теперь?

Есть и другие методы, которые мы можем попробовать, но Array.isArray() может быть немного проще. Цель этой функции, как следует из названия, — определить, является ли переданное значение массивом. Вот как это работает:

var arr = [1, 2, 3, 4] //is an array
var num = 2 //is an integer
var str = 'not an array'//is a string
console.log(Array.isArray(arr))
console.log(Array.isArray(num))
console.log(Array.isArray(str))
> true
> false 
> false

Идеально. Мы можем использовать это для фильтрации массива аргументов в зависимости от того, возвращает ли .isArray() значение true или false. Если это правда, сохраните элемент. Если это не…

Давайте добавим эту строку в нашу функцию прямо перед тем, как мы объединим массивы:

var onlyArrays = arrays.filter((x)=>{return Array.isArray(x)})

Потрясающий! Затем мы можем позволить merged быть равными нашим отфильтрованным значениям, объединенным в пустой массив, например:

var merged = [].concat(...onlyArrays)

Помните, нам нужно использовать оператор распространения с нашим новым значением, потому что в противном случае он просто добавит массив массивов к пустому массиву. Он не будет объединять массивы в onlyArrays друг с другом. В этом случае оператор спреда работает так же, как .apply(). Не беспокойтесь о том, что пустые массивы не будут пропущены. Поскольку они пусты и объединяются, дополнительные/дублирующиеся значения добавляться не будут.

Теперь, когда у нас есть один объединенный массив элементов, мы можем перейти к следующему ограничению.

…и эти массивы будут содержать только целые числа.

Теперь, когда все массивы объединены в один, мы можем снова использовать фильтр, чтобы удалить все элементы, не являющиеся числами. Я говорю числа вместо целые, потому что целое число — это число, не имеющее десятичной дроби. Число с десятичным знаком называется числом с плавающей запятой. Все, что может быть объединено для получения суммы, должно оставаться в массиве. как нам это сделать?

Что ж, вашим первым побуждением, как и моим, могло быть использование isNaN(). Это работает… за некоторыми исключениями.

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

isNumberFunction = (...args) =>{
 args.forEach((x)=>{console.log(isNaN(x))})
}
isNumberFunction('not a num', [1, 2], {number: 3}, '2', [2], ['2'], true, "")
> (3) true
> (5) false 

Как видите, есть несколько исключений из правил:

  • Строка числа (isNaN('3'))
  • Массив, содержащий один элемент в виде числа (isNaN([3]))
  • Массив, содержащий один элемент, представляющий собой строку числа (isNaN(['3']))
  • Логическое значение (isNaN(true))
  • Пустая строка (isNaN(""))

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

Мы можем использовать Number.isInteger(), чтобы определить, является ли что-то целым числом, но это не будет работать с числами с плавающей запятой. К сожалению, в JavaScript нет функции Number.isFloat(). Итак, как мы можем гарантировать, что наш массив содержит только те значения, которые могут дать сумму? Как мы можем фильтровать целые числа, а также числа с плавающей запятой? Мы можем добиться этого, проверив одну вещь:

  • Number(x) === x

Первая строка использует объект Number для преобразования значения в число. Затем мы видим, равно ли это принудительное значение исходному значению. Таким образом, хотя Number('1') становится 1, оно не равно '1'. Number также работает с поплавками! Скопируйте/вставьте приведенный ниже код в свою консоль и наблюдайте за волшебством.

isNumberFunction = (...args) =>{
 args.forEach((x)=>{console.log(Number(x) === x})
}
isNumberFunction('not a num', [1, 2], {number: 3}, '2', [2], ['2'], true, 3, 4.1)
> (7) false
> (2) true

Как видите, функция способна различать значения, которые являются числами или нет. Добавьте следующую строку после строки, в которой вы объединили массив в своей финальной функции.

merged = merged.filter((x)=>{return Number(x) === x})

Если вам интересно, вы также можете написать строку ниже и получить тот же результат.

merged = merged.filter((x)=>{return typeof(x) === "number"})

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

Финальная, окончательная функция должна выглядеть так:

const sumOfArrays = (...arrays) => {
  
  //filter 'arrays' to include ONLY arrays
  var onlyArrays = arrays.filter((x)=>{return Array.isArray(x)})
  
  //consolidate all arrays into one
  var merged = [].concat(...onlyArrays)
  //Can also use Number(x) === x 
  merged = merged.filter((x)=>{return typeof(x) === "number"})
  var count = 0 //initialize at 0
  merged.forEach((x)=>{
    //Each iteration of consolidated array, increment count by x 
    count += x 
  })
//once all items have been added to count, return count
  return count 
}

Резюме

Функция по-прежнему собирает все числа в массивах и выдает сумму, как это было сделано в предыдущем сообщении в блоге. Но теперь наша функция также…

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

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

Если вам понравился пост или вам нужны разъяснения по поводу чего-либо упомянутого, дайте мне знать в комментариях. Не стесняйтесь делиться и оставлять хлопки!

Обновите бесплатную подписку на Medium до платной здесь, и всего за 5 долларов США в месяц вы будете получать неограниченное количество статей без рекламы от тысяч авторов в самых разных изданиях. Это партнерская ссылка, и часть вашего членства помогает мне получать вознаграждение за контент, который я создаю. Спасибо!

использованная литература







Map
Объект Map содержит пары «ключ-значение
и запоминает исходный порядок вставки ключей.developer.mozilla.org»