Question Comment supprimer une clé de Hash et récupérer le hash restant dans Ruby / Rails?


Pour ajouter une nouvelle paire à Hash je fais:

{:a => 1, :b => 2}.merge!({:c => 3})   #=> {:a => 1, :b => 2, :c => 3}

Existe-t-il un moyen similaire de supprimer une clé de Hash?

Cela marche:

{:a => 1, :b => 2}.reject! { |k| k == :a }   #=> {:b => 2}

mais je m'attendrais à avoir quelque chose comme:

{:a => 1, :b => 2}.delete!(:a)   #=> {:b => 2}

Il est important que la valeur de retour soit le hash restant, donc je pourrais faire des choses comme:

foo(my_hash.reject! { |k| k == my_key })

dans une ligne.


453
2018-06-03 13:19


origine


Réponses:


Rails a un sauf / sauf! méthode qui renvoie le hachage avec ces clés supprimées. Si vous utilisez déjà Rails, créer votre propre version n’a aucun sens.

class Hash
  # Returns a hash that includes everything but the given keys.
  #   hash = { a: true, b: false, c: nil}
  #   hash.except(:c) # => { a: true, b: false}
  #   hash # => { a: true, b: false, c: nil}
  #
  # This is useful for limiting a set of parameters to everything but a few known toggles:
  #   @person.update(params[:person].except(:admin))
  def except(*keys)
    dup.except!(*keys)
  end

  # Replaces the hash without the given keys.
  #   hash = { a: true, b: false, c: nil}
  #   hash.except!(:c) # => { a: true, b: false}
  #   hash # => { a: true, b: false }
  def except!(*keys)
    keys.each { |key| delete(key) }
    self
  end
end

614
2018-06-19 17:03



Oneliner plaine rubis, cela fonctionne uniquement avec ruby> 1.9.x:

1.9.3p0 :002 > h = {:a => 1, :b => 2}
 => {:a=>1, :b=>2} 
1.9.3p0 :003 > h.tap { |hs| hs.delete(:a) }
 => {:b=>2} 

Robinet méthode retourne toujours l'objet sur lequel est invoqué ...

Sinon, si vous avez demandé active_support/core_ext/hash (qui est automatiquement requis dans chaque application Rails), vous pouvez utiliser l'une des méthodes suivantes selon vos besoins:

➜  ~  irb
1.9.3p125 :001 > require 'active_support/core_ext/hash' => true 
1.9.3p125 :002 > h = {:a => 1, :b => 2, :c => 3}
 => {:a=>1, :b=>2, :c=>3} 
1.9.3p125 :003 > h.except(:a)
 => {:b=>2, :c=>3} 
1.9.3p125 :004 > h.slice(:a)
 => {:a=>1} 

sauf utilise une approche de liste noire, donc il supprime toutes les clés répertoriées comme arguments, tout en tranche utilise une approche de liste blanche, de sorte qu'il supprime toutes les clés qui ne sont pas répertoriées en tant qu'arguments. Il existe également la version bang de cette méthode (except! et slice!) qui modifient le hash donné mais dont la valeur de retour est différente tous les deux renvoient un hachage. Il représente les clés supprimées pour slice! et les clés qui sont conservées pour le except!:

1.9.3p125 :011 > {:a => 1, :b => 2, :c => 3}.except!(:a)
 => {:b=>2, :c=>3} 
1.9.3p125 :012 > {:a => 1, :b => 2, :c => 3}.slice!(:a)
 => {:b=>2, :c=>3} 

171
2018-01-27 00:35



Pourquoi ne pas simplement utiliser:

hash.delete(key)

139
2018-06-03 13:23



