はじめに
Ruby標準のOpenStructクラスを使うことで手軽にデータ構造を扱うことができます。
この記事ではOpenStructを使ったサンプルを紹介したいと思います。
これらに興味がある人向けの記事です
- OpenStructを使ってみたい
- データ構造はハッシュよりクラスにしたい
- クラスを宣言的に用意するのは面倒
こんな人には不向きです
- ハッシュデータ構造をそのまま扱うのに何の苦も感じない
- クラスは宣言的に用意したい
目次
OpenStructとは
Ruby標準のライブラリです。リファレンスによると
要素を動的に追加・削除できる手軽な構造体を提供するクラスです。
OpenStruct のインスタンスに対して未定義なメソッド x= を呼ぶと、
OpenStruct クラスの BasicObject#method_missing で捕捉され、
そのインスタンスにインスタンスメソッド x, x= が定義されます。
この挙動によって要素を動的に変更できる構造体として働きます。
サンプルデータ構造
例えば、次のような映画のデータベースみたいなものがあったとします。
映画作品集
映画
タイトル
評価
監督
名前
生年月日
キャスト
俳優
名前
生年月日
俳優
:
:
映画
:
:
このデータ構造を次のようなハッシュと配列で表現したとします。
sample_data.rb
$sample_data = {
:movies => [
{
:title => "インターステラー",
:star => "8.6",
:director => {:name => "クリストファーノーラン", :born => "1970-07-30"},
:cast => [
{:name => "マシューマコノヒー", :born => "1969-11-04"},
{:name => "アンハサウェイ", :born => "1982-11-12"}
]
},
{
:title => "パルプフィクション",
:star => "8.9",
:director => {:name => "クエンティンタランティーノ", :born => "1963-03-27"},
:cast => [
{:name => "ジョントラボルタ", :born => "1954-02-18"},
{:name => "サミュエル L ジャクソン", :born => "1948-12-21"},
{:name => "ブルースウィリス", :born => "1955-03-19"}
]
},
{
:title => "君の名は",
:star => "8.3",
:director => {:name => "新海誠", :born => "1973-02-09"},
:cast => [
{:name => "神木隆之介", :born => "1993-05-19"},
{:name => "上白石萌音", :born => "1998-01-27"},
]
}
]
}
OpenStructを使うサンプル
OpenStructの初期化メソッドにハッシュを渡して、アクセッサーを使おうとするとハッシュキーに対応するアクセッサーが自動で定義されます。
ただし、ネストしたハッシュや配列の中身まで一発でアクセッサーを作ることができないようなので階層構造に沿ってOpenStructを生成しています。
sample1.rb
require_relative 'sample_data'
require 'ostruct'
#
# サンプルデータをOpenStructにマッピングする
#
root = OpenStruct.new
root.movies = []
$sample_data[:movies].each do | movie_hash |
movie = OpenStruct.new(:title => movie_hash[:title], :star => movie_hash[:star])
director_hash = movie_hash[:director]
director = OpenStruct.new(:name => director_hash[:name], :born => director_hash[:born])
movie.director = director
cast = movie_hash[:cast]
movie.cast = []
cast.each do | actor_hash |
actor = OpenStruct.new(:name => actor_hash[:name], :born => actor_hash[:born])
movie.cast.push( actor )
end
root.movies.push( movie )
end
#
# マッピングされたデータ構造を操作する
#
root.movies.each do | movie |
puts "movie: title: #{movie.title}, star: #{movie.star}"
puts "director: name: #{movie.director.name}, born: #{movie.director.born}"
movie.cast.each do |actor|
puts "actor: name: #{actor.name}, born: #{actor.born}"
end
puts
end
実行結果
movie: title: インターステラー, star: 8.6
director: name: クリストファーノーラン, born: 1970-07-30
actor: name: マシューマコノヒー, born: 1969-11-04
actor: name: アンハサウェイ, born: 1982-11-12
movie: title: パルプフィクション, star: 8.9
director: name: クエンティンタランティーノ, born: 1963-03-27
actor: name: ジョントラボルタ, born: 1954-02-18
actor: name: サミュエル L ジャクソン, born: 1948-12-21
actor: name: ブルースウィリス, born: 1955-03-19
movie: title: 君の名は, star: 8.3
director: name: 新海誠, born: 1973-02-09
actor: name: 神木隆之介, born: 1993-05-19
actor: name: 上白石萌音, born: 1998-01-27
最後に
ネストしたハッシュや配列を一発で変換できるやり方を知ってる方いらっしゃったら教えてください。
(JSON.parseは出来る)