Remove Adjacent Identical Elements in a Ruby Array

Remove adjacent identical elements in a Ruby Array?

For the simplest, leanest solution, you could use the method Enumerable#chunk:

a.chunk(&:itself).map(&:first)

The itself method is Ruby 2.2+. Use {|n| n} if you are stuck in an older Ruby, or my backports gems.
It was introduced in Ruby 1.9.2. If you're unlucky enough to be using older rubies, you could use my backports gem and require 'backports/1.9.2/enumerable/chunk'.

Remove sequential duplicates from Ruby array

Do as below using Enumerable#chunk :

arr = [1,1,1,4,4,4,3,3,3,3,5,5,5,1,1,1]
arr.chunk { |e| e }.map(&:first)
# => [1, 4, 3, 5, 1]
# if you have only **Fixnum**, something magic
arr.chunk(&:to_i).map(&:first)
# => [1, 4, 3, 5, 1]

UPDATE

as per @abdo's comment, here is another choice :

arr.join.squeeze.chars.map(&:to_i)
# => [1, 4, 3, 5, 1]

another choice

arr.each_with_object([]) { |el, a| a << el if a.last != el }

Remove adjacent duplicate substrings

s = "i sat down to write an article an article this morning but found that i i could make no progress"

max = s.scan(/\S+/).length
# => 20
1.upto(max).each_with_object(s) do
|n, s| s.gsub!(/((?:\b\s*\S+){#{n}})\1/, '\1')
end
# => "i sat down to write an article this morning but found that i could make no progress"

By the way,

"I like to move it move it, I like to move it move it"

will result in:

"I like to move it, I like to move it"

not:

"I like to move it"

as you mentioned in the comment because there is no adjacent repetition
beyond the string above (watch out for the comma and the space).

How to remove duplicate contiguous elements in an array?

I know you can do it with chunk_while and map, but there might be other ways:

[1,1,1,2,2,3,4,1,2].chunk_while { |e, f| e == f }.map(&:first)
# [1, 2, 3, 4, 1, 2]

With chunk_while you split the array by chunks whenever the block is evaluated to true, for that the block yields two variables, the "element before" and the "element after", that's to say, for every iteration you're going to get this:

[1, 1]
[1, 1]
[1, 2]
[2, 2]
[2, 3]
[3, 4]
[4, 1]
[1, 2]

After applying the logic in the proc, it'll chunk the receiver whenever the first and second yielded elements are equal, and you get:

[[1, 1, 1], [2, 2], [3], [4], [1], [2]]

After that you can map that result to get only one element from each array - there are many ways, but first is enough.

The chunk_while proc can also be shortened to (&:==), leaving as [1,1,1,2,2,3,4,1,2].chunk_while(&:==).map(&:first).

Similarly and just out of curiosity, you can use slice_when and last to save 2 characters: [1,1,1,2,2,3,4,1,2].slice_when(&:!=).map(&:last).

Ruby count adjacent duplicate elements in array

%w[a b c a a b b c c c].chunk{|e| e}.map{|_, v| v.length}.max #=> 3

Remove adjacent duplicate substrings

s = "i sat down to write an article an article this morning but found that i i could make no progress"

max = s.scan(/\S+/).length
# => 20
1.upto(max).each_with_object(s) do
|n, s| s.gsub!(/((?:\b\s*\S+){#{n}})\1/, '\1')
end
# => "i sat down to write an article this morning but found that i could make no progress"

By the way,

"I like to move it move it, I like to move it move it"

will result in:

"I like to move it, I like to move it"

not:

"I like to move it"

as you mentioned in the comment because there is no adjacent repetition
beyond the string above (watch out for the comma and the space).

Remove next elements in array with ruby

Here is how I would solve the problem:

def compress(arr)
return arr unless idx = arr.rindex {|e| e == 0 || e == 1}
value = arr[idx]
method_options = [:even?,:odd?]
arr[idx..-1].drop_while do |n|
n.public_send(method_options[value])
end.tap {|a| a.unshift(value) if value.zero? }
end

First we find index of the last occurrence of 0 or 1 using Array#rindex. If none then return the Array.

Then we get the value at that index.

Then we use Array#[] to slice off the tail end of the Array starting at the index.

Then drop all the consecutive adjacent :even? or :odd? numbers respective to the value (0 or 1) using Array#drop_while.

Finally if the value is 0 we place it back into the front of the Array before returning.

Examples

compress([3, 2, 0]) 
#=> [0]
compress([2, 0, 4, 6, 7])
#=> [0,7]
compress([2, 0, 4, 1, 3, 6])
#=> [6]
compress([3, 2, 0, 4, 1, 3, 6, 8, 5])
#=> [6,8,5]
compress([4, 5, 6])
#=> [4,5,6]
compress([0])
#=> [0]
compress([1])
#=> []

If your goal was to be mutative, as your question and gist seem to suggest, I honestly would not change what I have but rather go with:

def compress!(arr)
arr.replace(compress(arr))
end

For example

a = [3, 2, 0, 4, 1, 3, 6, 8, 5]
a == compress!(a)
#=> true
a
#=> [6,8,5]


Related Topics



Leave a reply



Submit