해시에서 하위 해시를 어떻게 추출합니까?
해시가 있습니다.
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}
if you use rails, it may be convenient to use Hash.except
h = {a:1, b:2}
h1 = h.except(:a) # {b:2}
class Hash
def extract(*keys)
key_index = Hash[keys.map{ |k| [k, true] }] # depends on the size of keys
partition{ |k, v| key_index.has_key?(k) }.map{ |group| Hash[group] }
end
end
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2, h1 = h1.extract(:b, :d, :e, :f)
Here is a quick performance comparison of the suggested methods, #select
seems to be the fastest
k = 1_000_000
Benchmark.bmbm do |x|
x.report('select') { k.times { {a: 1, b: 2, c: 3}.select { |k, _v| [:a, :b].include?(k) } } }
x.report('hash transpose') { k.times { Hash[ [[:a, :b], {a: 1, b: 2, c: 3}.fetch_values(:a, :b)].transpose ] } }
x.report('slice') { k.times { {a: 1, b: 2, c: 3}.slice(:a, :b) } }
end
Rehearsal --------------------------------------------------
select 1.640000 0.010000 1.650000 ( 1.651426)
hash transpose 1.720000 0.010000 1.730000 ( 1.729950)
slice 1.740000 0.010000 1.750000 ( 1.748204)
----------------------------------------- total: 5.130000sec
user system total real
select 1.670000 0.010000 1.680000 ( 1.683415)
hash transpose 1.680000 0.010000 1.690000 ( 1.688110)
slice 1.800000 0.010000 1.810000 ( 1.816215)
The refinement will look like this:
module CoreExtensions
module Extractable
refine Hash do
def extract(*keys)
select { |k, _v| keys.include?(k) }
end
end
end
end
And to use it:
using ::CoreExtensions::Extractable
{ a: 1, b: 2, c: 3 }.extract(:a, :b)
Both delete_if
and keep_if
are part of Ruby core. Here you can achieve what you would like to without patching the Hash
type.
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2 = h1.clone
p h1.keep_if { |key| [:b, :d, :e, :f].include?(key) } # => {:b => :B, :d => :D}
p h2.delete_if { |key, value| [:b, :d, :e, :f].include?(key) } #=> {:a => :A, :c => :C}
For futher info, check the links below from the documentation:
This code injects the functionality you're asking for into the Hash class:
class Hash
def extract_subhash! *keys
to_keep = self.keys.to_a - keys
to_delete = Hash[self.select{|k,v| !to_keep.include? k}]
self.delete_if {|k,v| !to_keep.include? k}
to_delete
end
end
and produces the results you provided:
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
p h1.extract_subhash!(:b, :d, :e, :f) # => {b => :B, :d => :D}
p h1 #=> {:a => :A, :c => :C}
Note: this method actually returns the extracted keys/values.
Here's a functional solution that can be useful if you're not running on Ruby 2.5 and in the case that you don't wan't to pollute your Hash class by adding a new method:
slice_hash = -> keys, hash { hash.select { |k, _v| keys.include?(k) } }.curry
Then you can apply it even on nested hashes:
my_hash = [{name: "Joe", age: 34}, {name: "Amy", age: 55}]
my_hash.map(&slice_hash.([:name]))
# => [{:name=>"Joe"}, {:name=>"Amy"}]
As others have mentioned, Ruby 2.5 added the Hash#slice method.
Rails 5.2.0beta1 also added it's own version of Hash#slice to shim the functionality for users of the framework that are using an earlier version of Ruby. https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8
If looking to implement your own for whatever reason, it's a nice one liner as well:
def slice(*keys)
keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
end unless method_defined?(:slice)
Just an addition to slice method, if the subhash keys which you want to separate from original hash is going to be dynamic you can do like,
slice(*dynamic_keys) # dynamic_keys should be an array type
We can do it by looping on keys only we want to extract and just checking the key is exist and then extract it.
class Hash
def extract(*keys)
extracted_hash = {}
keys.each{|key| extracted_hash[key] = self.delete(key) if self.has_key?(key)}
extracted_hash
end
end
h1 = {:a => :A, :b => :B, :c => :C, :d => :D}
h2 = h1.extract(:b, :d, :e, :f)
참고URL : https://stackoverflow.com/questions/9025277/how-do-i-extract-a-sub-hash-from-a-hash
'program tip' 카테고리의 다른 글
PHP에서 날짜 시간을 ISO 8601로 변환하는 방법 (0) | 2020.09.13 |
---|---|
디렉토리의 폴더 목록 가져 오기 (0) | 2020.09.13 |
Android Json 및 null 값 (0) | 2020.09.13 |
PHP is not recognized as an internal or external command in command prompt (0) | 2020.09.13 |
Java 컬렉션을 Scala 컬렉션으로 변환 (0) | 2020.09.13 |