วิธีลบคีย์ออกจากแฮชและรับแฮชที่เหลือใน Ruby / Rails


560

หากต้องการเพิ่มคู่ใหม่ใน Hash ฉัน:

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

มีวิธีคล้ายกันในการลบคีย์ออกจากแฮชหรือไม่

งานนี้:

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

แต่ฉันคาดหวังว่าจะมีสิ่งที่ชอบ:

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

เป็นสิ่งสำคัญที่ค่าส่งคืนจะเป็นแฮชที่เหลืออยู่ดังนั้นฉันสามารถทำสิ่งต่าง ๆ เช่น:

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

ในหนึ่งบรรทัด


1
คุณสามารถขยาย (เปิดที่รันไทม์) สร้างขึ้นใน Hash เพื่อเพิ่มวิธีการที่กำหนดเองนี้หากคุณต้องการจริงๆ
dbryson

คำตอบ:


750

Rails มีข้อยกเว้น / ยกเว้น! เมธอดที่ส่งคืนแฮชโดยลบคีย์เหล่านั้น หากคุณใช้ Rails อยู่แล้วคุณจะไม่สามารถสร้างเวอร์ชันนี้ได้

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
คุณไม่ต้องใช้ Rails stack แบบเต็ม ๆ คุณสามารถรวมการรวม ActiveSupport ไว้ในแอปพลิเคชัน Ruby
ฟราย

10
หากต้องการเพิ่มคำตอบของ Fryie คุณไม่จำเป็นต้องโหลด ActiveSupport ทั้งหมด คุณสามารถรวมพวกเขาได้แล้วrequire "active_support/core_ext/hash/except"
GMA

สายเกินไปที่จะแก้ไข: ฉันหมายถึง "รวมถึงอัญมณี" ไม่ "รวมไว้ด้วย"
GMA

@GMA: เมื่อการแก้ไขห้านาทีของคุณหมดลงคุณสามารถคัดลอกลบแก้ไขและโพสต์ความคิดเห็นใหม่ได้ตลอดเวลา
iconoclast

212

Oneliner ruby ​​ธรรมดาใช้งานได้กับ 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} 

วิธีการแตะจะส่งคืนวัตถุที่ถูกเรียก ...

มิฉะนั้นถ้าคุณต้องการactive_support/core_ext/hash(ซึ่งจำเป็นโดยอัตโนมัติในทุกแอปพลิเคชั่น Rails) คุณสามารถใช้หนึ่งในวิธีต่อไปนี้ขึ้นอยู่กับความต้องการของคุณ:

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

ยกเว้นใช้วิธีบัญชีดำดังนั้นมันจะลบคีย์ทั้งหมดที่แสดงรายการเป็น args ในขณะที่sliceใช้วิธี whitelist ดังนั้นมันจะลบคีย์ทั้งหมดที่ไม่อยู่ในรายการเป็นอาร์กิวเมนต์ นอกจากนี้ยังมีบางรุ่นของวิธีการเหล่านั้น ( except!และslice!) ซึ่งแก้ไขแฮชที่กำหนด แต่ค่าส่งคืนของพวกเขาจะแตกต่างกันทั้งสองพวกเขากลับแฮช เพราะมันหมายถึงกุญแจที่ถูกลบสำหรับslice!และกุญแจที่ถูกเก็บไว้สำหรับ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 hมันมูลค่าการกล่าวขวัญว่าวิธีนี้คือการทำลายล้างใน Hash#exceptจะไม่แก้ไขแฮชดั้งเดิม
ขอบคุณ

3
ใช้h.dup.tap { |hs| hs.delete(:a) }เพื่อหลีกเลี่ยงการดัดแปลงแฮชดั้งเดิม
Magicode

181

ทำไมไม่ใช้เพียง:

hash.delete(key)

2
@ dbryson: ฉันยอมรับว่าบางครั้งมันก็ไม่คุ้มค่า ฉันเพียงแค่สงสัยว่าทำไมมีmerge, merge!, deleteแต่ไม่มีdetele!...
Misha Moroshko

1
ถ้าคุณจริงๆต้องเป็นหนึ่งซับทำ:foo(hash.delete(key) || hash)
เบิร์ต Goethals

