web-dev-qa-db-fra.com

Matrice à hacher Ruby

Bon, voici le contrat. Cela fait très longtemps que je recherche des solutions sur ce site dans Google. Bien qu’il en existe beaucoup, ils ne semblent pas faire le travail que je recherche.

Fondamentalement, j'ai un tableau structuré comme celui-ci

["item 1", "item 2", "item 3", "item 4"] 

Je veux convertir ceci en un hachage afin qu'il ressemble à ceci

{ "item 1" => "item 2", "item 3" => "item 4" }

c'est-à-dire que les éléments figurant dans les index "pairs" sont les clés et les éléments figurant dans les index "impairs" sont les valeurs.

Des idées comment faire cela proprement? Je suppose qu'une méthode de force brute consisterait simplement à extraire tous les index pairs dans un tableau séparé, puis à les contourner pour ajouter les valeurs.

182
djhworld
a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # => { "item 1" => "item 2", "item 3" => "item 4" }

C'est ça. Le * est appelé l'opérateur splat.

Un avertissement par @ Mike Lewis (dans les commentaires): "Soyez très prudent avec ceci. Ruby élargit les splats sur la pile. Si vous faites cela avec un grand jeu de données, attendez-vous à ce que votre pile s'épuise."

Ainsi, dans la plupart des cas d'utilisation générale, cette méthode est excellente, mais utilisez une méthode différente si vous souhaitez effectuer la conversion sur de nombreuses données. Par exemple, @ Łukasz Niemier (également dans les commentaires) propose cette méthode pour les grands ensembles de données:

h = Hash[a.each_slice(2).to_a]
348
Ben Lee

Ruby 2.1.0 a introduit une méthode to_h sur Array qui répond à vos besoins si votre tableau d'origine est constitué de tableaux de paires clé-valeur: http://www.Ruby-doc.org/core- 2.1.0/Array.html # method-i-to_h .

[[:foo, :bar], [1, 2]].to_h
# => {:foo => :bar, 1 => 2}
95

Utilisez simplement Hash.[] avec les valeurs du tableau. Par exemple:

arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 4}
27
Chuck

Ou si vous avez un tableau de tableaux [key, value], vous pouvez faire:

[[1, 2], [3, 4]].inject({}) do |r, s|
  r.merge!({s[0] => s[1]})
end # => { 1 => 2, 3 => 4 }
25
Erik Escobedo

Voici ce que je cherchais en recherchant ceci:

[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v } => {:a=>1, :b=>2}

10
Karl Glaser

Enumerator comprend Enumerable . Depuis _2.1_, Enumerable a également une méthode #to_h . C'est pourquoi, nous pouvons écrire: -

_a = ["item 1", "item 2", "item 3", "item 4"]
a.each_slice(2).to_h
# => {"item 1"=>"item 2", "item 3"=>"item 4"}
_

Parce que #each_slice sans bloc nous donne Enumerator, et comme expliqué ci-dessus, nous pouvons appeler la méthode _#to_h_ sur le Enumerator objet.

9
Arup Rakshit

Vous pouvez essayer comme ça, pour un seul tableau

irb(main):019:0> a = ["item 1", "item 2", "item 3", "item 4"]
  => ["item 1", "item 2", "item 3", "item 4"]
irb(main):020:0> Hash[*a]
  => {"item 1"=>"item 2", "item 3"=>"item 4"}

pour tableau de tableau

irb(main):022:0> a = [[1, 2], [3, 4]]
  => [[1, 2], [3, 4]]
irb(main):023:0> Hash[*a.flatten]
  => {1=>2, 3=>4}
6
Jenorish
a = ["item 1", "item 2", "item 3", "item 4"]
Hash[ a.each_slice( 2 ).map { |e| e } ]

ou si vous détestez Hash[ ... ]:

a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end

ou, si vous êtes un fan fainéant de programmation fonctionnelle interrompue:

h = a.lazy.each_slice( 2 ).tap { |a|
  break Hash.new { |h, k| h[k] = a.find { |e, _| e == k }[1] }
}
#=> {}
h["item 1"] #=> "item 2"
h["item 3"] #=> "item 4"
5
Boris Stitnicky

Toutes les réponses supposent que le tableau de départ est unique. OP n'a pas précisé comment gérer les tableaux contenant des entrées en double, ce qui entraîne la duplication des clés.

Regardons:

a = ["item 1", "item 2", "item 3", "item 4", "item 1", "item 5"]

Vous perdrez la paire item 1 => item 2 car elle est remplacée par item 1 => item 5:

Hash[*a]
=> {"item 1"=>"item 5", "item 3"=>"item 4"}

Toutes les méthodes, y compris la reduce(&:merge!), entraînent la même suppression.

Il se pourrait que ce soit exactement ce que vous attendez, cependant. Mais dans d'autres cas, vous souhaiterez probablement obtenir un résultat avec un Array comme valeur:

{"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

La manière naïve serait de créer une variable d'assistance, un hachage qui a une valeur par défaut, puis de la remplir en boucle:

result = Hash.new {|hash, k| hash[k] = [] } # Hash.new with block defines unique defaults.
a.each_slice(2) {|k,v| result[k] << v }
a
=> {"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

Il est peut-être possible d'utiliser assoc et reduce à faire en haut d'une ligne, mais cela devient beaucoup plus difficile à raisonner et à lire.

0
berkes