Ruby Language
해시
수색…
소개
해시 (Hash)는 고유 한 키와 값의 사전 형 모음입니다. 연관 배열이라고도하며 배열과 비슷하지만 배열에서 정수를 인덱스로 사용하는 경우 해시를 사용하면 모든 객체 유형을 사용할 수 있습니다. 해시에서 키를 참조하여 새 항목을 검색하거나 만듭니다.
통사론
{first_name : "Noel", second_name : "Edmonds"}
{: first_name => "Noel", : second_name => "Edmonds"}
{ "First Name"=> "Noel", "Second Name"=> "Edmonds"}
{first_key => first_value, second_key => second_value}
비고
루비의 해시는 해시 테이블을 사용하여 값으로 매핑합니다.
임의의 해시 가능 객체가 키로서 사용될 수있다. 그러나 객체 할당이 줄어들어 여러 Ruby 버전에서 일반적으로 더 효율적이므로 Symbol
을 사용하는 것이 일반적입니다.
{ key1: "foo", key2: "baz" }
해시 만들기
Ruby의 해시는 키를 값에 매핑하는 해시 테이블 을 구현하는 객체입니다. Ruby는 {}
사용하여 해시를 정의하는 특정 리터럴 구문을 지원합니다.
my_hash = {} # an empty hash
grades = { 'Mark' => 15, 'Jimmy' => 10, 'Jack' => 10 }
해시는 표준 new
메서드를 사용하여 만들 수도 있습니다.
my_hash = Hash.new # any empty hash
my_hash = {} # any empty hash
해시는 배열, 객체 및 기타 해시와 같은 복합 유형을 포함하여 모든 유형의 값을 가질 수 있습니다.
mapping = { 'Mark' => 15, 'Jimmy' => [3,4], 'Nika' => {'a' => 3, 'b' => 5} }
mapping['Mark'] # => 15
mapping['Jimmy'] # => [3, 4]
mapping['Nika'] # => {"a"=>3, "b"=>5}
또한 키는 복잡한 유형을 포함하여 모든 유형이 될 수 있습니다.
mapping = { 'Mark' => 15, 5 => 10, [1, 2] => 9 }
mapping['Mark'] # => 15
mapping[[1, 2]] # => 9
심볼 은 일반적으로 해시 키로 사용되며 Ruby 1.9에서는이 프로세스를 단축하는 새로운 구문을 도입했습니다. 다음 해시는 동일합니다.
# Valid on all Ruby versions
grades = { :Mark => 15, :Jimmy => 10, :Jack => 10 }
# Valid in Ruby version 1.9+
grades = { Mark: 15, Jimmy: 10, Jack: 10 }
모든 키가 문자열이기 때문에 다음 해시 (모든 Ruby 버전에서 유효)가 다릅니다 .
grades = { "Mark" => 15, "Jimmy" => 10, "Jack" => 10 }
두 구문 버전이 섞여있을 수 있지만 다음은 권장되지 않습니다.
mapping = { :length => 45, width: 10 }
Ruby 2.2 이상 버전에는 기호 키가있는 해시를 생성하는 대체 구문이 있습니다 (기호에 공백이있는 경우 가장 유용합니다).
grades = { "Jimmy Choo": 10, :"Jack Sparrow": 10 }
# => { :"Jimmy Choo" => 10, :"Jack Sparrow" => 10}
값 액세스
해시의 개별 값은 []
및 []=
메소드를 사용하여 읽고 쓰게됩니다.
my_hash = { length: 4, width: 5 }
my_hash[:length] #=> => 4
my_hash[:height] = 9
my_hash #=> {:length => 4, :width => 5, :height => 9 }
기본적으로 해시에 추가되지 않은 키에 액세스하면 nil
반환되므로 키 값을 조회하는 것이 항상 안전합니다.
my_hash = {}
my_hash[:age] # => nil
해시에는 문자열에 키가 포함될 수도 있습니다. 그것들에 정상적으로 접근하려고하면 그냥 nil
리턴 할 것이고, 대신 문자열 키로 접근 할 것입니다 :
my_hash = { "name" => "user" }
my_hash[:name] # => nil
my_hash["name"] # => user
키가 예상되거나 존재해야하는 상황의 경우 해시에는 존재하지 않는 키에 액세스 할 때 예외를 발생시키는 fetch
메소드가 있습니다.
my_hash = {}
my_hash.fetch(:age) #=> KeyError: key not found: :age
fetch
는 키가 이전에 설정되지 않은 경우 반환되는 두 번째 인수로 기본값을 받아들입니다.
my_hash = {}
my_hash.fetch(:age, 45) #=> => 45
fetch
는 키가 이전에 설정되지 않은 경우 반환되는 블록을 받아 들일 수 있습니다.
my_hash = {}
my_hash.fetch(:age) { 21 } #=> 21
my_hash.fetch(:age) do |k|
puts "Could not find #{k}"
end
#=> Could not find age
해시는 []=
의 별칭으로 store
메소드를 지원합니다.
my_hash = {}
my_hash.store(:age, 45)
my_hash #=> { :age => 45 }
values
메소드를 사용하여 해시의 모든 값을 얻을 수도 있습니다.
my_hash = { length: 4, width: 5 }
my_hash.values #=> [4, 5]
참고 : 이것은 Ruby 2.3 #dig
에서만 사용 가능합니다. #dig
는 중첩 Hash
합니다. 각 단계에서 dig를 호출하여 중간 단계가 nil이면 nil을 반환하여 idx 객체 시퀀스에 의해 지정된 중첩 값을 추출합니다.
h = { foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) # => 1
h.dig(:foo, :zot, :xyz) # => nil
g = { foo: [10, 11, 12] }
g.dig(:foo, 1) # => 11
기본값 설정
기본적으로 존재하지 않는 키의 값을 검색하려고하면 nil
이 반환됩니다. 존재하지 않는 키를 사용하여 해시에 액세스 할 때 반환 할 다른 값 (또는 수행 할 작업)을 선택적으로 지정할 수 있습니다. 이것을 "디폴트 값"이라고 부르지 만 단일 값일 필요는 없습니다. 예를 들어 키의 길이와 같은 계산 된 값일 수 있습니다.
해시의 기본값은 생성자에 전달 될 수 있습니다.
h = Hash.new(0)
h[:hi] = 1
puts h[:hi] # => 1
puts h[:bye] # => 0 returns default value instead of nil
이미 생성 된 Hash에도 기본값을 지정할 수 있습니다 :
my_hash = { human: 2, animal: 1 }
my_hash.default = 0
my_hash[:plant] # => 0
새 키에 액세스 할 때마다 기본값이 복사되지 않으므로 기본값이 참조 유형 인 경우 놀라운 결과가 발생할 수 있습니다.
# Use an empty array as the default value
authors = Hash.new([])
# Append a book title
authors[:homer] << 'The Odyssey'
# All new keys map to a reference to the same array:
authors[:plato] # => ['The Odyssey']
이 문제를 피하기 위해 Hash 생성자는 새 키에 액세스 할 때마다 실행되는 블록을 허용하며 반환 값이 기본값으로 사용됩니다.
authors = Hash.new { [] }
# Note that we're using += instead of <<, see below
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"]}
위의 예에서는 해시에 기본값이 자동으로 할당되지 않으므로 << 대신 + =를 사용해야합니다. <<를 사용하면 배열에 추가되지만 작성자 [: homer]는 정의되지 않은 채로 남아 있습니다.
authors[:homer] << 'The Odyssey' # ['The Odyssey']
authors[:homer] # => []
authors # => {}
액세스시 기본값을 할당하고 더 복잡한 기본값을 계산할 수 있도록 기본 블록은 해시와 키를 모두 전달합니다.
authors = Hash.new { |hash, key| hash[key] = [] }
authors[:homer] << 'The Odyssey'
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"], :plato=>[]}
기본 블록을 사용하여 작업을 수행하거나 키 (또는 다른 데이터)에 종속 된 값을 반환 할 수도 있습니다.
chars = Hash.new { |hash,key| key.length }
chars[:test] # => 4
더 복잡한 해시를 만들 수도 있습니다.
page_views = Hash.new { |hash, key| hash[key] = { count: 0, url: key } }
page_views["http://example.com"][:count] += 1
page_views # => {"http://example.com"=>{:count=>1, :url=>"http://example.com"}}
이미 존재하는 해시에서 기본값을 Proc로 설정하려면 default_proc=
사용하십시오.
authors = {}
authors.default_proc = proc { [] }
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # {:homer=>["The Odyssey"]}
자동으로 딥 해시 만들기
해시는 요청되었지만 존재하지 않는 키 (nil)에 대한 기본값을가집니다.
a = {}
p a[ :b ] # => nil
새 해시를 만들 때 기본값을 지정할 수 있습니다.
b = Hash.new 'puppy'
p b[ :b ] # => 'puppy'
Hash.new 또한 블록을 사용하여 Perl의 자동 가상화 동작 또는 mkdir -p
와 같은 중첩 해시를 자동으로 만들 수 있습니다.
# h is the hash you're creating, and k the key.
#
hash = Hash.new { |h, k| h[k] = Hash.new &h.default_proc }
hash[ :a ][ :b ][ :c ] = 3
p hash # => { a: { b: { c: 3 } } }
키와 값 수정하기
키 또는 값을 수정하여 새 해시를 만들 수 있습니다. 실제로는 inject (AKA, reduce )를 사용하여 키를 추가하거나 삭제할 수도 있습니다. 예를 들어, 문자열 화 된 키와 대문자 값을 가진 해시를 생성하려면 다음과 같이하십시오.
fruit = { name: 'apple', color: 'green', shape: 'round' }
# => {:name=>"apple", :color=>"green", :shape=>"round"}
new_fruit = fruit.inject({}) { |memo, (k,v)| memo[k.to_s] = v.upcase; memo }
# => new_fruit is {"name"=>"APPLE", "color"=>"GREEN", "shape"=>"ROUND"}
해시는 열거 형이며 본질적으로 키 / 값 쌍의 모음입니다. 따라서 each
메소드, map
, inject
메소드가 있습니다.
해시의 모든 키 / 값 쌍에 대해 주어진 블록이 평가되고, 첫 번째 실행시 메모의 값은 inject
할 시드 값이고, 우리의 경우 빈 해시 {}
입니다. 후속 평가를위한 memo
의 값은 이전 블록 평가의 반환 값입니다. 따라서 값이있는 키를 설정하여 memo
를 수정 한 다음 끝에 memo
를 반환합니다. 최종 블록 평가의 반환 값은 우리의 경우 memo
에서 inject
의 반환 값입니다.
최종 값을 제공하지 않으려면 대신 each_with_object 를 사용할 수 있습니다.
new_fruit = fruit.each_with_object({}) { |(k,v), memo| memo[k.to_s] = v.upcase }
또는 지도 :
new_fruit = Hash[fruit.map{ |k,v| [k.to_s, v.upcase] }]
(해시를 제 위치에서 조작하는 방법을 포함하여 자세한 내용은 이 답변 을 참조하십시오.)
해시를 반복하다
Hash
는 Enumerable#each
, Enumerable#each_pair
, Enumerable#each_key
및 Enumerable#each_value
와 같은 여러 반복 방법을 제공하는 Enumerable
모듈을 포함합니다.
.each
및 .each_pair
는 각 키 - 값 쌍을 반복합니다.
h = { "first_name" => "John", "last_name" => "Doe" }
h.each do |key, value|
puts "#{key} = #{value}"
end
# => first_name = John
# last_name = Doe
.each_key
는 키를 통해서만 반복합니다 :
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_key do |key|
puts key
end
# => first_name
# last_name
.each_value
반복합니다.
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_value do |value|
puts value
end
# => John
# Doe
.each_with_index
는 요소를 반복하고 반복의 인덱스를 제공합니다.
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_with_index do |(key, value), index|
puts "index: #{index} | key: #{key} | value: #{value}"
end
# => index: 0 | key: first_name | value: John
# index: 1 | key: last_name | value: Doe
배열로의 변환과 배열로의 변환
해시는 배열간에 자유롭게 변환 될 수 있습니다. 키 / 값 쌍의 해시를 배열로 변환하면 쌍의 중첩 배열을 포함하는 배열이 생성됩니다.
{ :a => 1, :b => 2 }.to_a # => [[:a, 1], [:b, 2]]
반대 방향으로 동일한 형식의 배열에서 해시를 만들 수 있습니다.
[[:x, 3], [:y, 4]].to_h # => { :x => 3, :y => 4 }
마찬가지로 Hash[]
와 교대 키와 값 목록을 사용하여 Hash[]
초기화 할 수 있습니다.
Hash[:a, 1, :b, 2] # => { :a => 1, :b => 2 }
또는 각각 두 개의 값을 가진 배열의 배열로부터 :
Hash[ [[:x, 3], [:y, 4]] ] # => { :x => 3, :y => 4 }
해시는 flatten()
사용하여 키와 값이 번갈아 배열로 다시 변환 될 수 있습니다.
{ :a => 1, :b => 2 }.flatten # => [:a, 1, :b, 2]
배열로 쉽게 변환 할 수 있으므로 Hash
는 collect
및 zip
과 같은 많은 Enumerable
메서드에서 잘 작동합니다.
Hash[('a'..'z').collect{ |c| [c, c.upcase] }] # => { 'a' => 'A', 'b' => 'B', ... }
people = ['Alice', 'Bob', 'Eve']
height = [5.7, 6.0, 4.9]
Hash[people.zip(height)] # => { 'Alice' => 5.7, 'Bob' => '6.0', 'Eve' => 4.9 }
모든 키 또는 해시 값 가져 오기
{foo: 'bar', biz: 'baz'}.keys # => [:foo, :biz]
{foo: 'bar', biz: 'baz'}.values # => ["bar", "baz"]
{foo: 'bar', biz: 'baz'}.to_a # => [[:foo, "bar"], [:biz, "baz"]]
{foo: 'bar', biz: 'baz'}.each #<Enumerator: {:foo=>"bar", :biz=>"baz"}:each>
해시 함수 무시
루비 해시는 hash
와 eql?
메소드를 사용 eql?
해시 연산을 수행하고 해시에 저장된 객체를 내부 해시 저장소에 할당합니다. Ruby에서 hash
의 기본 구현은 hash
된 객체의 모든 멤버 필드에 대한 중얼 거림 해시 함수 입니다. 이 동작을 무시하려면 hash
와 eql?
을 재정의 할 수 eql?
행동 양식.
다른 해시 구현과 마찬가지로 두 객체 a와 b는 a.hash == b.hash
경우 동일한 버킷으로 해시되며 a.eql?(b)
경우 동일한 것으로 간주됩니다. 따라서 hash
와 eql?
다시 구현할 때 eql?
하나는 a
와 b
가 eql?
동일하면 보장하도록주의해야 eql?
동일한 hash
값을 반환해야합니다. 그렇지 않으면 해시에 항목이 중복 될 수 있습니다. 반대로, hash
구현에서 잘못된 선택은 많은 객체가 동일한 해시 버킷을 공유하도록 유도하여 효과적으로 O (1) 조회 시간을 파괴하고 eql?
을 호출하는 데 O (n)을 유발할 수 eql?
모든 개체에.
아래 예제에서 클래스 A
의 인스턴스 만 키로 저장됩니다. 먼저 추가되었습니다.
class A
def initialize(hash_value)
@hash_value = hash_value
end
def hash
@hash_value # Return the value given externally
end
def eql?(b)
self.hash == b.hash
end
end
class B < A
end
a = A.new(1)
b = B.new(1)
h = {}
h[a] = 1
h[b] = 2
raise "error" unless h.size == 1
raise "error" unless h.include? b
raise "error" unless h.include? a
해시 필터링
select
는 블록이 true
평가되는 키 - 값 쌍이있는 새 hash
를 반환 true
.
{ :a => 1, :b => 2, :c => 3 }.select { |k, v| k != :a && v.even? } # => { :b => 2 }
필터 블록에서 키 또는 값 을 필요로하지 않을 때는 해당 위치에서 _
을 사용하는 것이 관습입니다.
{ :a => 1, :b => 2, :c => 3 }.select { |_, v| v.even? } # => { :b => 2 }
{ :a => 1, :b => 2, :c => 3 }.select { |k, _| k == :c } # => { :c => 3 }
reject
는 블록이 false
평가 한 키 - 값 쌍이있는 새 hash
를 반환합니다.
{ :a => 1, :b => 2, :c => 3 }.reject { |_, v| v.even? } # => { :a => 1, :c => 3 }
{ :a => 1, :b => 2, :c => 3 }.reject { |k, _| k == :b } # => { :a => 1, :c => 3 }
해시 작업 설정
해시의 교차점
두 해시의 교집합을 얻으려면 값이 같은 공유 키를 반환하십시오.
hash1 = { :a => 1, :b => 2 } hash2 = { :b => 2, :c => 3 } hash1.select { |k, v| (hash2.include?(k) && hash2[k] == v) } # => { :b => 2 }
해시 연합 (병합) :
키가 병합 될 둘 해시 발생하면 해시의 키, 고유, 해시에서 하나의
merge
덮어 쓰기에 호출됩니다hash1 = { :a => 1, :b => 2 } hash2 = { :b => 4, :c => 3 } hash1.merge(hash2) # => { :a => 1, :b => 4, :c => 3 } hash2.merge(hash1) # => { :b => 2, :c => 3, :a => 1 }