Is there a way to remove specific duplicates in an array using Ruby? Example array:
["hello", "removeme", "removeme", "hello", "testing"]
I only want to remove duplicates of "removeme"
on that array. The desired output is:
["hello", "removeme", "hello", "testing"]
Is there a method like this to get the desired output? ["hello", "removeme", "removeme", "hello", "testing"].uniq('removeme')
You can use uniq
with a block which removes duplicates based on the block's return value:
ary = ["hello", "removeme", "removeme", "hello", "testing"]
ary.uniq { |obj| obj.eql?('removeme') || Object.new }
#=> ["hello", "removeme", "hello", "testing"]
For elements equal to 'removeme'
we return true
and for everything else ('hello'
, 'hello'
, and 'testing'
), we return a new object: (note the different object ids)
"hello" → #<Object:0x00007f9ab08590d8>
"removeme" → true
"removeme" → true
"hello" → #<Object:0x00007f9ab0858d68>
"testing" → #<Object:0x00007f9ab08589f8>
All elements with the same return value are considered duplicates, i.e. uniq
will treat 'removeme'
as a duplicate and anything else as unique regardless of its actual value. This allows the two identical 'hello'
strings to remain.
Instead of Object.new
, you could also use the element's index:
ary.enum_for(:uniq).with_index { |obj, i| obj.eql?('removeme') || i }
#=> ["hello", "removeme", "hello", "testing"]
enum_for
is needed, because uniq
without a block returns a new array instead of an enumerator (which in turn is needed for chaining with_index
).
What about rejecting all but the first occurrence of the word, like this.
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"]
See Array#index
, Enumerator#with_index
, and Array#reject
.
Or you could iterate the aray and copy only the first occurance into a new array:
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"]
See:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With