基于ruby中现有数组中元素之间的关系创建一个新数组

时间:2017-06-14 01:51:33

标签: ruby

我有以下数组:

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]

4 个答案:

答案 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::INFINITYe.peek引发StopIteration异常,Kernel#loop通过突破循环来处理。