Jak usunąć klucz z Hasha i uzyskać pozostały hash w Ruby / Rails?

Aby dodać nową parę do Hasha robię:

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

Czy istnieje podobny sposób na usunięcie klucza z Hasha ?

To działa:

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

Ale spodziewałbym się czegoś takiego:

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

Ważne jest, aby zwracana wartość była pozostałym Hashem, więc mogę robić rzeczy takie jak:

foo(my_hash.reject! { |k| k == my_key })
W jednej linii.
Author: Scott Schupbach, 2011-06-03

13 answers

Rails ma wyjątek / except! Metoda , która zwraca hash z usuniętymi kluczami. Jeśli już używasz Rails, nie ma sensu tworzyć własnej wersji tego.

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
 626
Author: Peter Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-20 01:27:49

Oneliner zwykły ruby, działa tylko z 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} 

Metoda Tap zawsze zwraca obiekt, na którym jest wywoływany...

W przeciwnym razie, jeśli masz wymagane active_support/core_ext/hash (co jest automatycznie wymagane w każdej aplikacji Rails), możesz użyć jednej z następujących metod w zależności od twoich potrzeb:

➜  ~  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} 

Z wyjątkiem używa metody czarnej listy, więc usuwa wszystkie klucze wymienione jako args, podczas gdy slice używa metody białej listy, więc usuwa wszystkie klucze, które nie są wymienione jako argumenty. Istnieje również wersja bang tych metod (except! i slice!), które modyfikują podany hash, ale ich wartość zwracana jest inna. Reprezentuje usunięte klucze dla slice! i klucze, które są przechowywane dla 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} 
 174
Author: Fabio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-04 10:24:57

Dlaczego po prostu nie używać:

hash.delete(key)
 140
Author: dbryson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-15 14:51:28

Istnieje wiele sposobów na usunięcie klucza z hasha i uzyskanie pozostałego hasha w Rubim.

  1. .slice => zwróci wybrane klucze i nie usunie ich z oryginalnego hasha

    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 => usunie wybrane klucze z oryginalnego hasha (może zaakceptować tylko jeden klucz i nie więcej niż jeden)

    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 => zwróci Pozostałe klucze, ale nie usunie niczego z oryginału hash

    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 => W przypadku konieczności usunięcia klucza na podstawie wartości. Oczywiście usunie pasujące klucze z oryginalnego hasha

    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} 
    

Wyniki oparte na Ruby 2.2.2.

 46
Author: techdreams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-30 15:05:36

Jeśli chcesz używać czystego Ruby (bez Rails), nie chcesz tworzyć metod rozszerzeń (może potrzebujesz tego tylko w jednym lub dwóch miejscach i nie chcesz zanieczyszczać przestrzeni nazw mnóstwem metod) i nie chcesz edytować hasha w miejscu (np. jesteś fanem programowania funkcyjnego jak ja), możesz 'wybrać':

>> 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}
 33
Author: Yura Taras,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-19 13:55:03
#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'
Ustawiłem to tak .remove zwraca kopię skrótu z usuniętymi kluczami, podczas gdy remove! modyfikuje sam hash. Jest to zgodne z konwencjami ruby. np. z konsoli
>> 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
Author: Max Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-06 11:13:50

Możesz użyć except! z facets gem:

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

Oryginalny hash nie zmienia się.

EDIT: jak mówi Russel, facets ma pewne ukryte problemy i nie jest całkowicie kompatybilny z API ActiveSupport. Z drugiej strony ActiveSupport nie jest tak kompletny jak aspekty. W końcu użyłbym jako i niech sprawy krawędzi w Twoim kodzie.

 27
Author: rewritten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-19 10:30:02

Zamiast łatania małp lub niepotrzebnego dodawania dużych bibliotek, możesz użyć udoskonaleń , Jeśli używasz Rubiego 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

Możesz używać tej funkcji bez wpływu na inne części programu lub konieczności dołączania dużych bibliotek zewnętrznych.

class FabulousCode
  using HashExtensions

  def incredible_stuff
    delightful_hash.except(:not_fabulous_key)
  end
end
 18
Author: Mohamad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-12 19:53:44

W pure Ruby:

{:a => 1, :b => 2}.tap{|x| x.delete(:a)}   # => {:b=>2}
 15
Author: gamov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-07 04:25:13

Zobacz Ruby on Rails: Delete multiple hash keys

hash.delete_if{ |k,| keys_to_delete.include? k }
 11
Author: Nakilon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:54

Jest to jednolinijkowy sposób, ale nie jest zbyt czytelny. Zaleca się użycie dwóch linii.

use_remaining_hash_for_something(Proc.new { hash.delete(:key); hash }.call)
 0
Author: the_minted,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-14 12:00:52

To było świetne, jeśli delete zwróci parę delete hasha. Robię to:

hash = {a: 1, b: 2, c: 3}
{b: hash.delete(:b)} # => {:b=>2}
hash  # => {:a=>1, :c=>3} 
 0
Author: frenesim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-02 09:48:38

To również zadziała: hash[hey] = nil

 -7
Author: fdghdfg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-12 18:43:13