13
มันจะสอดคล้องกับข้อตกลง Ruby มากขึ้นหากdeleteไม่ได้แก้ไขพารามิเตอร์และหากdelete!มีอยู่และแก้ไขพารามิเตอร์ของมัน
David J.

60
สิ่งนี้จะไม่ส่งคืนแฮชที่เหลือตามที่กล่าวถึงในคำถาม แต่จะคืนค่าที่เกี่ยวข้องกับคีย์ที่ถูกลบ
MhdSyrwan

1
delete ส่งคืนคีย์ แต่จะเปลี่ยนแฮชด้วย สำหรับสาเหตุที่ไม่มีการลบ! ฉันเดาว่ามันไม่มีความหมายที่จะเรียกการลบในบางสิ่งและไม่ได้ลบอย่างแท้จริง การเรียก hash.delete () ซึ่งตรงข้ามกับ hash.delete! () จะไม่ใช่การใช้งาน
eggmatters

85

มีหลายวิธีในการลบคีย์ออกจากแฮชและรับแฮชที่เหลือใน Ruby

  1. .slice=> มันจะส่งคืนคีย์ที่เลือกและไม่ลบออกจากแฮชดั้งเดิม ใช้ถ้าคุณต้องการที่จะลบปุ่มอื่นอย่างถาวรใช้งานง่ายslice!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 => มันจะลบคีย์ที่เลือกจากแฮชดั้งเดิม (สามารถยอมรับได้เพียงหนึ่งคีย์เท่านั้นและไม่เกินหนึ่งคีย์)

    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=> มันจะคืนกุญแจที่เหลืออยู่ แต่จะไม่ลบอะไรออกจากแฮชดั้งเดิม ใช้ถ้าคุณต้องการที่จะลบปุ่มอื่นอย่างถาวรใช้งานง่ายexcept!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=> ในกรณีที่คุณจำเป็นต้องลบคีย์ตามค่า เห็นได้ชัดว่ามันจะลบคีย์การจับคู่จากแฮชดั้งเดิม

    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=> มันถูกใช้เพื่อลบnilค่าทั้งหมดออกจากแฮช ใช้compact!ถ้าคุณต้องการที่จะลบค่าอื่นอย่างถาวรใช้งานง่ายnilcompact

    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}

ผลลัพธ์จาก Ruby 2.2.2


16
sliceและมีการเพิ่มโดยใช้except ActiveSupport::CoreExtensions::Hashพวกเขาไม่ได้เป็นส่วนหนึ่งของแกนทับทิม สามารถใช้งานได้โดยrequire 'active_support/core_ext/hash'
Madis Nõmme

3
เนื่องจาก Ruby 2.5 Hash#sliceอยู่ในไลบรารีมาตรฐาน ruby-doc.org/core-2.5.0/Hash.html#method-i-slice Yay!
Nõmme

38

หากคุณต้องการใช้ Ruby บริสุทธิ์ (ไม่มี Rails) ไม่ต้องการสร้างวิธีการขยาย (บางทีคุณอาจต้องการเพียงหนึ่งหรือสองแห่งและไม่ต้องการทำให้เนมสเปซสกปรกด้วยวิธีการมากมาย) และไม่ต้องการ แก้ไขแฮชในสถานที่ (เช่นคุณเป็นแฟนของการเขียนโปรแกรมการทำงานเช่นฉัน) คุณสามารถ 'เลือก':

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

ฉันได้ตั้งค่านี้ขึ้นเพื่อให้นำออกคืนสำเนาแฮชโดยลบกุญแจออกขณะที่ลบ! แก้ไขแฮชตัวเอง นี่คือการรักษาด้วยการประชุมทับทิม เช่นจากคอนโซล

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

คุณสามารถใช้except!จากfacetsอัญมณี:

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

แฮชดั้งเดิมไม่เปลี่ยนแปลง

