データをプログラム中の変数に取り込んだだけでは 情報を利用できない。利用者に提供するためには, 必要なものを絞り込んで提示する機能が必要である。
先述の例の履修科目をCSVファイルから読み取ったものから、 利用者が必要とするものを選んで出力するものを作成する。
ハッシュあるいは配列に複数の要素が入っているときに, その中からある一定の条件を満たしたもののみに選別するには select メソッドを利用する。CSV ファイルから読み取った値にもこれが適用できる。 ハッシュに適用する select メソッドは 配列の持つ select メソッドとほぼ同様の記法だが,ブロックに対する引数を2つにし, 各々にキーと値を受け取る形式にする。
csv-read.rb
で読み取った値のうち,「開講時期」が "春学期"
であるものを選択するには以下のようにする。
# csv-read.rbの続きから
db = csv # いったん別の変数にコピー
db = db.select {|row| row["開講時期"] == "春学期"}
selectメソッドによる絞り込みでは,文字列比較だけでなく 正規表現マッチや数値比較も使える。 これらの絞り込みを繰り返すことで複合条件の指定もできる。 以下の例は,開講時期と担当教員での絞り込みを正規表現マッチで行ない, 該当するもののみを出力するプログラムである。
#!/usr/bin/env ruby # coding: utf-8 Encoding.default_external = 'utf-8' # 入出力コードをutf-8に require 'csv' # CSVライブラリ csvfile = "syllabus.csv" # シラバス定義CSVファイル csv = CSV.read(csvfile, :headers => true) db = csv # いったん別の変数にコピー STDERR.print("開講時期は(指定なしの場合は空で)?: ") term = gets.chomp STDERR.print("担当教員は(指定なしの場合は空で)?: ") whom = gets.chomp # selectメソッドで絞り込み: # selectは1つずつ要素を取り出し、ブロック内の式が真(nilでもfalseでもない) # を帰す要素(CSVの1行に相当)のみで構成される集合を返す。 if term > "" # 開講時期指定に何か文字列を入れたなら ptn = Regexp.new(term) # 文字列を正規表現に変換 db = db.select {|row| ptn =~ row["開講時期"]} # 選択して再代入 end if whom > "" # 担当教員指定に何か文字列を入れたなら ptn = Regexp.new(whom) # 文字列を正規表現に変換 db = db.select {|row| ptn =~ row["担当教員"]} end puts("【該当科目一覧】") db.each {|row| # eachメソッドで1行ずつrowに取り出す printf("%s %sの情報 %s\n", "="*20, row["科目名"], "="*20) row.each {|key, value| printf("%s: %s\n", key, value) } }
CSV.read によるCSVファイル一括読込の標準動作では、 各項目を文字列として代入する。したがって、CSV データの項目中に数値と見なすべきデータがあった場合は、 数値に変換する処理が必要となる。これには以下の2つの方法が考えられる。
表1の履修科目表を CSV.read で読み取ったものから、「科目コード」が100以上200未満のものを選別してみる。 このとき「科目コード」はいずれも必ず整数値が入るものと仮定できるとする。
上記 csv-search.rb の科目検索部分の select による選択を以下のようにすればよい。
db = db.select {|row| # dbの集合から1行ずつ row 変数に取り出して選別 row["科目コード"].to_i >= 100 && row["科目コード"].to_i < 200 # 科目コード(数値) >= 100 かつ 科目コード(数値) < 200 }
以下のように CSV.read に与えるオプションで
:converters => :numeric
を与えると数値と解釈できる値は数値化される。
x = CSV.read(csvfile, :headers => true, :converters => :numeric)
これにより、上記科目コード選択は以下のように書けることになる。
db = db.select {|row| row["科目コード"] >= 100 && row["科目コード"] < 200 }
もちろん、元のCSVデータに数値化できない値があった場合は数値化されず、 数値に対してのみ行なえる演算を施すとエラーになるので注意が必要である。 数値でない項目がある可能性を考える場合は、:converters オプションを用いず、その都度 to_i や to_f を使うのが安全といえる。