Подтвердить что ты не робот

Удалить последовательные дубликаты из массива Ruby

Скажем, у меня есть следующий массив, и я хотел бы избавиться от непрерывных дубликатов:

arr = [1,1,1,4,4,4,3,3,3,3,5,5,5,1,1,1]

Я хотел бы получить следующее:

=> [1,4,3,5,1]

Было бы здорово, если бы было что-то более простое и эффективное, чем мои решения (или их варианты):

(arr + [nil]).each_cons(2).collect { |i| i[0] != i[1] ? i[0] : nil }.compact

или

(arr + [nil]).each_cons(2).each_with_object([]) { 
   |i, memo| memo << i[0] unless i[0] == i[1] 
 }

EDIT: Похоже, что решение @ArupRakshit ниже очень просто. Я все еще ищу лучшую эффективность, чем мое решение.

EDIT:

Я буду тестировать ответы, когда они придут:

require 'fruity'
arr = 10000.times.collect { [rand(5)] * (rand(4) + 2) }.flatten

compare do
  abdo { (arr + [nil]).each_cons(2).collect { 
    |i| i[0] != i[1] ? i[0] : nil }.compact 
  }
  abdo2 { 
          (arr + [nil]).each_cons(2).each_with_object([]) { 
           |i, memo| memo << i[0] unless i[0] == i[1] 
          }
  }
  arup { arr.chunk(&:to_i).map(&:first) }
  arupv2 { arr.join.squeeze.chars.map(&:to_i) }
  agis {
    i = 1
    a = [arr.first]

    while i < arr.size
      a << arr[i] if arr[i] != arr[i-1]
      i += 1
     end
    a
  }
  arupv3 { arr.each_with_object([]) { |el, a| a << el if a.last != el } }
end

Результаты тестов:

agis is faster than arupv3 by 39.99999999999999% ± 10.0%
arupv3 is faster than abdo2 by 1.9x ± 0.1
abdo2 is faster than abdo by 10.000000000000009% ± 10.0%
abdo is faster than arup by 30.000000000000004% ± 10.0%
arup is faster than arupv2 by 30.000000000000004% ± 10.0%

Если мы используем:

arr = 10000.times.collect { rand(4) + 1 } # less likelihood of repetition

Получаем:

agis is faster than arupv3 by 19.999999999999996% ± 10.0%
arupv3 is faster than abdo2 by 1.9x ± 0.1
abdo2 is similar to abdo
abdo is faster than arupv2 by 2.1x ± 0.1
arupv2 is similar to arup
4b9b3361

Ответ 1

Сделайте, как показано ниже, 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

в соответствии с комментарием @abdo, вот еще один выбор:

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

другой выбор

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

Ответ 2

Менее элегантное, но наиболее эффективное решение:

require 'benchmark'

arr = [1,1,1,4,4,4,3,3,3,3,5,5,5,1,1,1]

GC.disable
Benchmark.bm do |x|
  x.report do
    1_000_000.times do
      i = 1
      a = [arr.first]

      while i < arr.size
        a << arr[i] if arr[i] != arr[i-1]
        i += 1
      end
    end
  end
end
#      user     system      total        real
# 1.890000   0.010000   1.900000 (  1.901702)

GC.enable; GC.start; GC.disable

Benchmark.bm do |x|
  x.report do
    1_000_000.times do
      (arr + [nil]).each_cons(2).collect { |i| i[0] != i[1] ? i[0] : nil }.compact
    end
  end
end
#      user     system      total        real
# 6.050000   0.680000   6.730000 (  6.738690)