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}
Voici une comparaison rapide des performances des méthodes proposées, #select
semble être le plus rapide
k = 1_000_000
Benchmark.bmbm do |x|
x.report('select') { k.times { {a: 1, b: 2, c: 3}.select { |k, _v| [:a, :b].include?(k) } } }
x.report('hash transpose') { k.times { Hash[ [[:a, :b], {a: 1, b: 2, c: 3}.fetch_values(:a, :b)].transpose ] } }
x.report('slice') { k.times { {a: 1, b: 2, c: 3}.slice(:a, :b) } }
end
Rehearsal --------------------------------------------------
select 1.640000 0.010000 1.650000 ( 1.651426)
hash transpose 1.720000 0.010000 1.730000 ( 1.729950)
slice 1.740000 0.010000 1.750000 ( 1.748204)
----------------------------------------- total: 5.130000sec
user system total real
select 1.670000 0.010000 1.680000 ( 1.683415)
hash transpose 1.680000 0.010000 1.690000 ( 1.688110)
slice 1.800000 0.010000 1.810000 ( 1.816215)
Le raffinement ressemblera à ceci :
module CoreExtensions
module Extractable
refine Hash do
def extract(*keys)
select { |k, _v| keys.include?(k) }
end
end
end
end
Et de l'utiliser :
using ::CoreExtensions::Extractable
{ a: 1, b: 2, c: 3 }.extract(:a, :b)
Ce code injecte la fonctionnalité que vous demandez dans la classe Hash :
class Hash
def extract_subhash! *keys
to_keep = self.keys.to_a - keys
to_delete = Hash[self.select{|k,v| !to_keep.include? k}]
self.delete_if {|k,v| !to_keep.include? k}
to_delete
end
end
et produit les résultats que vous avez fournis :
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
p h1.extract_subhash!(:b, :d, :e, :f) # => {b => :B, :d => :D}
p h1 #=> {:a => :A, :c => :C}
Remarque : cette méthode renvoie en fait les clés/valeurs extraites.
Voici une solution fonctionnelle qui peut être utile si vous n'êtes pas sous Ruby 2.5 et dans le cas où vous ne voulez pas polluer votre classe Hash en ajoutant une nouvelle méthode :
slice_hash = -> keys, hash { hash.select { |k, _v| keys.include?(k) } }.curry
Ensuite, vous pouvez l'appliquer même sur des hachages imbriqués :
my_hash = [{name: "Joe", age: 34}, {name: "Amy", age: 55}]
my_hash.map(&slice_hash.([:name]))
# => [{:name=>"Joe"}, {:name=>"Amy"}]
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.