使用ruby识别阵列上的运行

时间:2014-05-13 20:48:31

标签: ruby pattern-matching

如果我们有一个数组

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

我们如何识别给定数字的运行(具有相同值的连续数量)? 例如:

run_pattern_for(array, 0) -> 2
run_pattern_for(array, 3) -> 1
run_pattern_for(array, 1) -> 1
run_pattern_for(array, 2) -> 0

没有2的运行,因为没有两个连续的显示。 有一次运行3,因为只有一个将树作为连续数字显示。

4 个答案:

答案 0 :(得分:11)

尝试:

class Array
  def count_runs(element)
    chunk {|n| n}.count {|a,b| a == element && b.length > 1}
  end
end

a = [1, 1, 0, 0, 2, 3, 0, 0, 0, 3, 3, 3 ]
a.count_runs 0   #=> 2
a.count_runs 3   #=> 1
a.count_runs 1   #=> 1
a.count_runs 2   #=> 0

答案 1 :(得分:3)

我同意@BroiSatse,Enumerable#chunk应该在这里使用,但我想展示如何使用方法Enumerator#next和{{3}直接使用枚举器来解决这个问题}。

<强>代码

def count_em(array)
  return [] if array.empty?
  h = Hash.new(0)
  enum = array.each
  loop do
    x = enum.next
    if x == enum.peek
      h[x] += 1
      enum.next until (enum.peek != x)
    else
      h[x] = 0 unless h.key?(x)
    end
  end
  h
end  

示例

array = [1, 1, 0, 0, 2, 3, 0, 0, 0, 3, 3, 3 ]
count_em(array) #=> {1=>1, 0=>2, 2=>0, 3=>1}

<强>解释

假设

array = [1, 1, 1, 0, 2, 2]
h = Hash.new(0)
enum = array.each
  #=> #<Enumerator: [1, 1, 1, 0, 2, 2]:each>

x = enum.next #=> 1
enum.peek     #=> 1

所以x == enum.peek #=> true,意味着至少有两个1的运行,所以希望执行:

h[x] += 1 #=> h[1] += 1

表示

h[1] = h[1] + 1

由于h在等式右侧没有键1h[x]设置为零,因此我们在创建哈希时建立了默认值。因此,哈希h现在是{ 1=>1 }。现在我们需要在运行中枚举并丢弃更多的1:

enum.next until (enum.peek != x)

enum.next #=> 1
enum.peek #=> 1
enum.next #=> 1
enum.peek #=> 0

现在回到循环的顶部:

x = enum.next #=> 0
enum.peek     #=> 2

(x == enum.peek) => (0 == 2) => falseh.key?(x) => false以来,我们设置了

h[0] = 0

,哈希现在是{ 1=>1, 0=>0 }。再次回到循环的顶部,

x = enum.next #=> 2
enum.peek     #=> 2

(x == enum.peek) => (2 == 2) => true开始,我们执行:

h[2] += 1 #=> 1

所以现在h => {1=>1, 0=>0, 2=>1}。现在我们执行

x = enum.next #=> 2
enum.peek #=> StopIteration: iteration reached an end

例外由Enumerator#peek获救。也就是说,引发StopIteration错误是摆脱循环的一种方法,导致方法的最后一行被执行并返回:

h #=> {1=>1, 0=>0, 2=>1}

(请注意,此结果与上述示例中的结果不同,因为它适用于不同的array。)

答案 2 :(得分:2)

Ruby 2.2,在发布此问题大约七个月后发布,它为我们提供了一个在此处有应用的方法,Enumerable#slice_when

array.slice_when { |i,j| i != j }.each_with_object(Hash.new(0)) { |a,h|
  h[a.first] += (a.size > 1) ? 1 : 0 }  
  #=> {1=>1, 0=>2, 2=>0, 3=>1}

答案 3 :(得分:0)

这是一项简单的任务;我有两种不同的方法:

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

hash = Hash[array.group_by { |e| e }.map{ |k, v| [k, v.size] }] 
# => {1=>2, 0=>5, 2=>1, 3=>4}

hash = Hash.new{ |h,k| h[k] = 0 }
array.each { |e| hash[e] += 1 }
hash # => {1=>2, 0=>5, 2=>1, 3=>4}

一旦你有了哈希,剩下的就很容易了:

hash[0] # => 5
hash[1] # => 2
hash[2] # => 1
hash[3] # => 4

如果您可以请求对数组中不存在的数字进行计数,并希望使用数字响应代替nil,请使用以下内容:< / p>

Integer(hash[4]) # => 0

Integer(...)为您将nil转换为0

在上面的第一个示例中,group_by将执行繁重的工作,并导致:

array.group_by { |e| e } # => {1=>[1, 1], 0=>[0, 0, 0, 0, 0], 2=>[2], 3=>[3, 3, 3, 3]}

map语句只是将数组转换为其大小。