Question

Chaque clé d'un hachage a une valeur qui est également un hachage.

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

Pour chaque objet de hachage, je souhaite supprimer la clé de niveau supérieur et la remplacer par la clé et la valeur des objets de hachage imbriqués.

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

Je le fais fonctionner, mais ma méthode utilise un merge!, et nécessite la création d'un autre hachage pour stocker les valeurs.Je suis curieux de voir si cela peut être fait en une seule ligne.J'ai essayé d'utiliser reduce(), mais je n'ai pas pu le faire fonctionner.

Était-ce utile?

La solution

Cela marche:

hash.values.inject(&:merge)

Éditer: Une autre option, en utilisant reduce (ce qui est le même que inject), et noter le commentaire de Tokland selon lequel TO_PROC est automatiquement appelé lorsque vous utilisez un symbole:

hash.values.reduce(:merge)

Alors cela devient non seulement concis mais très lisible.

Autres conseils

J'aime le mieux la réponse de @markthomas, mais pour la vitesse et l'efficacité de la mémoire, je suggère:

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

L'analyse comparative 200 000 itérations des réponses actuelles montrent que c'est la plus rapide:

                          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)

Depuis le commentaire de @ tokland—original.values.reduce(:update)—Modifie le hachage d'origine, nous ne pouvons pas le comparer directement aux autres méthodes. Cependant, si nous modifions tous les tests pour remettre un doublon du premier hachage dans l'original de chaque itération, la réponse de @ Tokland devient la plus rapide, mais toujours pas aussi rapide que la mienne:

                          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)

Si vous avez besoin d'une vitesse absolue et qu'il est normal de modifier les valeurs d'origine, utilisez la réponse de @ Tokland. Si vous le faites et que vous souhaitez préserver les hachages originaux non mères indemnes, alors vous pouvez:

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

Notez que le titre de votre question dit traverser; Si vous ne voulez pas vraiment fusionner les valeurs - si vous voudrez peut-être visiter une clé en double deux fois au lieu du dernier en-toile, puis faites-le:

original.values.each{ |h| h.each{ |k,v|
  # hey, we're traversing inside!
} }

Puisque vous n'accordez aucune valeur aux clés de niveau supérieur, utilisez #values ​​pour obtenir un tableau de valeurs (dans ce cas, également des hachages).Ensuite, vous pouvez utiliser #inject pour créer un nouveau hachage, en les fusionnant au fur et à mesure.

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

Il existe probablement d'autres façons de procéder.

Hash[original_hash.values.flat_map(&:to_a)]

Il suffit de prendre un coup de poing, d'abord, un essai était une force brute et de meilleures méthodes (dans les deux sens du mot ...) sont là-bas.

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

Bien que ce ne soit pas aussi bref que certaines des autres réponses, je pense each_with_object mérite une représentation.

output = input.each_with_object Hash.new do |(_,subhash), hash|
  hash.merge! subhash
end
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top