Il existe plusieurs façons de supprimer une clé d'un hachage et d'obtenir le hash restant dans Ruby.

  1. .slice => Il retournera les clés sélectionnées et ne les supprimera pas du hash original

    2.2.2 :074 > hash = {"one"=>1, "two"=>2, "three"=>3}
     => {"one"=>1, "two"=>2, "three"=>3} 
    2.2.2 :075 > hash.slice("one","two")
     => {"one"=>1, "two"=>2} 
    2.2.2 :076 > hash
     => {"one"=>1, "two"=>2, "three"=>3} 
    
  2. .delete => Il supprimera les clés sélectionnées du hash original (il ne peut accepter qu'une seule clé et pas plus d'une)

    2.2.2 :094 > hash = {"one"=>1, "two"=>2, "three"=>3}
     => {"one"=>1, "two"=>2, "three"=>3} 
    2.2.2 :095 > hash.delete("one")
     => 1 
    2.2.2 :096 > hash
     => {"two"=>2, "three"=>3} 
    
  3. .except => Il retournera les clés restantes mais ne supprimera rien du hash original

    2.2.2 :097 > hash = {"one"=>1, "two"=>2, "three"=>3}
     => {"one"=>1, "two"=>2, "three"=>3} 
    2.2.2 :098 > hash.except("one","two")
     => {"three"=>3} 
    2.2.2 :099 > hash
     => {"one"=>1, "two"=>2, "three"=>3}         
    
  4. .delete_if => Au cas où vous devez supprimer une clé en fonction d'une valeur. Il va évidemment supprimer les clés correspondantes du hash original

    2.2.2 :115 > hash = {"one"=>1, "two"=>2, "three"=>3, "one_again"=>1}
     => {"one"=>1, "two"=>2, "three"=>3, "one_again"=>1} 
    2.2.2 :116 > value = 1
     => 1 
    2.2.2 :117 > hash.delete_if { |k,v| v == value }
     => {"two"=>2, "three"=>3} 
    2.2.2 :118 > hash
     => {"two"=>2, "three"=>3} 
    

Résultats basés sur Ruby 2.2.2.


44
2017-08-30 15:05



Si vous voulez utiliser du Ruby pur (sans Rails), vous ne voulez pas créer de méthodes d'extension (peut-être vous n'en avez besoin qu'à un ou deux endroits et ne voulez pas polluer l'espace de noms avec des tonnes de méthodes) et vous ne voulez pas éditez le hachage en place (c.-à-d. vous êtes fan de la programmation fonctionnelle comme moi), vous pouvez «sélectionner»:

>> x = {:a => 1, :b => 2, :c => 3}
=> {:a=>1, :b=>2, :c=>3}
>> x.select{|x| x != :a}
=> {:b=>2, :c=>3}
>> x.select{|x| ![:a, :b].include?(x)}
=> {:c=>3}
>> x
=> {:a=>1, :b=>2, :c=>3}

32
2017-12-19 13:55



#in lib/core_extensions.rb
class Hash
  #pass single or array of keys, which will be removed, returning the remaining hash
  def remove!(*keys)
    keys.each{|key| self.delete(key) }
    self
  end

  #non-destructive version
  def remove(*keys)
    self.dup.remove!(*keys)
  end
end

#in config/initializers/app_environment.rb (or anywhere in config/initializers)
require 'core_extensions'

J'ai mis ceci en place pour que .remove renvoie une copie du hachage avec les clés retirées, alors que remove! modifie le hachage lui-même. Ceci est en accord avec les conventions de rubis. par exemple, à partir de la console

>> hash = {:a => 1, :b => 2}
=> {:b=>2, :a=>1}
>> hash.remove(:a)
=> {:b=>2}
>> hash
=> {:b=>2, :a=>1}
>> hash.remove!(:a)
=> {:b=>2}
>> hash
=> {:b=>2}
>> hash.remove!(:a, :b)
=> {}

30
2018-06-03 14:42



Vous pouvez utiliser except! du facets gemme:

>> require 'facets' # or require 'facets/hash/except'
=> true
>> {:a => 1, :b => 2}.except(:a)
=> {:b=>2}

Le hash original ne change pas.

EDIT: comme le dit Russel, les facettes ont des problèmes cachés et ne sont pas complètement compatibles API avec ActiveSupport. D'un autre côté, ActiveSupport n'est pas aussi complet que les facettes. Au final, j'utiliserais AS et laisserais le bord dans votre code.


27
2017-10-13 00:25



Au lieu de patcher un singe ou d'inclure de grandes bibliothèques, vous pouvez utiliser raffinements si vous utilisez Ruby 2:

module HashExtensions
  refine Hash do
    def except!(*candidates)
      candidates.each { |candidate| delete(candidate) }
      self
    end

    def except(*candidates)
      dup.remove!(candidates)
    end
  end
end

Vous pouvez utiliser cette fonctionnalité sans affecter les autres parties de votre programme ou devoir inclure de grandes bibliothèques externes.

class FabulousCode
  using HashExtensions

  def incredible_stuff
    delightful_hash.except(:not_fabulous_key)
  end
end

18
2017-12-12 19:19