Есть ли способ удалить определенные дубликаты в массиве с помощью Ruby? Пример массива:
["hello", "removeme", "removeme", "hello", "testing"]
Я хочу удалить только дубликаты "removeme"
в этом массиве. Желаемый результат:
["hello", "removeme", "hello", "testing"]
Есть ли такой метод, чтобы получить желаемый результат? ["hello", "removeme", "removeme", "hello", "testing"].uniq('removeme')
Привет @CarySwoveland, желаемый результат будет ["hello", "removeme", "hello", "testing"]
, я хочу удалить дубликаты с определенной строкой, например, выше, я хочу сохранить одно значение removeme
в массиве. Я не могу использовать uniq
, потому что это повлияет на значение hello
в массиве
Вы сказали: «Я хочу сохранить одно значение removeme
в массиве», но я спросил, какое из них оставить. Высказывание «желаемый результат будет ["hello", "removeme", "hello", "testing"]
» предполагает, что вы хотите сохранить первый экземпляр, но может случиться так, что вам все равно, какой из них будет сохранен. Вам нужно уточнить это. При уточнении вопросов всегда лучше отредактировать свой вопрос, а не уточнять его в комментариях, так как вопросы должны быть отдельными — от читателей не требуется читать все комментарии, чтобы понять вопрос.
Это чисто Ruby-проблема, поэтому у вас не должно быть тегов Rails.
Можно использовать следующий метод.
def removem(arr, to_remove)
i = arr.index(to_remove)
i.nil? ? arr : (arr - [to_remove]).insert(i, to_remove)
end
Предположим, что данный массив выглядит следующим образом.
arr = ["hello", "removeme", "hello", "goodbye", "removeme", "testing"]
Затем,
removem(arr, "removeme")
#=> ["hello", "hello", "goodbye", "testing", "removeme"]
removem(arr, "goodbye")
#=> ["hello", "removeme", "hello", "goodbye", "removeme", "testing"]
removem(arr, "missing")
#=> ["hello", "removeme", "hello", "goodbye", "removeme", "testing"]
В каждом примере arr
не меняется.
Как насчет отклонения всего, кроме первого появления слова, как это.
def uniq_word(array, word)
return array unless first = array.index(word)
array.reject.with_index { |elem, index| index > first && elem == word }
end
array = ["hello", "removeme", "removeme", "hello", "testing"]
uniq_word(array, 'removeme')
#=> ["hello", "removeme", "hello", "testing"]
См. Array#index , Enumerator#with_index и Array#reject.
Или вы можете повторить массив и скопировать только первое вхождение в новый массив:
def uniq_word(array, word)
found = false
[].tap do |result|
array.each do |elem|
if elem == word && !found
found = true
next
end
result << elem
end
end
end
array = ["hello", "removeme", "removeme", "hello", "testing"]
uniq_word(array, 'removeme')
#=> ["hello", "removeme", "hello", "testing"]
Видеть:
Вы можете использовать uniq с блоком, который удаляет дубликаты на основе возвращаемого значения блока:
ary = ["hello", "removeme", "removeme", "hello", "testing"]
ary.uniq { |obj| obj.eql?('removeme') || Object.new }
#=> ["hello", "removeme", "hello", "testing"]
Для элементов, равных 'removeme'
, мы возвращаем true
, а для всего остального ('hello'
, 'hello'
и 'testing'
) мы возвращаем новый объект: (обратите внимание на разные идентификаторы объектов)
"hello" → #<Object:0x00007f9ab08590d8>
"removeme" → true
"removeme" → true
"hello" → #<Object:0x00007f9ab0858d68>
"testing" → #<Object:0x00007f9ab08589f8>
Все элементы с одинаковым возвращаемым значением считаются дубликатами, т. е. uniq
будет рассматривать 'removeme'
как дубликат, а все остальное как уникальное, независимо от его фактического значения. Это позволяет сохранить две идентичные строки 'hello'
.
Вместо Object.new
вы также можете использовать индекс элемента:
ary.enum_for(:uniq).with_index { |obj, i| obj.eql?('removeme') || i }
#=> ["hello", "removeme", "hello", "testing"]
enum_for нужен, потому что uniq
без блока возвращает новый массив вместо перечислителя (который в свою очередь нужен для цепочки with_index).
Умный! Я пытался сделать что-то подобное, но споткнулся на последнем шаге. Я думаю, другой способ был бы n = 0; ary.uniq { |obj| obj.eql?('removeme') || n += 1 }
.
@CarySwoveland Я добавил ваш подход с индексом элемента.
Как насчет расширения класса Array путем добавления нового метода.
class Array
def uniq_specific!(targets)
found = {}
targets.each { |target| found[target] = false }
delete_if do |item|
if targets.include?(item)
if found[item]
true
else
found[item] = true
false
end
else
false
end
end
end
end
array = ["hello", "removeme", "removeme", "hello", "world", "world", "testing"]
array.uniq_specific!(["removeme", "hello"])
array # => ["removeme", "hello", "world", "world", "testing"]
Мы определяем новый метод uniq_specific! для класса Array, который принимает массив целевых элементов в качестве параметра. Он перебирает массив и удаляет все вхождения каждого целевого элемента, кроме первого. Затем мы вызываем этот метод для примера массива с целевыми значениями ["removeme", "hello"], что дает желаемый результат.
Я голосую против, потому что удаляются первые экземпляры как "hello"
, так и "removeme"
, а не только последний, но даже если метод был правильным, нет причин загрязнять класс Array
добавлением нового метода.
Как известно, в этом вопросе таких условий не было, я предложил другой вариант реализации.
Каков желаемый результат, если бы массив был
["hello", "removeme", "hello", "testing", "removeme"]
? Я хочу знать, важен ли выбор сохраняемого элемента (здесь первый или второй экземпляр"removeme"
).