수색…


소개

해시 (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 }

또는 지도 :

1.8
new_fruit = Hash[fruit.map{ |k,v| [k.to_s, v.upcase] }]

(해시를 제 위치에서 조작하는 방법을 포함하여 자세한 내용은 이 답변 을 참조하십시오.)

해시를 반복하다

HashEnumerable#each , Enumerable#each_pair , Enumerable#each_keyEnumerable#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]

배열로 쉽게 변환 할 수 있으므로 Hashcollectzip 과 같은 많은 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>

해시 함수 무시

루비 해시는 hasheql? 메소드를 사용 eql? 해시 연산을 수행하고 해시에 저장된 객체를 내부 해시 저장소에 할당합니다. Ruby에서 hash 의 기본 구현은 hash객체의 모든 멤버 필드에 대한 중얼 거림 해시 함수 입니다. 이 동작을 무시하려면 hasheql? 을 재정의 할 수 eql? 행동 양식.

다른 해시 구현과 마찬가지로 두 객체 a와 b는 a.hash == b.hash 경우 동일한 버킷으로 해시되며 a.eql?(b) 경우 동일한 것으로 간주됩니다. 따라서 hasheql? 다시 구현할 때 eql? 하나는 abeql? 동일하면 보장하도록주의해야 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 }
    


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow