🔥

【Ruby学習記録】連想配列の基本的な扱い方

2022/09/20に公開

お題1:次の内容をハッシュで定義して1つの変数に代入し、その中のキーが age の要素を取り出して出力せよ

name が トム
age が 22
hobby が baseball

answer.rb
person = {
    name: "トム",
    age: 22,
    hobby: "baseball",
}
puts person[:age]
===
$ ruby answer.rb
22

お題2:下記のハッシュを格納した変数 person に次の内容を実行せよ

キーが blood で値が B という要素を追加
キーが weight の値を 78 に変更
キーが country 要素を削除

example.rb
person = {name: "ken", country: "japan", height: 191, weight: 80}
answer.rb
person = {
    name: "ken",
    country: "japan",
    height: 191,
    weight: 80
}

person[:blood] = "B"
person[:weight] = 78
person.delete(:country)

puts person
===
$ ruby answer.rb
{:name=>"ken", :height=>191, :weight=>78, :blood=>"B"}

お題1、2はいずれもハッシュの基本的な扱い方のおさらい。代入の仕方や参照・更新の仕方。

Ruby で連想配列(ハッシュ)を定義したい場合、2 パターンの表記の仕方がある模様。
1 つは PHP あたりと同じようにキー名を「文字列リテラル」で記述しアロー演算子で値を紐づける方法。

# 定義
hash = { 'キー文字列' =>, 'キー文字列' =>... }
# 参照
hash['キー文字列']
# 更新
hash['キー文字列'] =

もう一つは「シンボル」表記を使う方法。

# 定義
hash = { キー文字列:, キー文字列:... }
# 参照
hash[:キー文字列]
# 更新
hash[:キー文字列] =

注意が必要なのは、アロー演算子を使った場合とシンボルを使った場合では、参照の仕方も変わること。
Ruby の世界では、見た目上のキーの名称が同じように見えても、「文字列リテラル」と「シンボル」はまったく違うものとして扱われるので、ハッシュのキーを文字列リテラルで定義した場合は、その後も文字列リテラルでしかアクセスできないし、シンボル表記で定義した場合はシンボル表記でしかアクセスできない。
なので、person['name']person[:name]では、まったく違うものを指していることになる。
よくよく調べると、文字リテラルは String クラスのインスタンスで、シンボルは Symbol クラスのインスタンスということなので、まったくの別クラスという風に言われると、確かに'name':nameはまったく別物になるのは理解できる。

お題3:下記のハッシュが代入された変数 hash から、値の USA を取り出して出力せよ

example.rb
hash = {
  sports: {
    soccer: {
      origin: "England"
    },
    volleyball: {
      origin: "USA"
    }
  }
}
answer.rb
hash = {
    sports: {
        soccer: {
            origin: "England"
        },
        volleyball: {
            origin: "USA"
        }
    }
}
puts hash[:sports][:volleyball][:origin]
===
$ ruby answer.rb
USA

こちらは、Ruby のハッシュは階層を持てるよ、入れ子にできるよ、というお話。

# 参照
hash[:親キー名][:子キー名][:孫キー名]

といった具合に、[キー名]を左から順に上位 → 下位とつなげて書いていく。

お題4:次のハッシュを代入した変数 user の各要素の値を全て小文字に変換した内容を別の変数に代入せよ

example.rb
user = {first_name: "KaTo", last_name: "KeN"}
answer.rb
user = {
    first_name: "KaTo",
    last_name: "KeN"
}
result =
    user.map do |key, value|
        # result[key] = value.downcase
        [key, value.downcase]
    end.to_h

p result
===
$ ruby answer.rb
{:first_name=>"kato", :last_name=>"ken"}

これは、ハッシュで使える繰り返し系メソッドを使って対応する問題。
で、map メソッドは「繰り返し処理を行った結果を配列として返す」というものになる。
なので、ハッシュ専用のメソッドではないし、結果がハッシュで返ってくるわけでもない。

ただ、ハッシュに対して map メソッドを使った場合、キーと値それぞれをループ内変数(今回はkey, value)で受け取れるので、それらを使って処理をしている。
で、どう処理しているのかというと、キーはそのまま、値に対してdowncaseメソッドを使って小文字に変換し、[キー, 値]の順で配列に格納している。

この処理を元のハッシュの「キー/値」のペア数分だけ繰り返して、さらに配列に格納したものが返ってくるのが、user.map do ~ endの部分となる。
*この時点では[[:first_name, "kato"], [:last_name, "ken"]]という、配列の入れ子の形になっている。

今回のお題は結果を「ハッシュ」で欲しいので、Array.to_hのメソッドで、配列をハッシュに変換してresultに代入している。

このお題の場合、user.map do ~ endという、一見制御構文っぽい箇所があって、それにto_hメソッドがつながっているので一瞬おかしく思える。(特に C とかから覚えた人間には)

だが、これは Ruby の場合map do ~ endで map メソッドの一連の式という扱いになるので、map do ~ end.to_hという書き方で map メソッドの戻り値に対して to_h メソッドがさらに使える、ということになる。

もちろん、このような表記の仕方が気持ち悪ければ、一旦配列の状態でresultに格納しておき、result.to_hとしても大丈夫。

GitHubで編集を提案

Discussion