[ruby] 해시에서 하위 해시를 어떻게 추출합니까?

해시가 있습니다.

h1 = {:a => :A, :b => :B, :c => :C, :d => :D}

이와 같은 하위 해시를 추출하는 가장 좋은 방법은 무엇입니까?

h1.extract_subhash(:b, :d, :e, :f) # => {:b => :B, :d => :D}
h1 #=> {:a => :A, :c => :C}



답변

메서드가 추출 된 요소를 반환하지만 h1은 동일하게 유지하도록하려는 경우 :

h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2 = h1.select {|key, value| [:b, :d, :e, :f].include?(key) } # => {:b=>:B, :d=>:D} 
h1 = Hash[h1.to_a - h2.to_a] # => {:a=>:A, :c=>:C} 

그리고 그것을 Hash 클래스에 패치하려면 :

class Hash
  def extract_subhash(*extract)
    h2 = self.select{|key, value| extract.include?(key) }
    self.delete_if {|key, value| extract.include?(key) }
    h2
  end
end

해시에서 지정된 요소를 제거하려는 경우 delete_if를 사용하는 것이 훨씬 쉽습니다 .

h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h1.delete_if {|key, value| [:b, :d, :e, :f].include?(key) } # => {:a=>:A, :c=>:C} 
h1  # => {:a=>:A, :c=>:C} 


답변

ActiveSupport는 최소한 2.3.8부터 4 가지 편리한 방법을 제공합니다 : #slice, #except그리고 파괴적인 대응 방법 : #slice!#except!. 그들은 다른 답변에서 언급되었지만 한곳에서 요약했습니다.

x = {a: 1, b: 2, c: 3, d: 4}
# => {:a=>1, :b=>2, :c=>3, :d=>4}

x.slice(:a, :b)
# => {:a=>1, :b=>2}

x
# => {:a=>1, :b=>2, :c=>3, :d=>4}

x.except(:a, :b)
# => {:c=>3, :d=>4}

x
# => {:a=>1, :b=>2, :c=>3, :d=>4}

bang 메서드의 반환 값에 유의하십시오. 기존 해시를 조정할뿐만 아니라 제거 된 (보관되지 않은) 항목도 반환합니다. Hash#except!에 가장 적합한 질문에 주어진 예 :

x = {a: 1, b: 2, c: 3, d: 4}
# => {:a=>1, :b=>2, :c=>3, :d=>4}

x.except!(:c, :d)
# => {:a=>1, :b=>2}

x
# => {:a=>1, :b=>2}

ActiveSupport전체 레일이 필요하지 않으며 매우 가볍습니다. 사실, 많은 non-rails gem이 이것에 의존하기 때문에 아마도 이미 Gemfile.lock에있을 것입니다. 스스로 Hash 클래스를 확장 할 필요가 없습니다.


답변

당신이 레일을 사용하는 경우 , 해시 # 슬라이스 길을 가야하는 것입니다.

{:a => :A, :b => :B, :c => :C, :d => :D}.slice(:a, :c)
# =>  {:a => :A, :c => :C}

당신이 레일을 사용하지 않는 경우 , 당신이 그들에게 물었다 같은 해시 #의 values_at는 동일한 순서로 값을 반환합니다 이 작업을 수행 할 수 있습니다 :

def slice(hash, *keys)
  Hash[ [keys, hash.values_at(*keys)].transpose]
end

def except(hash, *keys)
  desired_keys = hash.keys - keys
  Hash[ [desired_keys, hash.values_at(*desired_keys)].transpose]
end

전의:

slice({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2)
# => {'bar' => 'foo', 2 => 'two'}

except({foo: 'bar', 'bar' => 'foo', 2 => 'two'}, 'bar', 2)
# => {:foo => 'bar'}

설명:

의 아웃 {:a => 1, :b => 2, :c => 3}우리가 원하는{:a => 1, :b => 2}

hash = {:a => 1, :b => 2, :c => 3}
keys = [:a, :b]
values = hash.values_at(*keys) #=> [1, 2]
transposed_matrix =[keys, values].transpose #=> [[:a, 1], [:b, 2]]
Hash[transposed_matrix] #=> {:a => 1, :b => 2}

원숭이 패치가 갈 길이라고 생각되면 다음을 수행하십시오.

module MyExtension
  module Hash
    def slice(*keys)
      ::Hash[[keys, self.values_at(*keys)].transpose]
    end
    def except(*keys)
      desired_keys = self.keys - keys
      ::Hash[[desired_keys, self.values_at(*desired_keys)].transpose]
    end
  end
end
Hash.include MyExtension::Hash


답변

Ruby 2.5 추가 Hash # slice :

h = { a: 100, b: 200, c: 300 }
h.slice(:a)           #=> {:a=>100}
h.slice(:b, :c, :d)   #=> {:b=>200, :c=>300}


답변

ActiveSupport의 핵심 확장에서 사용할 수있는 slice! (* keys)를 사용할 수 있습니다.

initial_hash = {:a => 1, :b => 2, :c => 3, :d => 4}

extracted_slice = initial_hash.slice!(:a, :c)

initial_hash는 이제

{:b => 2, :d =>4}

extract_slide는 이제

{:a => 1, :c =>3}

당신은 볼 수 있습니다 slice.rb in ActiveSupport 3.1.3


답변

module HashExtensions
  def subhash(*keys)
    keys = keys.select { |k| key?(k) }
    Hash[keys.zip(values_at(*keys))]
  end
end

Hash.send(:include, HashExtensions)

{:a => :A, :b => :B, :c => :C, :d => :D}.subhash(:a) # => {:a => :A}


답변

h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
keys = [:b, :d, :e, :f]

h2 = (h1.keys & keys).each_with_object({}) { |k,h| h.update(k=>h1.delete(k)) }
  #=> {:b => :B, :d => :D}
h1
  #=> {:a => :A, :c => :C}