こんにちは、エンジニアのさもです。
仕事で次のような要件のメソッドを実装しました。
- 配列[1,2,3,4,5,6,7,8,9]がある。
- この配列から、指定した数字(実際は検索結果の配列に対して、一つのオブジェクトを指定)を含む周囲5件を抜き出した配列を作る
(具体例)
- 1を指定した場合は、[1,2,3,4,5]
- 2を指定した場合は、[1,2,3,4,5]
- 5を指定した場合は、[3,4,5,6,7]
- 8を指定した場合は、[5,6,7,8,9]
- 9を指定した場合は、[5,6,7,8,9]
今回は、実装したメソッドの紹介と、Minitestでのテストコードを紹介します。
テスト駆動ではなかったので、後で完成したテストコードを載せています。
スポンサーリンク
目次
実装例
コード
以下が完成したコードです。
def arround_values(array, target, length) return [] if array.size == 0 center_index = array.find_index(target) margin_size = length / 2 # 前方から優先的に一致させる ahead_start_index = [0, center_index - margin_size].max ahead_end_index = ahead_start_index + (length - 1) ahead_match = array[ahead_start_index..ahead_end_index] # 後方から一致させる back_end_index = [(array.size-1), center_index + margin_size].min back_start_index = back_end_index - (length - 1) back_match = array[back_start_index..back_end_index] # 長い配列が取れた方を返す ahead_match.size > back_match.size ? ahead_match : back_match end
解説
引数は、対象とする配列、ターゲット、返す配列の(最大の)長さとしています。
以下、lengthは5と仮定します。
7~9行目は、ターゲットが前の方にある場合の処理です。真ん中ぐらいにターゲットがあった場合もここで正しく取れます。
- 7行目では、配列の先頭から抜き出すか、ターゲットの2つ手前から抜き出すかを選択しています。
- 8行目ではスタートから5つ後ろのインデックスを決定しています。
- 9行目で、配列から目的の配列を抜き出しています。
12~14行目はターゲットが後ろの方にある場合の対応です。
17行目で、2つの抜き出した配列のうち、長い方(上手く取れている方)を返しています。
テストを書く
せっかくですから、テストを書いておきましょう。
Rubyではテストを書くためのクラスが標準で付いてきます。私が使用したバージョンは2.2.3だったのでMinitest::Testクラスを継承したテストクラスを作ります。
require 'minitest/autorun' class ArroundValuesTest < Minitest::Test def setup @array = ['apple', 'banana', 'car', 'handle', 'user', 'cat', 'dog', 'bird'] @length = 5 end def test_target_first target = 'apple' result = arround_values(@array, target, @length) assert_equal result, ['apple', 'banana', 'car', 'handle', 'user'] end def test_target_second target = 'banana' result = arround_values(@array, target, @length) assert_equal result, ['apple', 'banana', 'car', 'handle', 'user'] end def test_target_middle target = 'handle' result = arround_values(@array, target, @length) assert_equal result, ['banana', 'car', 'handle', 'user', 'cat'] end def test_target_last target = 'bird' result = arround_values(@array, target, @length) assert_equal result, ['handle', 'user', 'cat', 'dog', 'bird'] end def test_target_before_last target = 'dog' result = arround_values(@array, target, @length) assert_equal result, ['handle', 'user', 'cat', 'dog', 'bird'] end def test_short_array target = 'banana' result = arround_values(['apple', 'banana', 'car'], target, @length) assert_equal result, ['apple', 'banana', 'car'] end def test_blank_array target = 'banana' result = arround_values([], target, @length) assert_equal result, [] end end
テストを各場所は、メソッド定義のすぐ後で問題ないです。
実行は ruby 保存したファイル名
で実行できます。
では、ざっくりとテストコードの説明をします。
- setupメソッドは、各テスト実行前に実行してくれます。
- テストはtest_から始まるメソッドを実装することで書くことが出来ます。
- assert_equalは引数同士が等しい事をテストするメソッドです。次のようにtrue, falseをテストするassertメソッドを使って書いてもいいですが、assert_equalの方がテストの内容が分かりやすくなります。
assert result == ['apple', 'banana', 'car']
最後に
仕事でRailsを触っていると、基本的にはDBから取ってきて表示or登録・更新だけで済んでしまいますが、たまに頭を使う実装があると楽しくなりますね。
以上、ざっくりと私が書いたコードを紹介しました。
読者登録をしていただけると、ブログを続ける励みになりますので、よろしくお願いします。