12127


`[0,0,1,1,1,1,0,0,0,1,1,1,1,1,0,0]`

とあった時、1の続いている区間を調べたいです。

たとえばこの時、  
`2..5, 9..13` ということが知りたいです。

`each_with_index`でflag立ててやる方法は思いつきましたが、何かスマートな方法はありますか？


CC BY-SA 3.0
2015-07-07T13:03:19.810
12124
2015-07-07T20:42:19.153
2015-07-07T20:42:19.153
85
9596
question
ruby
配列内で特定の値が続いている区間を調べたい


フラグを立てないというと、こんな感じでしょうか。

[code]

    [0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0].
      each_with_index.chunk {|x, i| x == 1 }.select {|b, a| b}.
      map {|b, a| [a.first[1], a.last[1]] }
    #=> [[2, 5], [9, 13]]
    
[/code]


CC BY-SA 3.0
2015-07-07T15:55:58.433
12127
2015-07-07T15:55:58.433
4010
12124
answer


`search_ranges`メソッドを作ってみました。  
テスト付きのコードはこちらです。

[code]

    require 'minitest/autorun'
    
    def search_ranges(array, target)
      array
        .map.with_index{|obj, index| index if obj == target }
        .chunk{|index| index.nil? }
        .map{|not_target, indicies| indicies.first..indicies.last unless not_target }
        .compact
    end
    
    describe 'search_ranges' do
      let(:array) { [0,0,1,1,1,1,0,0,0,1,1,1,1,1,0,0] }
      it { search_ranges(array, 1).must_equal [2..5, 9..13] }
      it { search_ranges(array, 0).must_equal [0..1, 6..8, 14..15] }
    end
    
[/code]

何をやっているのか分かりづらい場合はこちらのコメント付きのコードをどうぞ。

[code]

    array
      .map.with_index{|obj, index| index if obj == target } # => [nil, nil, 2, 3, 4, 5, nil, nil, nil, 9, 10, 11, 12, 13, nil, nil]
      .chunk{|index| index.nil? } # => [[true, [nil, nil]], [false, [2, 3, 4, 5]], [true, [nil, nil, nil]], [false, [9, 10, 11, 12, 13]], [true, [nil, nil]]]
      .map{|not_target, indicies| indicies.first..indicies.last unless not_target } # => [nil, 2..5, nil, 9..13, nil]
      .compact # => [2..5, 9..13]
    
[/code]


CC BY-SA 3.0
2015-07-07T20:09:02.633
12129
2015-07-07T20:16:54.543
2015-07-07T20:16:54.543
85
85
12124
answer

[code]

    a = [0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0]
    
    pos = 0
    info = {0 => [], 1 => []}
    a.each.chunk{|x| x}.each{|v, array|
        info[v] << [pos, pos + array.size - 1]
        pos += array.size
    }
    p info
    
[/code]

実行すると次の出力が得られます。

[code]

    {0=>[[0, 1], [6, 8], [14, 15]], 1=>[[2, 5], [9, 13]]}
    
[/code]

0 が 1..1, 6..8 にあり  
1 が 2..5, 9..13 にある ことが得られています。


CC BY-SA 3.0
2015-07-07T20:42:10.640
12130
2015-07-07T20:42:10.640
2987
12124
answer