แก้ไข: ดังที่ Russel กล่าวว่า facets มีปัญหาที่ซ่อนอยู่บางส่วนและไม่สามารถใช้งานกับ API ได้อย่างสมบูรณ์กับ ActiveSupport ในอีกด้านหนึ่ง ActiveSupport ยังไม่สมบูรณ์เท่ากับ facets ในท้ายที่สุดฉันจะใช้ AS และให้ขอบตัวอักษรในรหัสของคุณ


เพียงแค่require 'facets/hash/except'และพวกเขาไม่ใช่ "ปัญหา" (ไม่แน่ใจว่าประเด็นใดที่พวกเขาจะต้องนอกเหนือจาก AS API ไม่ 100%) หากคุณกำลังทำโครงการ Rails โดยใช้ AS เหมาะสมถ้าไม่ใช่ Facets จะมีขนาดเล็กกว่ามาก
ทรานส์

@trans ActiveSupport ในปัจจุบันมีขนาดเล็กเกินไปและคุณต้องการเพียงบางส่วนเท่านั้น เช่นเดียวกับแง่มุม แต่ด้วยสายตาอีกมากมาย (ดังนั้นฉันคิดว่ามันจะได้รับรีวิวที่ดีกว่า)
เขียนใหม่

19

แทนที่จะทำการปะของลิงหรือรวมถึงไลบรารีขนาดใหญ่โดยไม่จำเป็นคุณสามารถใช้การปรับแต่งหากคุณใช้ 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

คุณสามารถใช้คุณสมบัตินี้ได้โดยไม่กระทบต่อส่วนอื่น ๆ ของโปรแกรมหรือต้องมีไลบรารี่ภายนอกขนาดใหญ่

class FabulousCode
  using HashExtensions

  def incredible_stuff
    delightful_hash.except(:not_fabulous_key)
  end
end


13

ดูRuby on Rails: ลบหลายแฮชคีย์

hash.delete_if{ |k,| keys_to_delete.include? k }

keys_to_delete.each {| k | hash.delete (k)} เร็วกว่ามากสำหรับชุดข้อมูลขนาดใหญ่ แก้ไขฉันถ้าผิด
Vignesh Jayavel

@ VigneshJayavel คุณพูดถูก แต่ OP ต้องการให้แฮชส่งคืน eachจะคืนอาร์เรย์
Nakilon

3

มันเยี่ยมมากถ้าลบคืนค่าลบคู่ของแฮช ฉันทำสิ่งนี้:

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

1

นี่เป็นวิธีหนึ่งในการทำ แต่ไม่สามารถอ่านได้มาก แนะนำให้ใช้สองบรรทัดแทน

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

1
Hash#exceptและHash#except!ได้รับการกล่าวถึงเพียงพอแล้ว รุ่นไม่สามารถอ่านได้มากเท่าที่คุณพูดถึงและมีความซับซ้อนมากกว่าProc.new use_remaining_hash_for_something(begin hash.delete(:key); hash end)อาจเพียงแค่ลบคำตอบนี้
Michael Kohl

1
ทำให้คำตอบของฉันสั้นลงและนำสิ่งที่พูดไปแล้วออก รักษาคำตอบของฉันพร้อมกับความคิดเห็นของคุณเพราะพวกเขาตอบคำถามและให้คำแนะนำที่ดีสำหรับการใช้งาน
the_minted

0

การลบคีย์ในแฮชได้หลายวิธี คุณสามารถใช้วิธีการใดก็ได้จากด้านล่าง

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

ดังนั้นหลายวิธีที่จะมีคุณสามารถมองในเอกสารทับทิมของกัญชาที่นี่

ขอบคุณ


-12

สิ่งนี้จะได้ผล: hash[hey] = nil


3
h = {: a => 1,: b => 2,: c => 3}; H [ก] = ศูนย์; h.each {| k, วี | ทำให้ k} ไม่เหมือนกับ: h = {: a => 1,: b => 2,: c => 3}; h.delete (ก); h.each {| k, วี | ทำให้ k}
obaqueiro

1
ในการลบคีย์ออกจากแฮชจะไม่เหมือนกับการลบค่าของคีย์ออกจากแฮช เนื่องจากอาจทำให้ผู้คนสับสนได้ดีกว่าที่จะลบคำตอบนี้ออก
เซบาสเตียนปัลมา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.