루비 스타일 : 중첩 된 해시 요소가 있는지 확인하는 방법
해시에 저장된 "사람"을 고려하십시오. 두 가지 예는 다음과 같습니다.
fred = {:person => {:name => "Fred", :spouse => "Wilma", :children => {:child => {:name => "Pebbles"}}}}
slate = {:person => {:name => "Mr. Slate", :spouse => "Mrs. Slate"}}
"person"에 자식이 없으면 "children"요소가 없습니다. 따라서 Mr. Slate의 경우 부모가 있는지 확인할 수 있습니다.
slate_has_children = !slate[:person][:children].nil?
그렇다면 "슬레이트"가 "사람"해시라는 사실을 모른다면 어떨까요? 중히 여기다:
dino = {:pet => {:name => "Dino"}}
더 이상 어린이를 쉽게 확인할 수 없습니다.
dino_has_children = !dino[:person][:children].nil?
NoMethodError: undefined method `[]' for nil:NilClass
그렇다면 특히 해시가 깊이 중첩 된 경우 (여기에 제공된 예제보다 더 깊이) 해시의 구조를 어떻게 확인할 수 있습니까? 더 나은 질문은 다음과 같습니다.이 작업을 수행하는 "루비 방식"은 무엇입니까?
이를 수행하는 가장 확실한 방법은 단순히 각 단계를 확인하는 것입니다.
has_children = slate[:person] && slate[:person][:children]
.nil 사용? 자리 표시 자 값으로 false를 사용할 때만 실제로 필요하며 실제로는 드뭅니다. 일반적으로 단순히 존재하는지 테스트 할 수 있습니다.
업데이트 : Ruby 2.3 이상을 사용하는
dig
경우이 답변에 설명 된 작업을 수행 하는 기본 제공 메서드가 있습니다.
그렇지 않은 경우이를 상당히 단순화 할 수있는 자체 Hash "dig"메서드를 정의 할 수도 있습니다.
class Hash
def dig(*path)
path.inject(self) do |location, key|
location.respond_to?(:keys) ? location[key] : nil
end
end
end
이 메서드는 각 단계를 확인하고 nil 호출에 대한 트립을 방지합니다. 얕은 구조의 경우 유틸리티는 다소 제한적이지만 깊이 중첩 된 구조의 경우 매우 유용합니다.
has_children = slate.dig(:person, :children)
예를 들어 : children 항목이 실제로 채워 졌는지 테스트하는 등 더 강력하게 만들 수도 있습니다.
children = slate.dig(:person, :children)
has_children = children && !children.empty?
Ruby 2.3에서는 안전한 탐색 연산자를 지원합니다 : https://www.ruby-lang.org/en/news/2015/11/11/ruby-2-3-0-preview1-released/
has_children
이제 다음과 같이 작성할 수 있습니다.
has_children = slate[:person]&.[](:children)
dig
다음과 같이 추가됩니다.
has_children = slate.dig(:person, :children)
또 다른 대안 :
dino.fetch(:person, {})[:children]
andand
gem을 사용할 수 있습니다 .
require 'andand'
fred[:person].andand[:children].nil? #=> false
dino[:person].andand[:children].nil? #=> true
http://andand.rubyforge.org/ 에서 자세한 설명을 찾을 수 있습니다 .
기본값 {}-빈 해시로 해시를 사용할 수 있습니다. 예를 들면
dino = Hash.new({})
dino[:pet] = {:name => "Dino"}
dino_has_children = !dino[:person][:children].nil? #=> false
이미 생성 된 Hash에서도 작동합니다.
dino = {:pet=>{:name=>"Dino"}}
dino.default = {}
dino_has_children = !dino[:person][:children].nil? #=> false
또는 nil 클래스에 대해 [] 메소드를 정의 할 수 있습니다.
class NilClass
def [](* args)
nil
end
end
nil[:a] #=> nil
전통적으로 다음과 같은 작업을 수행해야했습니다.
structure[:a] && structure[:a][:b]
그러나 Ruby 2.3에는 이러한 방식을보다 우아하게 만드는 기능이 추가되었습니다.
structure.dig :a, :b # nil if it misses anywhere along the way
ruby_dig
당신을 위해 이것을 백 패치 할 보석 이 있습니다.
def flatten_hash(hash)
hash.each_with_object({}) do |(k, v), h|
if v.is_a? Hash
flatten_hash(v).map do |h_k, h_v|
h["#{k}_#{h_k}"] = h_v
end
else
h[k] = v
end
end
end
irb(main):012:0> fred = {:person => {:name => "Fred", :spouse => "Wilma", :children => {:child => {:name => "Pebbles"}}}}
=> {:person=>{:name=>"Fred", :spouse=>"Wilma", :children=>{:child=>{:name=>"Pebbles"}}}}
irb(main):013:0> slate = {:person => {:name => "Mr. Slate", :spouse => "Mrs. Slate"}}
=> {:person=>{:name=>"Mr. Slate", :spouse=>"Mrs. Slate"}}
irb(main):014:0> flatten_hash(fred).keys.any? { |k| k.include?("children") }
=> true
irb(main):015:0> flatten_hash(slate).keys.any? { |k| k.include?("children") }
=> false
이것은 모든 해시를 하나로 합친 다음 어떤 것입니까? 하위 문자열 "children"과 일치하는 키가 있으면 true를 반환합니다. 이것은 또한 도움이 될 수 있습니다.
dino_has_children = !dino.fetch(person, {})[:children].nil?
레일에서 다음을 수행 할 수도 있습니다.
dino_has_children = !dino[person].try(:[], :children).nil? #
다음은 Ruby Hash 클래스를 몽키 패치하지 않고 해시 및 중첩 된 해시의 잘못된 값을 심층 검사 할 수있는 방법입니다 (Ruby 클래스에 몽키 패치를 적용하지 마십시오. .
(Rails를 가정하면 Rails 외부에서 작동하도록 쉽게 수정할 수 있습니다.)
def deep_all_present?(hash)
fail ArgumentError, 'deep_all_present? only accepts Hashes' unless hash.is_a? Hash
hash.each do |key, value|
return false if key.blank? || value.blank?
return deep_all_present?(value) if value.is_a? Hash
end
true
end
여기에서 위의 답변을 단순화하십시오.
다음과 같이 값이 nil 일 수없는 Recursive Hash 메서드를 만듭니다.
def recursive_hash
Hash.new {|key, value| key[value] = recursive_hash}
end
> slate = recursive_hash
> slate[:person][:name] = "Mr. Slate"
> slate[:person][:spouse] = "Mrs. Slate"
> slate
=> {:person=>{:name=>"Mr. Slate", :spouse=>"Mrs. Slate"}}
slate[:person][:state][:city]
=> {}
키에 대한 값이 존재하지 않으면 빈 해시를 만들어도 괜찮다면 :)
당신은 함께 놀 수 있습니다
dino.default = {}
또는 예 :
empty_hash = {}
empty_hash.default = empty_hash
dino.default = empty_hash
그렇게하면
empty_hash[:a][:b][:c][:d][:e] # and so on...
dino[:person][:children] # at worst it returns {}
주어진
x = {:a => {:b => 'c'}}
y = {}
다음 과 같이 x 와 y를 확인할 수 있습니다 .
(x[:a] || {})[:b] # 'c'
(y[:a] || {})[:b] # nil
대답을 위해 @tadman을 Thks.
perfs를 원하고 (그리고 ruby <2.3으로 고착 된)이 방법은 2.5 배 더 빠릅니다 :
unless Hash.method_defined? :dig
class Hash
def dig(*path)
val, index, len = self, 0, path.length
index += 1 while(index < len && val = val[path[index]])
val
end
end
end
and if you use RubyInline, this method is 16x faster:
unless Hash.method_defined? :dig
require 'inline'
class Hash
inline do |builder|
builder.c_raw '
VALUE dig(int argc, VALUE *argv, VALUE self) {
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
self = rb_hash_aref(self, *argv);
if (NIL_P(self) || !--argc) return self;
++argv;
return dig(argc, argv, self);
}'
end
end
end
You can also define a module to alias the brackets methods and use the Ruby syntax to read/write nested elements.
UPDATE: Instead of overriding the bracket accessors, request Hash instance to extend the module.
module Nesty
def []=(*keys,value)
key = keys.pop
if keys.empty?
super(key, value)
else
if self[*keys].is_a? Hash
self[*keys][key] = value
else
self[*keys] = { key => value}
end
end
end
def [](*keys)
self.dig(*keys)
end
end
class Hash
def nesty
self.extend Nesty
self
end
end
Then you can do:
irb> a = {}.nesty
=> {}
irb> a[:a, :b, :c] = "value"
=> "value"
irb> a
=> {:a=>{:b=>{:c=>"value"}}}
irb> a[:a,:b,:c]
=> "value"
irb> a[:a,:b]
=> {:c=>"value"}
irb> a[:a,:d] = "another value"
=> "another value"
irb> a
=> {:a=>{:b=>{:c=>"value"}, :d=>"another value"}}
I don't know how "Ruby" it is(!), but the KeyDial gem which I wrote lets you do this basically without changing your original syntax:
has_kids = !dino[:person][:children].nil?
becomes:
has_kids = !dino.dial[:person][:children].call.nil?
This uses some trickery to intermediate the key access calls. At call
, it will try to dig
the previous keys on dino
, and if it hits an error (as it will), returns nil. nil?
then of course returns true.
You can use a combination of &
and key?
it is O(1) compared to dig
which is O(n) and this will make sure person is accessed without NoMethodError: undefined method `[]' for nil:NilClass
fred[:person]&.key?(:children) //=>true
slate[:person]&.key?(:children)
ReferenceURL : https://stackoverflow.com/questions/1820451/ruby-style-how-to-check-whether-a-nested-hash-element-exists
'code' 카테고리의 다른 글
위임, 구성 및 집계 구분 (Java OO 디자인) (0) | 2020.12.29 |
---|---|
문자열에 마침표 (0) | 2020.12.29 |
cglib에 대한 대안이 있습니까? (0) | 2020.12.29 |
무효 유효한 코드를 반환합니까? (0) | 2020.12.29 |
순전히 기능적인 데이터 구조의 이점은 무엇입니까? (0) | 2020.12.29 |