如何计算数组中的连续数字?

时间:2019-05-07 03:10:39

标签: ruby-on-rails arrays ruby

如果我有一个数组:

array = [1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2]

我希望能够识别长度大于3的连续匹配数字,并映射连续数字的起始索引。上面的数组的示例输出为:

consecutive_numbers = [
  {starting_index: 1, value: 2, length: 4},
  {starting_index: 10, value: 3, length: 4},
  {starting_index: 14, value: 2, length: 7}
]

值可以相同,但连续的序列必须互斥。看到有2个散列,其值为2,但是它们的起始索引不同。

到目前为止我的尝试...看起来像这样:

array.each_cons(3).with_index.select{|(a,b,c), i| 
  [a,b,c].uniq.length == 1
}

但这将返回:

[[[2, 2, 2], 1], [[2, 2, 2], 2], [[1, 1, 1], 7], [[3, 3, 3], 10], [[3, 3, 3], 11], [[2, 2, 2], 14], [[2, 2, 2], 15], [[2, 2, 2], 16], [[2, 2, 2], 17], [[2, 2, 2], 18]]

但这会返回重叠的结果。

5 个答案:

答案 0 :(得分:4)

array.each_with_index.
      chunk(&:first).
      select { |_,a| a.size > 3 }.
      map { |n,a| { starting_index: a.first.last, value: n, length: a.size } }
  #=> [{:starting_index=> 1, :value=>2, :length=>4},
  #    {:starting_index=>10, :value=>3, :length=>4},
  #    {:starting_index=>14, :value=>2, :length=>7}] 

步骤如下。

e = array.each_with_index.chunk(&:first)
  #=> #<Enumerator: #<Enumerator::Generator:0x00005b1944253c18>:each> 

我们可以将此枚举器转换为数组,以查看它将生成的元素并传递到其块。

e.to_a
  #=> [[1, [[1, 0]]],
  #    [2, [[2, 1], [2, 2], [2, 3], [2, 4]]],
  #    [5, [[5, 5], [5, 6]]],
  #    [1, [[1, 7], [1, 8], [1, 9]]],
  #    [3, [[3, 10], [3, 11], [3, 12], [3, 13]]],
  #    [2, [[2, 14], [2, 15], [2, 16], [2, 17], [2, 18], [2, 19], [2, 20]]]] 

继续

c = e.select { |_,a| a.size > 3 }
  #=> [[2, [[2, 1], [2, 2], [2, 3], [2, 4]]],
  #    [3, [[3, 10], [3, 11], [3, 12], [3, 13]]],
  #    [2, [[2, 14], [2, 15], [2, 16], [2, 17], [2, 18], [2, 19], [2, 20]]]] 
c.map { |n,a| { starting_index: a.first.last, value: n, length: a.size } }
  #=> [{:starting_index=> 1, :value=>2, :length=>4},
  #    {:starting_index=>10, :value=>3, :length=>4},
  #    {:starting_index=>14, :value=>2, :length=>7}] 

这是另一种方式。

array.each_with_index.with_object([]) do |(n,i),arr|
  if arr.any? && arr.last[:value] == n
    arr.last[:length] += 1
  else
    arr << { starting_index: i, value: n, length: 1 }
  end
end.select { |h| h[:length] > 3 }
  #=> [{:starting_index=> 1, :value=>2, :length=>4},
  #    {:starting_index=>10, :value=>3, :length=>4},
  #    {:starting_index=>14, :value=>2, :length=>7}]     

答案 1 :(得分:2)

您可以chunk_while的每对元素相等:

p array.chunk_while { |a, b| a == b }.to_a
# [[1], [2, 2, 2, 2], [5, 5], [1, 1, 1], [3, 3, 3, 3], [2, 2, 2, 2, 2, 2, 2]]

选择具有3个或更多元素的数组。

在那之后,使用then,您可以屈服自我,因此您可以访问数组数组,可以用来获取starting_index

[1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2].chunk_while(&:==).then do |this|
  this.each_with_object([]).with_index do |(e, memo), index|
    memo << { starting_index: this.to_a[0...index].flatten.size, value: e.first, length: e.size }
  end
end.select { |e| e[:length] > 3 }

# [{:starting_index=>1, :value=>2, :length=>4},
#  {:starting_index=>10, :value=>3, :length=>4},
#  {:starting_index=>14, :value=>2, :length=>7}]

对于starting_index,您将元素获取到当前索引(不包括在内),将它们展平,并获得元素总数。

由于数组中的每个数组具有相同的元素,因此该值可以是任意值,即长度,即“主”数组中当前数组的长度。

答案 2 :(得分:2)

这是另一个选择。


array
     .zip(0..)
     .slice_when { |a, b| a.first != b.first }
     .map { |a| { starting_index: a.first.last, value: a.first.first, length: a.size } }
     .reject { |h| h[:length] < 3 }

#=> [{:starting_index=>1, :value=>2, :length=>4}, {:starting_index=>7, :value=>1, :length=>3}, {:starting_index=>10, :value=>3, :length=>4}, {:starting_index=>14, :value=>2, :length=>7}]

答案 3 :(得分:0)

最明显(也是最快)的方法是遍历数组并手动计算所有内容:

array = [1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2]
array_length_pred = array.length.pred

consecutive_numbers = []

starting_index = 0
value = array.first
length = 1

array.each_with_index do |v, i|
  if v != value || i == array_length_pred
    length += 1 if i == array_length_pred && value == v

    if length >= 3
      consecutive_numbers << {
        starting_index: starting_index,
        value: value,
        length: length
      }
    end

    starting_index = i
    value = v
    length = 1
    next
  end

  length += 1
end

p consecutive_numbers

# [{:starting_index=>1, :value=>2, :length=>4},
# {:starting_index=>7, :value=>1, :length=>3},
# {:starting_index=>10, :value=>3, :length=>4},
# {:starting_index=>14, :value=>2, :length=>7}]

答案 4 :(得分:0)

您可以改为使用字符串。

在这里,我将数组强制为字符串:

input_sequence = [1,2,2,2,2,5,5,1,1,1,3,3,3,3,2,2,2,2,2,2,2].join

我使用正则表达式对连续字符进行分组:

groups = input_sequence.gsub(/(.)\1*/).to_a
#=> ["1", "2222", "55", "111", "3333", "2222222"]

现在,我可以在输入字符串中以子字符串的形式搜索组:

groups.map do |group|
  {
    starting_index: input_sequence.index(group), 
    value: group[0].to_i,
    length: group.length
  }
end.reject { |group| group[:length] <= 3 }

#=> [{:starting_index=>1, :value=>2, :length=>4},
     {:starting_index=>7, :value=>1, :length=>3},
     {:starting_index=>10, :value=>3, :length=>4},
     {:starting_index=>14, :value=>2, :length=>7}]

这里还有改进的空间-我正在为一个对象创建许多中间对象-但我想我会提供另一种方法。