해시에서 키를 제거하고 Ruby / Rails에서 나머지 해시를 얻는 방법은 무엇입니까?


560

해시에 새 쌍을 추가하려면 다음을 수행하십시오.

{: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
이 해시가 필요할 경우 항상 내장 해시를 확장 (런타임에 열림)하여이 커스텀 메소드를 추가 할 수 있습니다.
dbryson

답변:


750

레일즈에는 예외 / 제외가 있습니다! 해당 키가 제거 된 해시를 반환하는 메서드 입니다. 이미 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 스택을 사용할 필요는 없습니다. 모든 Ruby 애플리케이션에 ActiveSupport를 포함시킬 수 있습니다.
Fryie

10
Fryie의 답변에 추가하기 위해 모든 ActiveSupport를로드 할 필요조차 없습니다. 당신은 다음을 포함 할 수 있습니다require "active_support/core_ext/hash/except"
GMA

너무 늦게 편집 : "보석 포함"이 아니라 "보석 포함"
GMA

@GMA : 5 분 분량의 편집이 완료되면 언제든지 주석을 복사, 삭제, 수정 및 다시 게시 할 수 있습니다.
iconoclast

212

Oneliner 일반 루비, 루비> 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} 

Tap 메소드는 항상 호출 된 객체를 반환합니다 ...

그렇지 않으면 필요한 경우 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} 

제외 는 블랙리스트 접근 방식을 사용하므로 인수로 나열된 모든 키를 제거 하고 slice 는 화이트리스트 접근 방식을 사용하므로 인수로 나열되지 않은 모든 키를 제거합니다. 주어진 해시를 수정 하는 메소드 ( 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!...
미샤 Moroshko

1
만약 당신이 정말로 하나의 라이너처럼 그것을 원한다면 :foo(hash.delete(key) || hash)
Bert Goethals

13
매개 변수를 수정 delete하지 않았 거나 delete!존재하고 매개 변수를 수정 한 경우 Ruby 규칙과 더 일관성이 있습니다 .
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!제거 하려면 사용하십시오 . nil그렇지 않으면 simple을 사용하십시오 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}

Ruby 2.2.2를 기반으로 한 결과.


16
sliceexcept사용에 의해 추가된다 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 예!
Madis Nõmme 2016

38

순수 루비 (레일 없음)를 사용하려면 확장 메소드를 작성하지 말고 (한두 곳에서만 필요하며, 수많은 메소드로 네임 스페이스를 오염시키지 않으려는 경우) 원하지 않습니다. 해시를 제자리에서 편집하십시오 (즉, 나와 같은 기능적 프로그래밍의 팬입니다).

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

.remove가 키를 제거한 상태로 해시 사본을 반환하고 제거하도록 이것을 설정했습니다! 해시 자체를 수정합니다. 이것은 루비 규칙과 일치합니다. 예를 들어 콘솔에서

>> 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이 말했듯이 패싯에는 숨겨진 문제가 있으며 ActiveSupport와 완전히 API 호환되지 않습니다. 반면에 ActiveSupport는 패싯만큼 완벽하지 않습니다. 결국, 나는 AS를 사용하고 코드에서 가장 중요한 사례를 보자.


정의 require 'facets/hash/except'와 그 자체는 "문제"가 아닙니다 (100 % AS API 이외의 문제가 무엇인지 확실하지 않음). AS를 사용하여 Rails 프로젝트를 수행하는 것이 의미가있는 경우, Facets는 훨씬 작은 풋 프린트를 갖습니다.
trans

@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

17

순수한 루비에서 :

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


3

delete가 해시의 삭제 쌍을 반환하면 좋았습니다. 나는 이것을하고있다 :

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

여러 가지 방법이 있습니다 .Hash의 Ruby 문서를 여기 에서 볼 수 있습니다 .

감사합니다


-12

이것은 또한 작동합니다 : hash[hey] = nil


3
h = {: a => 1, : b => 2, : c => 3}; h [: a] = nil; h. 각각 {| k, v | ks를 씁니다. h = {: a => 1, : b => 2, : c => 3}; h.delete (: a); h. 각각 {| k, v | k}
obaqueiro

1
해시에서 키를 제거하는 것은 해시에서 키 값을 제거하는 것과 다릅니다. 이로 인해 사람들이 혼동을 일으킬 수 있으므로이 답변을 삭제하는 것이 좋습니다.
Sebastian Palma
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.