データをプログラム中の変数に取り込んだだけでは 情報を利用できない。利用者に提供するためには, 必要なものを絞り込んで提示する機能が必要である。
先述の例の履修科目をCSVファイルから読み取ったものから、 利用者が必要とするものを選んで出力するものを作成する。
ハッシュあるいは配列に複数の要素が入っているときに, その中からある一定の条件を満たしたもののみに選別するには select メソッドを利用する。CSV ファイルから読み取った値にもこれが適用できる。 ハッシュに適用する select メソッドは 配列の持つ select メソッドとほぼ同様の記法だが,ブロックに対する引数を2つにし, 各々にキーと値を受け取る形式にする。
csv-read.rb
で読み取った値は以下のようになっている。
3行分のデータがそれぞれ、ハッシュ形式に変換されていることに注意する。
./csv-read.rb {"科目名"=>"基礎プログラミング", "科目コード"=>"154", "担当教員"=>"鳥海三輔", "開講時期"=>"春学期", "概要"=>"プログラミングを通じて問題解決能力を向上を図る。\n" + "本講ではRubyを用い,データ処理の構造と考え方の基礎を学ぶ"} {"科目名"=>"応用プログラミング", "科目コード"=>"254", "担当教員"=>"飯森大和", "開講時期"=>"秋学期", "概要"=> "問題解決を通じてプログラミング能力向上を図る。\n" + "本講ではプログラミング言語Cを通じて,問題解決に最適なアルゴリズムを適用する方策を学ぶ。"} {"科目名"=>"応用もっけ論", "科目コード"=>"39", "担当教員"=>"酒田育造", "開講時期"=>"春学期", "概要"=> "山形県庄内地方でよく使われる「もっけ」には様々な意味があり,他の地域にもこのような多様性を持つ言葉がある。\n" + "その成立ちをふまえて正しくその地域の「もっけ」を理解し,深い交流のきっかけとする。"}
この3つのレコードのうち,「開講時期」が "春学期"
であるものを選択するには以下のようにする。
# csv-read.rbの続きから
db = csv # いったん別の変数にコピー
db = db.select {|row| row["開講時期"] == "春学期"}
ブロック変数 row
は、3度代入され繰り返される。
row["開講時期"]
の値は回ごとに、
"春学期"
,
"秋学期"
,
"春学期"
が得られるため、== "春学期"
で比較して true
が返るのは1行目と3行目である。
なお、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 を使うのが安全といえる。