Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you merge consecutive repeating elements in an array?

Tags:

ruby

I need to merge consecutive repeating elements in an array, such that

[1, 2, 2, 3, 1]

becomes

[1, 2, 3, 1]

#uniq doesn't work for this purpose. Why? Because #uniq will produce this:

[1, 2, 3]
like image 652
dan Avatar asked Jan 02 '11 01:01

dan


3 Answers

There is a abstraction in the core that pretty much does the job, Enumerable#chunk:

xs = [1, 2, 2, 3, 3, 3, 1]
xs.chunk { |x| x }.map(&:first)
#=> [1, 2, 3, 1]
like image 195
tokland Avatar answered Nov 16 '22 10:11

tokland


def remove_consecutive_duplicates(xs)
  [xs.first] + xs.each_cons(2).select do |x,y|
    x != y
  end.map(&:last)
end

remove_consecutive_duplicates([1, 2, 2, 3, 1])
#=> [1,2,3,1]

This returns a new array like uniq does and works in O(n) time.

like image 43
sepp2k Avatar answered Nov 16 '22 09:11

sepp2k


sepp2k's answer is already accepted, but here are some alternatives:

# Because I love me some monkeypatching
class Array
  def remove_consecutive_duplicates_2
    # Because no solution is complete without inject
    inject([]){ |r,o| r << o unless r.last==o; r }
  end

  def remove_consecutive_duplicates_3
    # O(2n)
    map.with_index{ |o,i| o if i==0 || self[i-1]!=o }.compact
  end

  def remove_consecutive_duplicates_4
    # Truly O(n)
    result = []
    last   = nil
    each do |o|
      result << o unless last==o
      last = o
    end
    result
  end
end

And although performance is not everything, here are some benchmarks:

Rehearsal --------------------------------------------
sepp2k     2.740000   0.010000   2.750000 (  2.734665)
Phrogz_2   1.410000   0.000000   1.410000 (  1.420978)
Phrogz_3   1.520000   0.020000   1.540000 (  1.533197)
Phrogz_4   1.000000   0.000000   1.000000 (  0.997460)
----------------------------------- total: 6.700000sec

               user     system      total        real
sepp2k     2.780000   0.000000   2.780000 (  2.782354)
Phrogz_2   1.450000   0.000000   1.450000 (  1.440868)
Phrogz_3   1.530000   0.020000   1.550000 (  1.539190)
Phrogz_4   1.020000   0.000000   1.020000 (  1.025331)

Benchmarks run on removing duplicates from orig = (0..1000).map{ rand(5) } 10,000 times.

like image 3
Phrogz Avatar answered Nov 16 '22 08:11

Phrogz