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]
UPDATEas per @abdo's comment, here is another choice :
arr.join.squeeze.chars.map(&:to_i)
# => [1, 4, 3, 5, 1]
another choicearr.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
How to (Massively) Reduce The Number of SQL Queries in Rails App
Ruby: How to Convert an Array of Data to Hash and to JSON Format
How to Handle Serialized Edit Fields in an Active Admin Resource
Reading Files in a Zip Archive, Without Unzipping The Archive
Can't Install Debugger Gem - Rails - MAC Osx Mavericks
Many to Many Table with an Extra Column in Rails
Issue Installing Gems on Windows 7 with Proxy
Why The Unit Test Frameworks in Fortran Rely on Ruby Instead of Fortran Itself
How to Click on Specific Element in Canvas by Its Coordinates (Using Webdriver)
Shellwords.Shellescape Implementation for Ruby 1.8
How to Reference a Local Gem from a Ruby Script
How to "Break" Out of a Case...While in Ruby
How to Mass Rename Files in Ruby
Hw Impossibility: "Create a Rock Paper Scissors Program in Ruby Without Using Conditionals"
Rails Has_Many Through Form with Additional Attributes
Regex That Matches Valid Ruby Local Variable Names