Как пройти этот хеш в пределах одной строки? - PullRequest
3 голосов
/ 25 января 2012

Каждый ключ в хэше имеет значение, которое также является хешем.

    {
      100 => {
        1 => 'ruby',
        2 => 'enumerables'
      },
      50 => {
        3 => 'can',
        4 => 'cause'
      },
      15 => {
        5 => 'occassional',
        6 => 'insanity'
      }
    }

Для каждого хеш-объекта я хочу отбросить ключ верхнего уровня и заменить его ключом и значениемвложенные хеш-объекты.

{
  1 => 'ruby',
  2 => 'enumerables',
  3 => 'can',
  4 => 'cause',
  5 => 'occasional',
  6 => 'insanity'
}

У меня это работает, но мой метод использует merge! и требует создания другого хеша для хранения значений.Мне любопытно посмотреть, можно ли это сделать в одну строку.Я пытался использовать reduce(), но не смог заставить его работать.

Ответы [ 6 ]

10 голосов
/ 25 января 2012

Это работает:

hash.values.inject(&:merge)

Редактировать: Другой вариант, использующий reduce (который совпадает с inject), и отмечающий комментарий токланда о том, что to_proc автоматически вызываетсякогда вы используете символ:

hash.values.reduce(:merge)

, тогда он становится не только кратким, но и очень читабельным.

5 голосов
/ 25 января 2012

Мне больше нравится ответ @MarkThomas, но для скорости и эффективности памяти я предлагаю:

flatter = {}.tap{ |h| original.values.each{ |h2| h.merge!(h2) } }

Сравнительный анализ 200 000 итераций текущих ответов показывает, что это самый быстрый:

                          user     system      total        real
Phrogz                0.710000   0.020000   0.730000 (  0.728706)
Joshua Creek          0.830000   0.010000   0.840000 (  0.830700)
Mark Thomas symbol    1.460000   0.020000   1.480000 (  1.486463)
Mark Thomas to_proc   1.540000   0.030000   1.570000 (  1.565354)
Tim Peters            1.650000   0.030000   1.680000 (  1.678283)

Поскольку комментарий @ tokland - original.values.reduce(:update) - изменяет исходный хеш, мы не можем сравнивать его напрямую с другими методами.Однако, если мы изменим все тесты, чтобы поместить дубликат первого хеша обратно в исходный каждую итерацию, ответ @ tokland станет самым быстрым, хотя и не таким быстрым, как мой:

                          user     system      total        real
tokland's destroyer   0.760000   0.010000   0.770000 (  0.772774)
Phrogz                1.020000   0.020000   1.040000 (  1.034755)
Joshua Creek          1.060000   0.000000   1.060000 (  1.063874)
Mark Thomas symbol    1.780000   0.040000   1.820000 (  1.816909)
Mark Thomas to_proc   1.790000   0.030000   1.820000 (  1.819014)
Tim Peters            1.800000   0.040000   1.840000 (  1.827984)

Если вам нужноабсолютная скорость, и можно изменять исходные значения, используйте ответ @ tokland.Если вы сделаете это и хотите сохранить исходные необработанные хэши в целости и сохранности, то вы можете:

first_k,orig_v = original.each{ |k,v| break [k,v.dup] }
merged = original.values.reduce(:update)
original[first_k] = orig_v

Обратите внимание, что в заголовке вашего вопроса написано traverse ;если вы на самом деле не хотите объединять значения - если вы захотите дважды посетить дублированный ключ вместо последних выигрышей, - просто выполните:

original.values.each{ |h| h.each{ |k,v|
  # hey, we're traversing inside!
} }
3 голосов
/ 25 января 2012
Hash[original_hash.values.flat_map(&:to_a)]
3 голосов
/ 25 января 2012

Поскольку вы не цените ключи верхнего уровня, используйте #values, чтобы получить массив значений (в данном случае также хэши). Затем вы можете использовать #inject для создания нового хэша, объединяя их по ходу работы.

yourhash.values.inject{|hash, thing| hash.merge(thing)}

Возможно, есть и другие способы сделать это.

2 голосов
/ 25 января 2012

Хотя это не так кратко, как некоторые другие ответы, я думаю, each_with_object заслуживает представления.

output = input.each_with_object Hash.new do |(_,subhash), hash|
  hash.merge! subhash
end
2 голосов
/ 25 января 2012

Только что сделал удар, первая попытка была грубой силой, и лучшие методы (в обоих смыслах этого слова ...) существуют.

h.map {|k,v| v}.inject({}) {|i,h| i.merge(h)}
...