На прошлой неделе я наконец подписался на Codewars, чтобы попытаться расширить свои ресурсы для внеклассного обучения, и, должен сказать, меня сразу же зацепили. После нескольких недель создания (и полного наслаждения) React, было забавно изменить темп использования JavaScript для программного решения логических проблем.

Один из моих любимых катов, над которым я работал, назывался Persistent Bugger. Инструкции были следующие:

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

Они также привели несколько примеров:

persistence(39) === 3 // because 3*9 = 27, 2*7 = 14, 1*4=4                        // and 4 has only one digit   
persistence(999) === 4 // because 9*9*9 = 729, 7*2*9 = 126,                                    // 1*2*6 = 12, and finally 1*2 = 2   
persistence(4) === 0 // because 4 is already a one-digit number

Подсчет цифр

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

let num = 305
num.toString().split('').map(Number) 
==> [3, 0, 5]

После преобразования в строку выше я split каждой цифрой, которая вернула мне массив строк. Поскольку мне нужен массив целых чисел, я затем вызвал функцию map с аргументом конструктора объекта Number, чтобы отобразить массив и вернуть мне нужный мне массив целых чисел.

Время размножаться

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

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

Давай зациклимся!

Я решил двигаться дальше, используя цикл while для выполнения итерации while digits.length > 1. При каждом запуске итерации мне нужно было сохранять цифры предыдущего итога, поэтому после forEach я переназначал digits и сбрасывал total при каждом запуске моей итерации.

Хотя это заставляет нас перебирать эти цифры до тех пор, пока не останется одна цифра (бросьте console.log() в цикл, чтобы увидеть его в действии в этот момент), наше возвращаемое значение всегда будет 1, поскольку это назначенное значение total в конце каждой итерации. . Мы также не учитываем, сколько раз выполнялся цикл while, который мы на самом деле хотим вернуть, а не значение total в любой момент.

Как лучше вести счет, чем добавлять счетчик?

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

Хорошо, но эта функция довольно долгая.

Я однозначно согласен! Давайте проведем рефакторинг.

Использование функции JavaScript reduce ()

MDN определяет reduce() следующим образом:

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

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

  • аккумулятор (обязательно) - это значение является либо ссылкой на необязательное начальное значение, переданное в reduce, либо, если значение не было предоставлено, здесь будет использоваться первый элемент массива.
  • текущее значение (обязательно) - если для начального значения было указано значение, это будет первый элемент в массиве. Если значение не было предоставлено для начального значения, это будет второй элемент массива, поскольку первый будет использоваться в качестве аккумулятора.
  • текущий индекс (необязательно) - это индекс текущего элемента в итерации. Это начнется с 0, если аккумулятор не присутствует, и в этом случае он начнется с 1.
  • сам массив (необязательно) - вы также можете передать сам массив, который вы повторяете, в качестве аргумента. Это полезно, если вы хотите использовать элементы или атрибуты массива как часть функции обратного вызова (например, array.length)

Используя reduce(), я смог преобразовать строки цикла while в одну строку кода. Я установил digits равным возвращаемому значению моей однострочной функции, поэтому digits будет переназначаться каждый раз во время итерации.

Однострочная функция начинается с вызова split() для ранее определенного значения digits (которое на нашей первой итерации цикла while равно аргументу num, изначально преобразованному в строку вне цикла), разбиения по цифрам и затем сопоставление массива, чтобы получить наш массив чисел, который у нас был раньше. Затем мы вызываем reduce() в этом массиве, передавая функцию обратного вызова, которая возвращает нам произведение двух чисел, без передачи необязательного начального значения. В первой итерации a будет равно первой цифре в массиве digits, а b будет равно второй. Во второй раз в итерации a будет равно произведению (или накоплению) a и b из первой итерации, а b будет равно третьей цифре в массиве, если таковая имеется, которая затем будет умножена на аккумулятор также. Этот процесс будет продолжаться до тех пор, пока у нас не будет единственного произведения всех цифр, и в этот момент это значение будет возвращено из функции сокращения, а затем снова преобразовано в строку, чтобы мы могли начать процесс заново на следующей итерации while. петля.

Этот рефакторинг экономит несколько строк кода и объединяет шаги, необходимые для выполнения этой задачи. Хотя я выбрал описанный выше процесс, есть много разных способов решить это ката - попробуйте свой собственный и отправьте его на Codewars, если вам так хочется.

Если вы хотите узнать больше о reduce() и его различных приложениях, ознакомьтесь с документацией MDN, чтобы узнать больше.