J'ai un hash :
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
Quelle est la meilleure façon d'extraire un sous-réseau comme celui-ci ?
h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D}
h1 #=> {:a => :A, :c => :C}
J'ai un hash :
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
Quelle est la meilleure façon d'extraire un sous-réseau comme celui-ci ?
h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D}
h1 #=> {:a => :A, :c => :C}
ActiveSupport
au moins depuis la version 2.3.8, fournit quatre méthodes pratiques : #slice
, #except
et leurs homologues destructeurs : #slice!
et #except!
. Ils ont été mentionnés dans d'autres réponses, mais pour les résumer en un seul endroit :
x = {a: 1, b: 2, c: 3, d: 4}
# => {:a=>1, :b=>2, :c=>3, :d=>4}
x.slice(:a, :b)
# => {:a=>1, :b=>2}
x
# => {:a=>1, :b=>2, :c=>3, :d=>4}
x.except(:a, :b)
# => {:c=>3, :d=>4}
x
# => {:a=>1, :b=>2, :c=>3, :d=>4}
Notez les valeurs de retour des méthodes bang. Elles ne vont pas seulement adapter le hachage existant mais aussi retourner les entrées supprimées (non conservées). Le site Hash#except!
convient le mieux à l'exemple donné dans la question :
x = {a: 1, b: 2, c: 3, d: 4}
# => {:a=>1, :b=>2, :c=>3, :d=>4}
x.except!(:c, :d)
# => {:a=>1, :b=>2}
x
# => {:a=>1, :b=>2}
ActiveSupport
ne nécessite pas l'ensemble de Rails, est assez léger. En fait, beaucoup de gemmes non-rails en dépendent, donc il est fort probable que vous l'ayez déjà dans Gemfile.lock. Il n'est pas nécessaire d'étendre la classe Hash par vous-même.
Si vous voulez spécifiquement que la méthode renvoie les éléments extraits mais que h1 reste le même :
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2 = h1.select {|key, value| [:b, :d, :e, :f].include?(key) } # => {:b=>:B, :d=>:D}
h1 = Hash[h1.to_a - h2.to_a] # => {:a=>:A, :c=>:C}
Et si vous voulez patcher ça dans la classe Hash :
class Hash
def extract_subhash(*extract)
h2 = self.select{|key, value| extract.include?(key) }
self.delete_if {|key, value| extract.include?(key) }
h2
end
end
Si vous voulez simplement supprimer les éléments spécifiés du hachage, c'est beaucoup plus facile en utilisant la méthode suivante supprimer_if .
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h1.delete_if {|key, value| [:b, :d, :e, :f].include?(key) } # => {:a=>:A, :c=>:C}
h1 # => {:a=>:A, :c=>:C}
C'est O(n2) - vous aurez une boucle sur le select, une autre boucle sur l'include qui sera appelée h1.size plusieurs fois.
Bien que cette réponse soit décente pour ruby pur, si vous utilisez rails, la réponse ci-dessous (utilisant l'intégré slice
ou except
en fonction de vos besoins) est beaucoup plus propre
Ruby 2.5 ajouté Hash#slice :
h = { a: 100, b: 200, c: 300 }
h.slice(:a) #=> {:a=>100}
h.slice(:b, :c, :d) #=> {:b=>200, :c=>300}
Si vous utilisez des rails , Hash#slice est la voie à suivre.
{:a => :A, :b => :B, :c => :C, :d => :D}.slice(:a, :c)
# => {:a => :A, :c => :C}
Si vous n'utilisez pas de rails , Hash#values_at retournera les valeurs dans l'ordre dans lequel vous les avez demandées. pour que vous puissiez faire ça :
def slice(hash, *keys)
Hash[ [keys, hash.values_at(*keys)].transpose]
end
def except(hash, *keys)
desired_keys = hash.keys - keys
Hash[ [desired_keys, hash.values_at(*desired_keys)].transpose]
end
ex :
slice({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2)
# => {'bar' => 'foo', 2 => 'two'}
except({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2)
# => {:foo => 'bar'}
Explication :
Hors de {:a => 1, :b => 2, :c => 3}
nous voulons {:a => 1, :b => 2}
hash = {:a => 1, :b => 2, :c => 3}
keys = [:a, :b]
values = hash.values_at(*keys) #=> [1, 2]
transposed_matrix =[keys, values].transpose #=> [[:a, 1], [:b, 2]]
Hash[transposed_matrix] #=> {:a => 1, :b => 2}
Si vous avez l'impression que le singe Parcheando est la voie à suivre, voici ce que vous voulez :
module MyExtension
module Hash
def slice(*keys)
::Hash[[keys, self.values_at(*keys)].transpose]
end
def except(*keys)
desired_keys = self.keys - keys
::Hash[[desired_keys, self.values_at(*desired_keys)].transpose]
end
end
end
Hash.include MyExtension::Hash
Vous pouvez utiliser slice !(*keys) qui est disponible dans les extensions de base d'ActiveSupport.
initial_hash = {:a => 1, :b => 2, :c => 3, :d => 4}
extracted_slice = initial_hash.slice!(:a, :c)
initial_hash serait maintenant
{:b => 2, :d =>4}
La diapositive extraite serait maintenant
{:a => 1, :c =>3}
Vous pouvez consulter slice.rb in ActiveSupport 3.1.3
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
5 votes
Note complémentaire : apidock.com/rails/Hash/slice%21
1 votes
@JanDvorak Cette question ne concerne pas seulement le retour d'un subhash mais aussi la modification d'un subhash existant. Ce sont des choses très similaires mais ActiveSupport a des moyens différents pour les traiter.