как удалить ключ из hash и получить оставшийся хэш в ruby / rails?

<p>чтобы добавить новую пару в hash, я делаю:</p><pre><code>{:a => 1, :b => 2}.merge!({:c => 3}) #=> {:a => 1, :b => 2, :c => 3} </code></pre><p>есть ли аналогичный способ удалить ключ из hash?</p><p>это работает:</p><pre><code>{:a => 1, :b => 2}.reject! { |k| k == :a } #=> {:b => 2} </code></pre><p>но я бы ожидал что-то вроде:</p><pre><code>{:a => 1, :b => 2}.delete!(:a) #=> {:b => 2} </code></pre><p>важно, чтобы возвращаемое значение было оставшимся хешем, поэтому я мог бы сделать что-то вроде:</p><pre><code>foo(my\_hash.reject! { |k| k == my\_key }) </code></pre><p>в одну строку.</p>

<p> <a href="http://api.rubyonrails.org/classes/hash.html#method-i-except">в rails есть метод исключения/исключение!</a> , который возвращает хеш с удаленными ключами. если вы уже используете rails, нет смысла создавать собственную версию этого.</p><pre><code>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 </code></pre>

<p>единственный простой ruby, он работает только с ruby> 1.9.x:</p><pre><code>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} </code></pre><p> <a href="http://blog.moertel.com/articles/2007/02/07/ruby-1-9-gets-handy-new-method-object-tap">tap</a> метод всегда возвращает объект, для которого вызывается ...</p><p>в противном случае, если вам требуется <code>active\_support/core\_ext/hash</code> (который автоматически требуется в каждом приложении rails), вы можете использовать один из следующих методов в зависимости от ваших потребностей:</p><pre><code>➜ ~ 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} </code></pre><p> <a href="http://as.rubyonrails.org/classes/activesupport/coreextensions/hash/except.html">кроме</a> использует подход черного списка, поэтому он удаляет все ключи, перечисленные как аргументы, в то время как <a href="http://as.rubyonrails.org/classes/activesupport/coreextensions/hash/slice.html">slice</a> использует подход белого списка, поэтому он удаляет все ключи, которые не перечислены в качестве аргументов. существует также версия взрыва этих методов (<code>except!</code> и <code>slice!</code>), которые модифицируют данный хеш, но их возвращаемое значение отличается, оба возвращают хеш. он представляет удаленные ключи для <code>slice!</code> и ключи, которые хранятся для <code>except!</code>:</p><pre><code>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} </code></pre>

<p>почему бы просто не использовать:</p><pre><code>hash.delete(key) </code></pre>

<p>есть много способов удалить ключ из хеша и получить оставшийся хеш в ruby.</p><ol> <li> <p><code>.slice</code> => он вернет выбранные ключи и не удалит их из исходного хэша</p> <pre><code>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} </code></pre></li> <li> <p><code>.delete</code> => он удалит выбранные ключи из исходного хэша (он может принять только один ключ и не более одного)</p> <pre><code>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} </code></pre></li> <li> <p><code>.except</code> => он вернет оставшиеся ключи, но не удалит ничего из исходного хэша</p> <pre><code>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} </code></pre></li> <li> <p><code>.delete\_if</code> => в случае, если вам нужно удалить ключ на основе значения. это, очевидно, удалит соответствующие ключи из оригинального хеша</p> <pre><code>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} </code></pre></li> </ol><p>результаты на основе ruby 2.2.2.</p>

<p>если вы хотите использовать чистый ruby (без rails), не нужно создавать методы расширения (возможно, вам это нужно только в одном или двух местах, и вы не хотите загрязнять пространство имен множеством методов) и не хотите редактировать хеш на месте (то есть, вы любите функциональное программирование, как я), вы можете "выбрать":</p><pre><code>>> 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} </code></pre>

<pre><code>#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' </code></pre><p>я настроил это так, что .remove возвращает копию хэша с удаленными ключами при удалении! изменяет сам хэш это соответствует правилам ruby. например, из консоли</p><pre><code>>> 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) => {} </code></pre>

<p>вы можете использовать <code>except!</code> из гема <code>facets</code>:</p><pre><code>>> require 'facets' # or require 'facets/hash/except' => true >> {:a => 1, :b => 2}.except(:a) => {:b=>2} </code></pre><p>оригинальный хеш не меняется.</p><p>правка: как говорит рассел, аспекты имеют некоторые скрытые проблемы и не полностью api-совместимы с activesupport. с другой стороны activesupport не так полон, как аспекты. в конце концов, я бы использовал as и включил edge в ваш код.</p>

<p>вместо того, чтобы вносить исправления обезьяны или без необходимости включать большие библиотеки, вы можете использовать <a href="https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/">уточнения, если вы используете ruby 2</a> :</p><pre><code>module hashextensions refine hash do def except!(*candidates) candidates.each { |candidate| delete(candidate) } self end def except(*candidates) dup.remove!(candidates) end end end </code></pre><p>вы можете использовать эту функцию, не затрагивая другие части вашей программы или не включая большие внешние библиотеки.</p><pre><code>class fabulouscode using hashextensions def incredible\_stuff delightful\_hash.except(:not\_fabulous\_key) end end </code></pre>

<p>в чистом рубине</p><pre><code>{:a => 1, :b => 2}.tap{|x| x.delete(:a)} # => {:b=>2} </code></pre>

<p>смотрите <a href="https://stackoverflow.com/questions/1560572/ruby-delete-multiple-hash-keys">ruby on rails: удалить несколько ключей хеша</a> </p><pre><code>hash.delete\_if{ |k,| keys\_to\_delete.include? k } </code></pre>

<p>было бы здорово, если delete вернул пару удаления хэша. я делаю это:</p><pre><code>hash = {a: 1, b: 2, c: 3} {b: hash.delete(:b)} # => {:b=>2} hash # => {:a=>1, :c=>3} </code></pre>

<p>это однострочный способ сделать это, но он не очень читабелен. рекомендуется использовать две строки вместо.</p><pre><code>use\_remaining\_hash\_for\_something(proc.new { hash.delete(:key); hash }.call) </code></pre>

<p>несколько способов удалить key in hash. вы можете использовать любой метод снизу</p><pre><code>hash = {a: 1, b: 2, c: 3} hash.except!(:a) # will remove *a* and return hash hash # output :- {b: 2, c: 3} hash = {a: 1, b: 2, c: 3} hash.delete(:a) # will remove *a* and return 1 if *a* not present than return nil </code></pre><p>есть так много способов, вы можете посмотреть на ruby doc of hash <a href="https://docs.ruby-lang.org/en/2.0.0/hash.html#method-i-reject">здесь</a> .</p><p>спасибо</p>