Convert array of 2-element arrays into a hash, where duplicate keys append additional values

后端 未结 4 983
感动是毒
感动是毒 2020-12-01 05:03

For example

Given an array:

array = [[:a,:b],[:a,:c],[:c,:b]]

Return the following hash:

hash = { :a => [:b,:c         


        
4条回答
  •  眼角桃花
    2020-12-01 05:38

    Using functional baby steps:

    irb:01.0> array = [[:a,:b],[:a,:c],[:c,:b]]
    #=> [[:a, :b], [:a, :c], [:c, :b]]
    
    irb:02.0> array.group_by(&:first)
    #=> {:a=>[[:a, :b], [:a, :c]], :c=>[[:c, :b]]}
    
    irb:03.0> array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] }
    #=> [[:a, [:b, :c]], [:c, [:b]]]
    
    irb:04.0> Hash[ array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] } ]
    #=> {:a=>[:b, :c], :c=>[:b]}
    

    Using imperative style programming:

    irb:10.0> h = Hash.new{ |h,k| h[k]=[] }
    #=> {}
    
    irb:11.0> array.each{ |k,v| h[k] << v }
    #=> [[:a, :b], [:a, :c], [:c, :b]]
    
    irb:12.0> h
    #=> {:a=>[:b, :c], :c=>[:b]}
    

    As an imperative one-liner:

    irb:13.0> h = Hash.new{ |h,k| h[k]=[] }.tap{ |h| array.each{ |k,v| h[k] << v } }
    #=> {:a=>[:b, :c], :c=>[:b]}
    

    Or using everyone's favorite inject:

    irb:14.0> array.inject(Hash.new{ |h,k| h[k]=[] }){ |h,(k,v)| h[k] << v; h }
    #=> {:a=>[:b, :c], :c=>[:b]}
    

    If you really want to have single values not collided as an array, you can either un-array them as a post-processing step, or use a different hash accumulation strategy that only creates an array upon collision. Alternatively, wrap your head around this:

    irb:17.0> hashes = array.map{ |pair| Hash[*pair] } # merge many mini hashes
    #=> [{:a=>:b}, {:a=>:c}, {:c=>:b}]
    
    irb:18.0> hashes.inject{ |h1,h2| h1.merge(h2){ |*a| a[1,2] } }
    #=> {:a=>[:b, :c], :c=>:b}
    

提交回复
热议问题