我有以下数组:
a = [1, 2, 6, 10, 11]
我希望返回一个新数组b
,它由相邻元素的总和组成。在这种情况下,返回的数组将是:
b = [3, 21]
即。 a[0]
和a[1]
之间存在差异,因此将它们相加并将3
添加到b
。
a[3]
和a[4]
因人而异,所以请将它们相加并将21
添加到b
。
更新
我犯了一个错误:
a = [1, 2, 6, 10, 11, 12]
它应该返回:
b = [3, 33]
答案 0 :(得分:4)
你可以初始化一个b
变量并使用each_cons
,从数组中取两个连续的元素,然后使用map
,你可以在里面得到每个数组的两个值的总和两个值的减法等于1,因为你得到nil
值然后你可以压缩"映射"结果:
a = [1, 2, 6, 10, 11]
b = a.each_cons(2).map do |value|
value.reduce(:+) if value[1] - value[0] == 1
end.compact
# => [3, 21]
这是一个更新,您可以使用slice_when
并将结果获得的枚举数转换为数组,然后映射具有多个值的每个数组元素的sum
在内部和紧凑,以删除nil
元素:
p arr.slice_when{|a, b| b != a.next}.to_a
# => [[1, 2], [6], [10, 11, 12]]
p arr.slice_when{|a, b| b != a.next}.to_a.map{|e| e.sum if e.size > 1}
# => [3, nil, 33]
p arr.slice_when{|a, b| b != a.next}.to_a.map{|e| e.sum if e.size > 1}.compact
# => [3, 33]
但是使用select
并在最后映射sum
元素时,这看起来更好:
p arr.slice_when{|a, b| b != a.next}.to_a.select{|e| e.size > 1}.map(&:sum)
基准:
arr = [1, 2, 6, 10, 11, 12]
Benchmark.bm do |bm|
bm.report do
iterations.times do
arr.slice_when{|a, b| b != a.next}.to_a.map{|e| e.sum if e.size > 1}.compact
end
end
bm.report do
iterations.times do
arr.slice_when{|a, b| b != a.next}.to_a.select{|e| e.size > 1}.map(&:sum)
end
end
bm.report do
iterations.times do
arr.chunk_while { |a,b| b == a.next }.select{ |a| a.size > 1 }.map{|e| e.reduce(:+)}
end
end
end
user system total real
0.920000 0.010000 0.930000 ( 0.942134)
0.920000 0.010000 0.930000 ( 0.939316)
0.940000 0.010000 0.950000 ( 0.964895)
答案 1 :(得分:3)
您可以使用chunk_while
。如果相邻元素相差1
(使用测试@SebastiánPalma但有abs
),它会“组合”相邻元素。有关这些方法的详细信息,请参阅Ruby documentation。
a.chunk_while { |x,y| (x-y).abs == 1 }.select{ |a| a.size > 1 }.map(&:sum)
#=> [3, 21]
注意:Array#sum
只能在Ruby> = 2.4中使用。否则使用inject(&:+)
:
a.chunk_while {|x,y| (x-y).abs == 1 }.select{|a| a.size > 1}.map {|a| a.inject(&:+)}
a.chunk_while {|x,y| (x-y).abs == 1 } #actually returns an enumerator.
#=> [[1, 2], [6], [10, 11]]
a.chunk_while {|x,y| (x-y).abs == 1 }.select{|a| a.size > 1}
#=> [[1, 2], [10, 11]]
a.chunk_while {|x,y| (x-y).abs == 1 }.select{|a| a.size > 1}.map(&:sum)
#=> [3, 21]
答案 2 :(得分:1)
遍历每个元素,如果sum为nil,则将var'sum'初始化为elem。当elem和next之间的差异为1时,将下一个elem添加到sum并存储在sum中,增加seq,这样我们就知道至少有一个diff为1。
这样做直到diff b / t elem和next不是1,当diff不是1时,如果seq>则将和推送到res数组。 0并将sum重置为nil,将seq重置为0.这只需要O(n)。
a.each_with_object([]).with_index do |(x, res), i|
sum ||= x
if a[i+1] && (x - a[i+1]).abs == 1
seq += 1
sum += a[i+1]
else
res << sum if seq > 0
sum = nil
seq = 0
end
end
答案 3 :(得分:1)
这适用于Ruby v1.9 +。
arr = [1, 2, 6, 6, 10, 11, 12]
arr.drop(1).
each_with_object([[arr.first]]) { |n,a| (a.last.last - n).abs == 1 ?
a.last.push(n) : a.push([n]) }.
reject { |a| a.size == 1 }.
map(&:sum)
#=> [3, 33]
这是一个允许我们跳过步骤reject { |a| a.size == 1 }
的变体。 (我认为这可能会引起人们的兴趣,即使我不认为我会提倡它。)
e = (arr + [Float::INFINITY]).to_enum
a = [[e.next]]
loop do
n = e.next
(a.last.last-n).abs==1 ? a.last.push(n) : (a.push([n]) if (n-e.peek).abs==1)
end
a.map(&:sum)
#=> [3, 33]
当迭代器结束时n #=> Float::INFINITY
,e.peek
引发StopIteration
异常,Kernel#loop通过突破循环来处理。