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

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

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

한 줄에.



답변

레일즈에는 예외 / 제외가 있습니다! 해당 키가 제거 된 해시를 반환하는 메서드 입니다. 이미 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


답변

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} 


답변

왜 사용하지 않습니까?

hash.delete(key)


답변

해시에서 키를 제거하고 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를 기반으로 한 결과.


답변

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

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


답변

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


답변

보석 except!에서 사용할 수 있습니다 facets:

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

원래 해시는 변경되지 않습니다.

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