ローイングファンのプログラミング日記

ボート競技やプログラミングについて書きます

Time差0.1秒未満のレースを選び出す

今回はデータベースに収録してあるボートレース(レガッタ)の記録から僅差のレースを選び出す。
データベースの操作にはActive Recordをつかう。

目次


条件

PostgreSQLをインストールして次のようなテーブルを用意しておく。

データベース名: result
テーブル名: results

year
(int)
tournament_name
(varchar(10))
raceno
(int)
event
(varchar(15))
group
(varchar(10))
rank1
(varchar(10))
time_1st
(varchar(10))
rank2
(varchar(10))
time_2nd
(varchar(10))
rank3
(varchar(10))
time_3rd
(varchar(10))
2000 全日本選手権 1 男子エイト 予選A組 Aクルー 05:50.12 Bクルー 05:55.21 Cクルー 06:00.33
・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・ ・・・
2018 全日本選手権 137 男子エイト 予選A組 Xクルー 05:51.12 Yクルー 05:56.21 Zクルー 06:01.33


プログラムの内容

2014年から2018年の5年間で1着と2着のタイム差が0.1秒未満のレースを抽出する。

  • データベースから5年分のレース結果を抽出する
  • 文字列のタイムをparseメソッドでTimeオブジェクトにする
  • タイム差が0.1秒未満という条件をつける
  • 条件が真のデータを出力する


必要なGemのインストール

RubyGemsをアップデートする。

gem update --system


Bundlerをアップデートする。

gem update bundler

※ Bundlerがインストールされていない場合はgem install bundlerでインストールする。

Gemfileを作成する。

bundle init


Gemfileに下記を追記する。

gem 'activerecord'
gem 'pg'
gem 'dotenv'


Gemをインストールする。

bundle install --path vendor/bundle


.envファイルをつくる

コードにそのまま表記したくない情報を変数に代入する。
.envファイルをつくり、つぎのように記入する。

myadapter='postgresql'
myname='Username' #各々のユーザーネーム
mydatabase='result'


Rubyのコードを書く

activerecord_lesson.rb

require 'active_record'
require 'dotenv/load'
require 'time'

class Result < ActiveRecord::Base
    establish_connection(
        adapter:  ENV['myadapter'],
        host:     "",
        username: ENV['myname'],
        password: "",
        database: ENV['mydatabase']
    )
    #年代を絞り込むメソッド
    def select_races
        race_results = Result.where(
            "year = ? or year = ? or year = ? or year = ? or year = ?",
            "2014","2015","2016","2017","2018"
        )
        return race_results
    end
    #タイム差が0.1秒未満のレースだけ出力するメソッド
    def display_narrow_margin_races(race_results)
        race_results.each do |race_result|
            #2着と1着のタイムを文字列からTimeオブジェクトに変換する
            parsed_time_2nd = Time.parse("00:#{race_result['time_2nd']}")
            parsed_time_1st = Time.parse("00:#{race_result['time_1st']}")
            if (parsed_time_2nd - parsed_time_1st) < 0.1
                puts "#{race_result['year']}#{race_result['tournament_name']}"
                puts "RaceNo:#{race_result['raceno']}"
                puts "#{race_result['event']} #{race_result['group']}"
                puts "1着: #{race_result['rank1']} Time: #{race_result['time_1st']}"
                puts "2着: #{race_result['rank2']} Time: #{race_result['time_2nd']}"
                puts "Time差: #{parsed_time_2nd - parsed_time_1st}"
                puts "-----------------"
            end
        end
    end
end

#モデルを作成
result = Result.new
#年代を絞り込む
race_results = result.select_races
#出力する
result.display_narrow_margin_races(race_results)


実行する

PostgreSQLサーバーを起動する。

pg_ctl -D /usr/local/var/postgres -l logfile start


Rubyコードを実行する。

bundle exec ruby activerecord_lesson.rb


結果

次のようにターミナルに出力される。
- 例 -

2017全日本軽量級選手権
RaceNo:100
男子シングルスカル 準決C組
1着: Aクルー Time: 07:30.56
2着: Bクルー Time: 07:30.57
Time差: 0.01秒
-----------------
2018全日本大学選手権
RaceNo:193
男子舵手なしクォドルプル 決勝
1着: Cクルー Time: 06:21.39 
2着: Dクルー Time: 06:21.47 
Time差: 0.08秒


終了する

PostgreSQLサーバーを停止する。

pg_ctl -D /usr/local/var/postgres -l logfile stop


注意するところ

ActiveRecordを使うときはpgのインストールも必要(requireは不要)。

parseする文字列は時間:分:秒の書式になっている必要があるので、今回の場合は桁を合わせるために時間単位の00:を加える必要がある。

Timeオブジェクトの引き算で求めた差はTimeオブジェクトではなくFloatオブジェクトになる。
なので今回の条件式は(parsed_time_2nd - parsed_time_1st) < 0.1となる(Floatオブジェクトの0.1を使う)。

今回の環境

macOS 10.14.1
PostgreSQL 10.3
Ruby 2.5.1p57
RubyGems 2.7.8
Bundler 1.17.1
Active Record 5.2.1
pg 1.1.3
dotenv 2.5.0