arr
это массив строк:
["hello", "world", "stack", "overflow", "hello", "again"]
Какой простой и элегантный способ проверить наличие arr
дубликатов и, если да, вернуть один из них (неважно, какой)?
Примеры:
["A", "B", "C", "B", "A"] # => "A" or "B"
["A", "B", "C"] # => nil
arr == arr.uniq
было бы простым и элегантным способом проверить,arr
есть ли дубликаты, однако, он не предоставляет, которые были дублированы.Ответы:
Я знаю, что это не очень элегантный ответ, но мне это нравится. Это красивый код лайнера. И работает отлично, если вам не нужно обрабатывать огромный набор данных.
Ищете более быстрое решение? Ну вот!
Он линейный, O (n), но теперь ему нужно управлять несколькими строками кода, тестами и т. Д.
Если вам нужно еще более быстрое решение, попробуйте C.
А вот суть, сравнивающая различные решения: https://gist.github.com/naveed-ahmad/8f0b926ffccf5fbd206a1cc58ce9743e
источник
a.select {|e| a.count(e) > 1}.uniq
Вы можете сделать это несколькими способами, причем первый вариант самый быстрый:
И вариант O (N ^ 2) (т.е. менее эффективный):
источник
group_by.select
ary.group_by(&:itself)
. :-)Просто найдите первый экземпляр, где индекс объекта (считая слева) не равен индексу объекта (считая справа).
Если дубликатов нет, возвращаемое значение будет равно нулю.
Я считаю, что это самое быстрое решение, опубликованное в потоке, также, поскольку оно не основано на создании дополнительных объектов
#index
и#rindex
реализовано в C. Время выполнения big-O равно N ^ 2 и, следовательно, медленнее, чем Серджио, но время стены может быть намного быстрее из-за того, что «медленные» части работают в C.источник
arr.find_all {|e| arr.rindex(e) != arr.index(e) }.uniq
arr.detect.with_index { |e, idx| idx != arr.rindex(e) }
. Использованиеwith_index
должно устранить необходимость в первомindex
поиске.detect
находит только один дубликат.find_all
найду их всех:источник
count
каждый элемент в массиве. (Например, счетный хэш гораздо эффективнее; например,h = {"A"=>2, "B"=>2, "C"=> 1 }
h.select { |k,v| v > 1 }.keys #=> ["A", "B"]
Вот еще два способа найти дубликат.
Используйте набор
Используйте
select
вместо,find
чтобы вернуть массив всех дубликатов.использование
Array#difference
Отбросьте,
.first
чтобы вернуть массив всех дубликатов.Оба метода возвращаются,
nil
если нет дубликатов.Я предложил
Array#difference
добавить его в ядро Ruby. Больше информации в моем ответе здесь .эталонный тест
Давайте сравним предложенные методы. Для начала нам нужен массив для тестирования:
и метод для запуска тестов для разных тестовых массивов:
Я не включил ответ @ JjP, потому что должен быть возвращен только один дубликат, и когда его / ее ответ изменяется, чтобы он соответствовал предыдущему ответу @ Naveed. Я также не включил ответ @ Marin, который, хотя и был опубликован до ответа @ Naveed, возвращал все дубликаты, а не только один (незначительный момент, но нет смысла оценивать оба, так как они идентичны, когда возвращают только один дубликат).
Я также изменил другие ответы, которые возвращали все дубликаты, чтобы вернуть только первый найденный, но это не должно было существенно повлиять на производительность, так как они вычислили все дубликаты перед выбором одного.
Результаты для каждого теста перечислены от самого быстрого до самого медленного:
Сначала предположим, что массив содержит 100 элементов:
Теперь рассмотрим массив с 10000 элементов:
Обратите внимание, что это
find_a_dup_using_difference(arr)
было бы намного эффективнее, если бы ониArray#difference
были реализованы на C, что было бы в случае добавления в ядро Ruby.Вывод
Многие из ответов являются разумными, но использование набора является лучшим выбором . Он самый быстрый в случаях средней сложности, самый быстрый в самых сложных и только в вычислительно тривиальных случаях - когда ваш выбор все равно не имеет значения - его можно победить.
Один очень особый случай, в котором вы можете выбрать решение Криса, будет, если вы захотите использовать метод для раздельного дублирования тысяч небольших массивов и ожидать, что дубликат будет найден, как правило, менее чем в 10 элементах. Это будет немного быстрее поскольку это позволяет избежать небольших дополнительных затрат на создание набора.
источник
Увы большинство ответов
O(n^2)
.Вот
O(n)
решение,В чем сложность этого?
O(n)
и ломается в первом матчеO(n)
память, но только минимальное количествоТеперь, в зависимости от того, как часто встречаются дубликаты в вашем массиве, время выполнения может стать еще лучше. Например, если размер массива
O(n)
был выбран из совокупностиk << n
различных элементов, становитсяO(k)
сложнее только время выполнения и пространство , однако более вероятно, что исходный плакат проверяет входные данные и хочет убедиться, что нет дубликатов. В этом случае и время выполнения, и сложность памяти,O(n)
поскольку мы ожидаем, что элементы не будут иметь повторений для большинства входных данных.источник
У объектов Ruby Array отличный метод
select
.Первая форма - это то, что вас здесь интересует. Позволяет выбирать объекты, которые проходят тест.
У объектов Ruby Array есть другой метод
count
.В этом случае вас интересуют дубликаты (объекты, которые появляются в массиве более одного раза). Соответствующий тест есть
a.count(obj) > 1
.Если
a = ["A", "B", "C", "B", "A"]
тоВы заявляете, что хотите только один объект. Так что выбирай один.
источник
["A", "B", "B", "A"]
.uniq
на массив.count
для каждого элемента массива, что является расточительным и ненужным. Смотрите мой комментарий на ответ JjP.find_all () возвращает
array
содержащий все элементы,enum
для которыхblock
нетfalse
.Чтобы получить
duplicate
элементыИли дубликаты
uniq
элементовисточник
Как то так будет работать
То есть поместите все значения в хеш, где ключ - это элемент массива, а значение - это число вхождений. Затем выберите все элементы, которые встречаются более одного раза. Легко.
источник
Я знаю, что эта тема конкретно о Ruby, но я приземлился здесь в поисках того, как сделать это в контексте Ruby on Rails с ActiveRecord, и подумал, что тоже поделюсь своим решением.
Вышеприведенное возвращает массив всех адресов электронной почты, которые дублируются в таблице базы данных этого примера (которая в Rails будет "active_record_classes").
источник
Это
O(n)
процедура.В качестве альтернативы вы можете сделать одну из следующих строк. Также O (n), но только одна итерация
источник
Вот мой взгляд на большой набор данных - такой как устаревшая таблица dBase, чтобы найти повторяющиеся части
источник
источник
each_with_object
твой друг!источник
Этот код вернет список дублированных значений. Хэш-ключи используются как эффективный способ проверки того, какие значения уже были замечены. В зависимости от того, было ли замечено значение, исходный массив
ary
разбивается на 2 массива: первый содержит уникальные значения, а второй - дубликаты.Вы можете дополнительно сократить его - хотя и за счет немного более сложного синтаксиса - до этой формы:
источник
Полученные результаты
источник
Если вы сравниваете два разных массива (вместо одного с самим собой), очень быстрый способ - использовать оператор пересечения,
&
предоставляемый классом Ruby Array .источник
Мне нужно было выяснить, сколько было дубликатов и что они были, поэтому я написал функциональную сборку из того, что Naveed опубликовал ранее:
источник
давайте продемонстрируем в реализации кода
Теперь вызовите метод дублирования и выведите результат возврата -
источник
[1,2,3].uniq!.nil? => true
[1,2,3,3].uniq!.nil? => false
Обратите внимание, что вышесказанное является разрушительным
источник