Let's say I have the following array and I would like to get rid of contiguous duplicates:
假设我有以下数组,我想摆脱连续的重复:
arr = [1,1,1,4,4,4,3,3,3,3,5,5,5,1,1,1]
I would like to get the following:
我想得到以下内容:
=> [1,4,3,5,1]
It would be great if there's something simpler and more efficient than my solutions (or variants thereof):
如果有比我的解决方案(或其变体)更简单,更有效的东西,那将是很棒的:
(arr + [nil]).each_cons(2).collect { |i| i[0] != i[1] ? i[0] : nil }.compact
or
要么
(arr + [nil]).each_cons(2).each_with_object([]) {
|i, memo| memo << i[0] unless i[0] == i[1]
}
EDIT: It looks like @ArupRakshit's solution below is very simple. I'm still looking for better efficiency than my solution.
编辑:看起来@ ArupRakshit的解决方案非常简单。我仍然在寻找比我的解决方案更好的效率。
EDIT:
编辑:
I'll be benchmarking responses as they come:
我会在回复时对响应进行基准测试:
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
Benchmark results:
基准测试结果:
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%
If we use:
如果我们使用:
arr = 10000.times.collect { rand(4) + 1 } # less likelihood of repetition
We get:
我们得到:
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
2 个解决方案
#1
14
Do as below using Enumerable#chunk
:
使用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
UPDATE
as per @abdo's comment, here is another choice :
根据@ abdo的评论,这是另一种选择:
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 }
#2
4
The less elegant yet most efficient solution:
不太优雅但最有效的解决方案:
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)
#1
14
Do as below using Enumerable#chunk
:
使用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
UPDATE
as per @abdo's comment, here is another choice :
根据@ abdo的评论,这是另一种选择:
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 }
#2
4
The less elegant yet most efficient solution:
不太优雅但最有效的解决方案:
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)