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

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

Учитывая: [1,2,3,1,3], ваша функция должна возвращать [1,3,1,3], а не [1,3], как я сначала подумал. Что, как говорится. Давайте покопаемся. Вот мое решение на GitHub. Обратите внимание, что я знаю, что это не элегантное решение, но это мое решение.

Во-первых, мы инициализируем два пустых массива, retval и checked, которые будут использоваться внутри цикла. retval — это массив, который мы вернем, как только наша функция выполнит свои обязанности. Внутри цикла for мы будем помещать элементы, прошедшие наш тест, в этот массив. checked — это массив для отслеживания того, какие элементы мы уже проверили. В конце каждой итерации цикла for мы добавляем текущий элемент массива, который мы просматриваем, к проверенному. Нам нужен этот массив для оператора if() внутри цикла for.

Поскольку эти истории пишутся для меня и людей, чей опыт выходит за рамки JavaScript 101, я предполагаю, что читатель знает, что такое цикл for. Если нет, проверьте документы. Да, я мог бы использовать forEach, но я больше знаком с for и подумал, что, поскольку это была первая задача, я бы использовал самую простую версию структуры цикла.

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

Внутри цикла

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

Природа оператора «ИЛИ» ( || ) состоит в том, чтобы остановить обработку его родительского условного оператора, если первое сравнение возвращает true. Вот почему я выбрал место второго сравнения.

Это единственная оптимизация в моем неуклюжем алгоритме.

Мало того, что массив data.slice() может быть больше, чем проверенный массив, он воссоздается каждый раз, когда сравнивается с ним. Именно поэтому я поместил его вторым условием в операторе if и не присвоил его переменной перед оператором if. Со временем будут случаи, когда текущий элемент уже находится в проверяемом массиве. Когда это происходит, нет необходимости видеть, находится ли он в наборе элементов, которые появляются после него, так зачем создавать новый массив и вызывать для него include()?

Нарезать что?

Мы вызываем метод slice() для входного массива, начиная с элемента, следующего за текущим элементом. Slice возвращает копию нарезанного массива, но только элементы после того, где вы указали, что он должен начаться. Отсюда (ii + 1). Мы не хотим нарезать текущий элемент, потому что этот вызов include() всегда будет возвращать true. Например, если бы мы нарезали [2,4,6,8,10] на элементе с индексом 2, числом 6, нам был бы возвращен массив из [6,8,10]. Если бы мы затем проверили, находится ли тот же самый элемент с индексом 2 в первом массиве в новом массиве, он всегда возвращал бы истину. Вместо этого мы нарезаем элемент, следующий за нашим текущим элементом, который в этом примере вернет [8,10].

Нажмите это

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

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

Второй вызов push() происходит независимо от того, прошел ли текущий элемент наши сравнительные тесты. Помните, этот массив является хранилищем для всех элементов, которые мы уже проверили. Если элемент находится в этом массиве, нет необходимости разрезать входной массив и проверять оставшиеся элементы. Следовательно, мы должны добавить каждый элемент, который мы проверяем, в этот проверяемый массив.

Отправить на дом

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

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

if (user === 'John') return true; 
else return false;

Если у вас неправильная структура функций, вы можете увидеть:

“unreachable code after return statement”

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

Выводы

Перечитав вступительный абзац этой истории, я понимаю, что не до конца следил за постановкой задачи. Было сказано удалить уникальные элементы. Вероятно, поэтому возвращаемая переменная в исходной пустой функции CheckiO была такой же, как и входной параметр data. Так что, хотя технически я решил проблему, я не решил ее точно. Я не изменил массив, я создал новый массив.

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

Я также только что обновил код на GitHub, чтобы исправить последнюю проверку assert.deepEqual(), которую я добавил, чтобы увидеть, работает ли это для массива, содержащего как целые числа, так и строки. Слава Богу, что есть свободно типизированные языки.

Мое решение на CheckiO.org

Почему я написал этот рассказ

Как работает мой шаблон Node

Я всегда ищу отзывы о том, как я могу оптимизировать как мои алгоритмы, так и мое профессиональное письмо.