Hash(ハッシュ・辞書)

  • key, value が一対になったセット
  • ハッシュのクラス名 : Hash
  • ハッシュ、ハッシュオブジェクト、Hash オブジェクトなどと呼ぶ
h = {:key1 => "value", :key2 => 1}

p h[:key1] # "value"
p h[:key2] # 1
  • キー → シンボル
{ "キー" => "値", "キー" => "値" }
↓
{:key1 => "value", :key2 => 1}
↓
{ "シンボルオブジェクト" => "文字列オブジェクト", "シンボルオブジェクト" => "整数オブジェクト" }

シンボル symbol

  • シンボルは :(コロン)から書き始める
    • ダブルクォーテーションで囲まないので注意
  • シンボルも文字列や整数と同じようにオブジェクトの一種
  • シンボルのクラス名 : Symbol
  • => : 親しみを込めて ハッシュロケット と呼ばれる
  • シンボルはハッシュのキーで、ラベルのような使い方をする
    • ハッシュのキー以外でもシンボルは使われる
  • シンボル / 文字列は相互に変換できる
p "coffee".to_sym
# :coffee

p :coffee.to_s
# "coffee"

ハッシュの 2 種類の書き方

  • パターン B の方が読みやすいが、以下の条件がある
    • ハッシュのキーにシンボルを指定した時のみ」使える
  • 「ハッシュのキーに文字列」など、シンボル以外のオブジェクトを使う場合は、パターン A を使う必要がある
# パターン A
{:coffee => 300, :latte => 400}

# パターン B
{coffee: 300, latte: 400}
# 「ハッシュのキーが文字列」のためハッシュロケットで書く
{"coffee" => 300, "latte" => 400}

ハッシュの要素へのアクセス

  • シンボルでのアクセス
hash[:symbol]
menu = {coffee: 300, latte: 400}

p menu[:latte]
# 400
  • キー名でのアクセス
hash["key_name"]
menu = {"coffee" => 300, "latte" => 400}

p menu["latte"]
# 400

要素の追加 / 更新 / 削除

追加

hash[:new_symbol]
  • 新しい要素 menu[:mocha]500 を代入するイメージ
  • 新しい要素は末尾に追加される
    • ただし、ハッシュは「キーを指定して値を取得する」ことが多いため、配列のように並び順を気にすることはあまりない
menu = {coffee: 300, latte: 400}

menu[:mocha] = 500

p menu[:mocha]
# 500

p menu
# {:coffee=>300, :latte=>400, :mocha=>500}

更新

  • ハッシュは同じキーを持たない
    • 既にあるキーを指定して登録すると、既存のものが上書きされる
menu = {coffee: 300, latte: 400}

menu[:mocha] = 500
p menu
# {:coffee=>300, :latte=>400, :mocha=>500}

menu[:mocha] = 650
p menu
# {:coffee=>300, :latte=>400, :mocha=>650}

値に追加して更新

menu = {coffee: 300, latte: 400}

menu[:coffee] += 100
p menu
# {:coffee=>400, :latte=>400}
menu = {coffee: "300", latte: 400}

menu[:coffee] += "100"
p menu
# {:coffee=>"300100", :latte=>400}

削除 delete

hash.delete(:symbol)
menu = {coffee: 300, latte: 400, mocha: 650}

menu.delete(:latte)
p menu
# {:coffee=>300, :mocha=>650}

ハッシュの結合 merge

menu1 = {coffee: 300, latte: 400}
menu2 = {apple: 200, banana: 300}

menu = menu1.merge(menu2)

p menu
# {:coffee=>300, :latte=>400, :apple=>200, :banana=>300}

存在しないキーを指定すると nil が返る

menu = {coffee: 300, latte: 400}

p menu[:hoge]
# nil

ハッシュのループ

  • 配列と同じように each メソッドでループを回せる
    • 変数が |key, value| と指定できる
menu = {coffee: 300, latte: 400}

menu.each { |k, v| p "#{k} is #{v}" }
# "coffee is 300"
# "latte is 400"

キーだけのループ each_key

menu = {coffee: 300, latte: 400}
menu.each_key { |k| p k }
# :coffee
# :latte
menu = {coffee: 300, latte: 400}
menu.each_key { |k| p k.to_s }
# "coffee"
# "latte"
menu = {coffee: 300, latte: 400}

array = []
menu.each_key {|k| array << k.to_s}
p array
# ["coffee", "latte"]

値だけのループ each_value

menu = {coffee: 300, latte: 400}

menu.each_value { |v| p v }
# 300
# 400

キー / 値を配列で取得

全キーを配列として取得 keys

menu = {"coffee" => 300, "latte" => 400}

p menu.keys
# ["coffee", "latte"]

値の全値を配列として取得 values

menu = {"coffee" => 300, "latte" => 400}

p menu.values
# [300, 400]

ハッシュを使った集計

banana に登場するアルファベットそれぞれの合計数を集計する

  1. banana を 1 文字ずつ分解して配列にする
  2. ハッシュの キー にアルファベット 1 文字、 に登場回数をセットしていく
text = "banana"

array = text.chars
p array
# ["b", "a", "n", "a", "n", "a"]

hash = {}

array.each { |t| hash[t] += 1 }
# undefined method `+' for nil:NilClass (NoMethodError)
  • 空のハッシュにキーを指定して、数値を追加(加算 +=)するとエラーになる
    • 存在しないキーを指定すると nil が返る
      • nil + 1 → エラー

default

hash = {}
p hash.default # nil

hash.default = 0
p hash.default # 0
  • 先ほどのエラーで止まったスクリプトに default メソッドを加える
text = "banana"

array = text.chars
p array
# ["b", "a", "n", "a", "n", "a"]

hash = {}
hash.default = 0

array.each { |t| hash[t] += 1 }
p hash
# {"b"=>1, "a"=>3, "n"=>2}

hash.each {|k, v| puts "#{k}は#{v}回"}
# bは1回
# aは3回
# nは2回

tally

  • Ruby 2.7 から追加された tally を使うと簡単に 配列 → ハッシュ として集計できる

Enumerable#tally (Ruby 3.1 リファレンスマニュアル)

text = "banana"
array = text.chars # ["b", "a", "n", "a", "n", "a"]

hash = array.tally
p hash
# {"b"=>1, "a"=>3, "n"=>2}