Bir anahtarı Hash'den nasıl çıkarabilir ve Ruby / Rails'te kalan karmayı nasıl alabilirim?


560

Hash'e yeni bir çift eklemek için:

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

Hash'ten bir anahtarı silmenin benzer bir yolu var mı?

Bu çalışıyor:

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

ama şöyle bir şey olmasını beklerdim:

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

Dönen değerin kalan karma olması önemlidir, bu yüzden aşağıdaki gibi şeyler yapabilirim:

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

tek satırda.


1
Gerçekten ihtiyacınız varsa bu özel yöntemi eklemek için her zaman yerleşik Hash içinde genişletebilirsiniz (çalışma zamanında açık).
dbryson

Yanıtlar:


750

Raylar hariç / hariç! bu anahtarlar kaldırıldığında karma değerini döndüren yöntem . Zaten Rails kullanıyorsanız, kendi sürümünüzü oluşturmanın bir anlamı yoktur.

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

51
Tam Rails yığınını kullanmak zorunda değilsiniz. ActiveSupport'u herhangi bir Ruby uygulamasına dahil edebilirsiniz.
Fryie

10
Fryie'nin cevabına eklemek için tüm ActiveSupport'u yüklemenize bile gerek yok; bunları daha sonra dahil edebilirsinizrequire "active_support/core_ext/hash/except"
GMA

düzenlemek için çok geç: "taş dahil" demek istedim "dahil değil"
GMA

@GMA: Beş dakikalık düzenleme süreniz dolduğunda, her zaman bir yorumu kopyalayabilir, silebilir, değiştirebilir ve yeniden gönderebilirsiniz.
iconoclast

212

Oneliner düz yakut, sadece yakut> 1.9.x ile çalışır:

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} 

Dokunma yöntemi her zaman çağrıldığı nesneyi döndürür ...

Aksi takdirde active_support/core_ext/hash(her Rails uygulamasında otomatik olarak gerekli olan) gerekiyorsa, ihtiyaçlarınıza bağlı olarak aşağıdaki yöntemlerden birini kullanabilirsiniz:

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

dışında bir kara liste yaklaşımı kullanır, bu nedenle argümanlar olarak listelenmeyen tüm anahtarları kaldırırken, dilim beyaz liste yaklaşımı kullanır, bu nedenle bağımsız değişken olarak listelenmeyen tüm anahtarları kaldırır. Ayrıca , verilen yöntemi değiştiren bu yöntemin ( except!ve slice!) patlama sürümü de vardır, ancak dönüş değerleri farklıdır ve her ikisi de bir karma döndürür. İçin kaldırılan anahtarları slice!ve aşağıdakiler için saklanan anahtarları temsil eder 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} 

18
+1 Bu yöntemin yıkıcı olduğunu belirtmek gerekir h. Hash#exceptorijinal karmayı değiştirmez.
Teşekkürler

3
h.dup.tap { |hs| hs.delete(:a) }Orijinal karmayı değiştirmekten kaçınmak için kullanın .
Magicode

181

Neden sadece kullanmıyorsunuz:

hash.delete(key)

2
@dbryson: Bazen buna değmeyeceğine katılıyorum. Gerçekten merak ediyorum orada neden merge, merge!, delete, ama hiç detele!...
Misha Moroshko

1
gerçekten bir astar olarak ihtiyacınız varsa:foo(hash.delete(key) || hash)
Bert Goethals

13
Eğer Yakut sözleşmeleri ile daha tutarlı olurdu deletevermedi değil onun parametreyi değiştirmek ve eğer delete!var olduğunu ve bunun parametreyi değiştirmek yaptı.
David J.

60
Bu, soruda belirtildiği gibi kalan karmayı döndürmez, silinen anahtarla ilişkili değeri döndürür.
MhdSyrwan

1
delete anahtarı döndürür ancak karmayı da değiştirir. Neden silme yok! İle ilgili olarak, sanırım anlamsal olarak bir şey üzerinde silme işlemini çağırmanın ve gerçekten silmemenin mantıklı olmadığıdır. hash.delete () yerine hash.delete! () öğesini çağırmak işlem yapılmaz.
Eggmatters

85

Bir anahtarı bir karmadan kaldırmanın ve Ruby'de kalan karmayı almanın birçok yolu vardır.

  1. .slice=> Seçilen anahtarları döndürür ve orijinal karmadan silmez. slice!Anahtarları kalıcı olarak kaldırmak istiyorsanız kullanın, aksi takdirde basit kullanın slice.

    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 => Seçilen anahtarları orijinal karmadan silecektir (yalnızca bir anahtarı kabul edebilir ve birden fazla kabul edemez).

    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=> Kalan anahtarları döndürür, ancak orijinal karmadan hiçbir şeyi silmez. except!Anahtarları kalıcı olarak kaldırmak istiyorsanız kullanın, aksi takdirde basit kullanın except.

    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=> Bir değere dayalı bir anahtarı kaldırmanız gerektiğinde. Açıkça eşleşen anahtarları orijinal karmadan kaldıracaktır.

    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} 
  5. .compact=> Tüm nildeğerleri karmadan kaldırmak için kullanılır . Kullanım compact!kaldırmak isterseniz nilkalıcı başka kullanım basit değerlerini compact.

    2.2.2 :119 > hash = {"one"=>1, "two"=>2, "three"=>3, "nothing"=>nil, "no_value"=>nil}
     => {"one"=>1, "two"=>2, "three"=>3, "nothing"=>nil, "no_value"=>nil} 
    2.2.2 :120 > hash.compact
     => {"one"=>1, "two"=>2, "three"=>3}

Sonuçlar Ruby 2.2.2'ye dayanmaktadır.


16
sliceve exceptkullanılarak eklenir ActiveSupport::CoreExtensions::Hash. Ruby çekirdeğinin bir parçası değiller. Onlar tarafından kullanılabilirrequire 'active_support/core_ext/hash'
Madis Nõmme

3
Ruby 2.5 Hash#slicestandart kütüphanede olduğundan. ruby-doc.org/core-2.5.0/Hash.html#method-i-slice Yay!
Madis Nõmme

38

Saf Ruby (Rails yok) kullanmak istiyorsanız, uzantı yöntemleri oluşturmak istemeyin (belki de sadece bir veya iki yerde buna ihtiyacınız vardır ve ad alanını tonlarca yöntemle kirletmek istemezsiniz) ve hash in place (yani benim gibi fonksiyonel programlamanın hayranısınız), 'seç' yapabilirsiniz:

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

30
#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'

Bunu kaldırdım, böylece .remove kaldırırken anahtarları kaldırılmış olan karma bir kopyasını döndürür! karma değerini değiştirir. Bu yakut sözleşmelerle uyumludur. örneğin, konsoldan

>> 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)
=> {}

26

Sen kullanabilirsiniz except!gelen facetsmücevher:

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

Orijinal karma değişmez.

EDIT: Russel'in dediği gibi, yönlerin bazı gizli sorunları var ve ActiveSupport ile tamamen API uyumlu değil. Diğer yandan ActiveSupport, fasetler kadar eksiksiz değil. Sonunda, AS kullanmak ve kod kenar durumlarda izin.


Sadece require 'facets/hash/except've onların hiçbir "sorunları" (onlar% 100 AS API dışında zaten ne olacağını onlar emin değilim) vardır. AS kullanarak bir Rails projesi yapıyorsanız, mantıklı değilse, Facets çok daha az yer kaplar.
trans

@trans ActiveSupport günümüzde oldukça az yer kaplamaktadır ve yalnızca bir kısmına ihtiyaç duyabilirsiniz. Sadece fasetler gibi, ama daha birçok göz ile (bu yüzden daha iyi değerlendirmeleri alır varsayalım).
yeniden yazıldı

19

Maymun yamalama yerine veya gereksiz yere büyük kütüphaneler dahil olmak üzere, Ruby 2 kullanıyorsanız ayrıntılandırmaları kullanabilirsiniz :

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

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

Bu özelliği, programınızın diğer bölümlerini etkilemeden veya büyük harici kitaplıklar eklemek zorunda kalmadan kullanabilirsiniz.

class FabulousCode
  using HashExtensions

  def incredible_stuff
    delightful_hash.except(:not_fabulous_key)
  end
end

17

saf Ruby'de:

{:a => 1, :b => 2}.tap{|x| x.delete(:a)}   # => {:b=>2}


3

Silme işleminin karma silme çiftini döndürmesi harikaydı. Bunu yapıyorum:

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

1

Bu, bunu yapmanın tek bir yoludur, ancak çok okunabilir değildir. Bunun yerine iki satır kullanmanızı öneririz.

use_remaining_hash_for_something(Proc.new { hash.delete(:key); hash }.call)

1
Hash#exceptve Hash#except!zaten yeterince bahsedilmişti. Proc.newEğer söz ve ayrıca birden fazla komplike olarak sürüm çok okunabilir değil use_remaining_hash_for_something(begin hash.delete(:key); hash end). Belki sadece bu cevabı sil.
Michael Kohl

1
Cevabımı kısalttı ve söylenenleri sildi. Cevabınızı yorumunuzla birlikte tutmak çünkü soruyu cevaplıyor ve kullanım için iyi önerilerde bulunuyorlar.
the_minted

0

Hash içindeki Anahtarı silmenin birden çok yolu. aşağıdan herhangi bir yöntemi kullanabilirsiniz

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

Pek çok yol var, burada Ruby Ruby of Doc belgesine buradan bakabilirsiniz .

teşekkür ederim


-12

Bu da işe yarar: hash[hey] = nil


3
h = {: a => 1,: b => 2,: c => 3}; h [a] = sıfırdır; h.each {| k v | koyar k} ile aynı değildir: h = {: a => 1,: b => 2,: c => 3}; h.delete (a); h.each {| k v | k}
obaqueiro

1
Bir anahtarı bir karmadan kaldırmak, bir anahtarın değerini bir karma değerinden kaldırmakla aynı şey değildir. Bu, insanların kafasını karıştırmasına neden olabileceğinden, bu yanıtı kaldırmak daha iyi olur.
Sebastian Palma
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.