Как объединить массив хэшей, чтобы получить хэш массивов значений

Это противоположно Превращение хэша массивов в массив хэшей в Ruby .

Элегантно и / или эффективно превращайте массив хэшей в хэш, где значениями являются массивы всех значений:

hs = [
  { a:1, b:2 },
  { a:3, c:4 },
  { b:5, d:6 }
]
collect_values( hs )
#=> { :a=>[1,3], :b=>[2,5], :c=>[4], :d=>[6] }

Этот краткий код почти работает, но не может создать массив, когда нет дубликатов :

def collect_values( hashes )
  hashes.inject({}){ |a,b| a.merge(b){ |_,x,y| [*x,*y] } }
end
collect_values( hs )
#=> { :a=>[1,3], :b=>[2,5], :c=>4, :d=>6 }

Этот код работает, но можете ли вы написать лучшую версию?

def collect_values( hashes )
  # Requires Ruby 1.8.7+ for Object#tap
  Hash.new{ |h,k| h[k]=[] }.tap do |result|
    hashes.each{ |h| h.each{ |k,v| result[k]<

Решения, которые работают только в Ruby 1.9, приемлемы, но должны быть отмечены как таковые.


Вот результаты тестирования различных ответов ниже ( и еще несколько моих), [{: a => 1}, {: b => 2}, {: c => 3}, {: d => 4}, {: e => 5}, {: f => 6 }, {: g => 7}, ...]

  • один, где каждый хэш имеет один и тот же ключ, поэтому происходит максимальное слияние:
    [{: a => 1}, {: a => 2}, {: a => 3}, {: a => 4}, {: a => 5}, {: a => 6 }, {: a => 7}, ...]

  • и тот, который представляет собой сочетание уникальных и общих ключей:
    [{: c => 1}, {: d => 1}, {: c => 2}, {: f => 1}, {: c => 1,: d => 1}, {: h => 1}, {: c => 3}, ...]
  •                user     system      total        real
    Phrogz 2a  0.577000   0.000000   0.577000 (  0.576000)
    Phrogz 2b  0.624000   0.000000   0.624000 (  0.620000)
    Glenn 1    0.640000   0.000000   0.640000 (  0.641000)
    Phrogz 1   0.671000   0.000000   0.671000 (  0.668000)
    Michael 1  0.702000   0.000000   0.702000 (  0.700000)
    Michael 2  0.717000   0.000000   0.717000 (  0.726000)
    Glenn 2    0.765000   0.000000   0.765000 (  0.764000)
    fl00r      0.827000   0.000000   0.827000 (  0.836000)
    sawa       0.874000   0.000000   0.874000 (  0.868000)
    Tokland 1  0.873000   0.000000   0.873000 (  0.876000)
    Tokland 2  1.077000   0.000000   1.077000 (  1.073000)
    Phrogz 3   2.106000   0.093000   2.199000 (  2.209000)
    

    Самый быстрый код - это добавленный мной метод:

    def collect_values(hashes)
      {}.tap{ |r| hashes.each{ |h| h.each{ |k,v| (r[k]||=[]) << v } } }
    end
    

    Я принял " ответ Гленна Макдональда "поскольку он был конкурентоспособным с точки зрения скорости, достаточно кратким, но (что наиболее важно) потому, что он указывал на опасность использования хэша с самомодифицирующимся процессом по умолчанию для удобного построения, поскольку это может привести к плохим изменениям, когда пользователь индексирует это позже.

    Наконец, вот код теста на случай, если вы захотите провести собственное сравнение:

    require 'prime'   # To generate the third hash
    require 'facets'  # For tokland1's map_by
    AZSYMBOLS = (:a..:z).to_a
    TESTS = {
      '26 Distinct Hashes'   => AZSYMBOLS.zip(1..26).map{|a| Hash[*a] },
      '26 Same-Key Hashes'   => ([:a]*26).zip(1..26).map{|a| Hash[*a] },
      '26 Mixed-Keys Hashes' => (2..27).map do |i|
        factors = i.prime_division.transpose
        Hash[AZSYMBOLS.values_at(*factors.first).zip(factors.last)]
      end
    }
    
    def phrogz1(hashes)
      Hash.new{ |h,k| h[k]=[] }.tap do |result|
        hashes.each{ |h| h.each{ |k,v| result[k]<

    44
    задан Community 23 May 2017 в 12:34
    поделиться