2017-03-23 3 views
-2

私はarray_of_hashと呼ばれるハッシュの配列を持っている:ruby​​ 2.1.2でハッシュの配列を変更する方法は?

array_of_hash = [ 
{:name=>"1", :address=>"USA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}, 
{:name=>"5", :address=>"UK", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC"}, 
{:name=>"6", :address=>"CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"CD"}, 
{:name=>"29", :address=>"GERMANY", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE"}, 
{:name=>"30", :address=>"CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"FG"} 
] 

私はキー:nameの連続した値でグループにこれらのハッシュを望みます。 :name => "1".sucC#=> "2"のキーがないため、最初のグループは"1"だけです。第2のグループは、"5"および"6"の値を有するハッシュを含む。 3番目のグループは配列内の最後の2つのハッシュで、そのうち:name=>29:name=>30です。

ハッシュの私の希望配列は次のようになります。

[ 
    {:name=>"1", :address=>"USA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}, 
    {:name=>"5-6", :address=>"UK,CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC,CD"}, 
    {:name=>"29-30", :address=>"GERMANY,CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE, FG"}, 
] 

ユースケースII

array_of_hash = [ 
{:name=>"1", :address=>"USA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}, 
{:name=>"2", :address=>"UK", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC"}, 
{:name=>"3", :address=>"CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"CD"}, 
{:name=>"29", :address=>"GERMANY", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE"}, 
{:name=>"30", :address=>"CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"FG"} 
] 
私がこれまでやったどのようなユースケースII

[ 
    {:name=>"1-3", :address=>"USA,UK,CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB,BC,CD"}, 
    {:name=>"29-30", :address=>"GERMANY,CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE, FG"}, 
] 

ため

望ましい結果:

new_array_of_hashes = [] 
new_array_of_hashes << { name: array_of_hashes.map {|h| h[:name].to_i}} << {address: array_of_hashes.map {|h| h[:address]}} << {collection: array_of_hashes.map {|h| h[:collection]}} << {sequence: array_of_hashes.map {|h| h[:sequence]}} 

[{:name=>[1, 5, 6, 29, 30]}, 
{:address=>["USA", "UK", "CANADA", "GERMANY", "CHINA"]}, 
{:collection=> 
[["LAND", "WATER", "OIL", "TREE", "SAND"], 
["LAND", "WATER", "OIL", "TREE", "SAND"], 
["LAND", "WATER", "OIL", "TREE", "SAND"], 
["LAPTOP", "SHIP", "MOUNTAIN"], 
["LAPTOP", "SHIP", "MOUNTAIN"]]}, 
{:sequence=>["AB", "BC", "CD", "DE", "FG"]}] 

私はそれを組み合わせることができます。

+1

どの要素を結合するか、どの要素を別の要素にするかはどのように決定しますか? – moveson

+0

@moveson ':collection'の値が同じ場合、 – kavin

+2

を結合します。その場合、最初の3つの要素をすべて組み合わせてはいけませんか? – moveson

答えて

2

まず、最終的に必要なグループの配列を作成しましょう。 RubyのArray#slice_whenメソッドを使用します。このメソッドは、現在の配列要素と次の配列要素を持つ配列を反復処理し、2つの要素を比較します。私たちの条件は、(整数に変換された)名前が連続していない場合、またはコレクションが同一でない場合、配列をスライスするようRubyに指示します。

>> groups = array_of_hash.slice_when { |i, j| i[:name].to_i + 1 != j[:name].to_i || i[:collection] != j[:collection] }.to_a 

しかし、あなたはルビー2.1を使用しているので、あなたはslice_beforeを使用して、以前の要素を追跡するために、ローカル変数を使用する必要があります。

>> prev = array_of_hash[0] 

し、我々は、配列を反復処理として及び第二のローカル変数をリセットする:documentationあたり、まずローカル変数をプライミングすることによってこれを達成することができる

いずれの場合においても
>> groups = array_of_hash.slice_before { |e| prev, prev2 = e, prev; prev2[:name].to_i + 1 != prev[:name].to_i || prev2[:collection] != prev[:collection] }.to_a 

groups

=> [[{:name=>"1", 
    :address=>"USA", 
    :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], 
    :sequence=>"AB"}], 
[{:name=>"5", 
    :address=>"UK", 
    :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], 
    :sequence=>"BC"}, 
    {:name=>"6", 
    :address=>"CANADA", 
    :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], 
    :sequence=>"CD"}], 
[{:name=>"29", 
    :address=>"GERMANY", 
    :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], 
    :sequence=>"DE"}, 
    {:name=>"30", 
    :address=>"CHINA", 
    :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], 
    :sequence=>"FG"}]] 

結果の配列を取得し、指定したとおりにフォーマットされた新しいハッシュにその要素をマップします。

:nameの場合は、グループの最初と最後の要素を取り、重複を排除するために.uniqを呼び出し、それらをハイフンで結合します。 (単一の要素が存在する場合、joinは単一の要素をそのまま返します)

:collectionの場合、グループの最初の要素にあるコレクションを使用します。

:sequenceについては、グループの各要素のシーケンスをカンマで結合します。 (ここでも、単一の要素は変更されずに返されます。)

>> groups.map { |group| {name: [group.first[:name], group.last[:name]].uniq.join('-'), 
         collection: group.first[:collection], 
         sequence: group.map { |e| e[:sequence] }.join(',') } } 

=> [{:name=>"1", 
    :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], 
    :sequence=>"AB"}, 
{:name=>"5-6", 
    :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], 
    :sequence=>"BC,CD"}, 
{:name=>"29-30", 
    :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], 
    :sequence=>"DE,FG"}] 
+0

ありがとうございますが、私のルビーバージョンは2.1.2です。 'when_slice'は2.2で動作します。 – kavin

+0

アップグレードする方法はありませんか? Ruby 2.1は3歳以上です。コードは 'slice_when'なしでもっと複雑になります。 – moveson

+0

はい、アップグレードできない問題です。 'when_slice'のような同様のメソッドがありますか? – kavin

0
def slice_when(array) 
    big = [] 
    small = [] 
    last_index = array.size - 1 
    (0..last_index).each do |i| 
    small << array[i] 
    if last_index == i || yield(array[i], array[i + 1]) 
     big << small 
     small = [] 
    end 
    end 
    big 
end 

あなたがslice_beforeを使用したくない場合は、これを使用して試すことができます。すでにArrayが返され、Enumuratorは返されないことに注意してください。

0

コード

def aggregate(array_of_hash) 
    array_of_hash.chunk_while { |g,h| h[:name] == g[:name].succ }. 
    flat_map { |a| a.chunk { |g| g[:collection] }.map { |_c,b| combine(b) } } 
end 

def combine(arr) 
    names  = values_for_key(arr, :name) 
    addresses = values_for_key(arr, :address) 
    sequences = values_for_key(arr, :sequence) 
    arr.first.merge { 
    name: names.size==1 ? names.first : "%s-%s" % [names.first, names[-1]], 
    address: addresses.join(','), 
    sequence: sequences.join(',') 
    } 
end 

def values_for_key(arr, key) 
    arr.map { |h| h[key] } 
end 

aggregate(array_of_hash) 
    #=> [{:name=>"1", :address=>"USA", 
    #  :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}, 
    # {:name=>"5-6", :address=>"UK,CANADA", 
    #  :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC,CD"}, 
    # {:name=>"29-30", :address=>"GERMANY,CHINA", 
    #  :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE,FG"}] 

は、ここで第二の例です。

array_of_hash[2][:collection] = ['dog', 'cat', 'pig'] 
    #=> [{:name=>"1", :address=>"USA", 
    #  :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}, 
    # {:name=>"5", :address=>"UK", 
    #  :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC"}, 
    # {:name=>"6", :address=>"CANADA", 
    #  :collection=>["dog", "cat", "pig"], :sequence=>"CD"}, 
    # {:name=>"29-30", :address=>"GERMANY,CHINA", 
    #  :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE,FG"}] 

:collectionの値が異なるため、この例では:name=>"5":name=>"6"付きハッシュをグループ化することができません。問題は、このような状況が発生する可能性があるかどうかについては述べていません。もしそれができないのであれば、コードはまだ正しいですが、以下のように単純化することができます。以下のステップ上記例えば

def aggregate(array_of_hash) 
    array_of_hash.chunk_while { |g,h| h[:name] == g[:name].succ }. 
    map { |a| combine(a) } 
end 

説明

です。

e0 = array_of_hash.chunk_while { |g,h| h[:name] == g[:name].succ } 
    #=> #<Enumerator: #<Enumerator::Generator:0x007fa25e022f30>:each> 

は、Ruby V.2.3でデビューしたEnumerable#chunk_whileを参照してください。

この列挙子は、Enumerable#flat_mapに渡される次の要素を生成します。

は、この例で得られたハッシュの配列を返します。 e0によって生成され、ブロックに渡され、ブロック変数にflat_mapで割り当てられた最初の要素を考えます。

a = e0.next 
    #=> [{:name=>"1", :address=>"USA", 
    #  :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}] 

ブロック計算は

e1 = a.chunk { |g| g[:collection] } 
    #=> #<Enumerator: #<Enumerator::Generator:0x007fa25c857158>:each> 
e1.to_a 
    #=> [[["LAND", "WATER", "OIL", "TREE", "SAND"], 
    #  [{:name=>"1", :address=>"USA", 
    #  :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}] 
    # ] 
    # ] 

_c,b = e1.next 
    #=> [["LAND", "WATER", "OIL", "TREE", "SAND"], 
    # [{:name=>"1", :address=>"USA", 
    #  :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}] 
    # ] 
    # _c 
    # #=> ["LAND", "WATER", "OIL", "TREE", "SAND"] 
    # b #=> [{:name=>"1", :address=>"USA", 
    #   :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"}] 
combine(b) 
    #=> {:name=>"1", :address=>"USA", 
    # :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"} 

残りの計算は類似していることです。

関連する問題