LoginSignup
11
4

More than 5 years have passed since last update.

配列を走査する際に現在の値と次の値を比較したいなら Enumerable#each_cons が便利

Last updated at Posted at 2017-02-08

例題

配列からある要素がひとつ前の要素と別の値になる箇所のインデックス、その一覧を取り出したいとします。

names = ['ゆの', 'ゆの', 'ゆの', '宮子', '宮子', '沙英', 'ヒロ', 'ヒロ', 'ヒロ']

例えば、上記の配列では [3, 5, 6] です。

方法 1

実直にやるとこのような実装になると思います。

names
  .each_with_object([])
  .with_index do |(name, indexes), i|
    indexes << i + 1 if names[i + 1] && name != names[i + 1]
  end

#=> [3, 5, 6]

names[i + 1] で次の要素を取得し、それが存在し、なおかつ現在の値と異なるかどうかを調べています。ループ内で、元の配列 names を利用しているのが個人的に好きではないです。

方法 2

今回オススメするのが以下の方法です。

names
  .each_cons(2)
  .each_with_object([])
  .with_index do |((current_val, next_val), indexes), i|
    indexes << i + 1 if current_val != next_val
  end

#=> [3, 5, 6]

ループ内で直接次の要素にアクセスできるので便利です。

この方法では Enumerable#each_cons を使って、配列をあらかじめ重複ありの 2 要素ずつに区切り、それからさらにループを回すというアプローチを取っています。

names.each_cons(2).to_a
#=> [["ゆの", "ゆの"], ["ゆの", "ゆの"], ["ゆの", "宮子"], ["宮子", "宮子"], ["宮子", "沙英"], ["沙英", "ヒロ"], ["ヒロ", "ヒロ"], ["ヒロ", "ヒロ"]]

なお、Enumerable#each_slice と挙動が異なることに注意してください。

names.each_slice(2).to_a
#=> [["ゆの", "ゆの"], ["ゆの", "宮子"], ["宮子", "沙英"], ["ヒロ", "ヒロ"], ["ヒロ"]]
11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4