Help us understand the problem. What is going on with this article?

with_indexが便利だという話とstable_sort_by

More than 3 years have passed since last update.

Rubyはいろんなことがone lineで書けて便利ですよね。

いろんなことを1行に書けるようにするには

String, Array, Hash, Enumerable

あたりの、リスト系の構造になったものにどういうメソッドがあるかを覚えておけば、たいていのことは1行で書けます。

今回の話は、これに加えて、

Enumerator

のメソッドも知っておくともっと世界が広がるよという話です。

map_with_indexが欲しい!

例えば、あるユーザーのリストに対して、名前の前に通し番号をふった文字列を出力したい(あんまり無さそうな例でごめんなさい)という要求があったとしましょう。

それをただ出力すれば良いのであれば、each_with_indexを使って

users.each_with_index do |user, i|
  puts "#{i+1}: #{user.name}"
end

とすれば実現できます。

ですが、この結果が配列として欲しいとなると、mapを使いたいところですが、map_with_indexなんて関数ないしなぁとなります。

これを解決する一つの方法としては、each_with_indexの結果をmapして

users.each_with_index.map { |user, i| "#{i+1}: #{user.name}" }

とやる方法があります。

でも、これ、カッコ悪いですよね?

で、調べてみると、ありました!Enumeratorの中にwith_indexというメソッドが!
これを使うと、

users.map.with_index { |user, i| "#{i+1}: #{user.name}" }

と書くことができ、とても読みやすくなります。
めでたしめでたし。

ちなみに、with_indexは何の数値からはじめるかを引数にとることができます。なので、

users.map.with_index(1) { |user, i| "#{i}: #{user.name}" }

として、1-originにすることでi+1と毎回しなくてよくなり、少し綺麗に書くことができます。

with_indexを使って安定ソートを1行で

with_indexにはかなり使える応用があって、それが安定ソート(Stable Sort)です。
何故かRubyはデフォルトで安定ソートが提供されてないんですよね。

例えばFacebookの投稿のようなモデルがあって、それの配列をLike数が多い順、Like数が同じ場合は作られた順でソートしようと思った場合、もしsort_byがstableであれば以下の様なコードでうまくいくはず。

posts.sort_by(&:created_at).sort_by(&:like_count)

ですが、多分ソートの内部実装がQuick Sortなため、値が等しい時の順番が崩れてしまいます。

posts.sort_by(&:created_at).sort_by.with_index{ |x,i| x.like_count }

と書いて、2回目のソートをstable sort風にすればone lineで書くことができるようになります。

普通にstable_sort_byを使えるようにする

上でもコマンド上でやるときはいいですが、通常は

enumerable.rb
module Enumerable
  def stable_sort_by
    self.sort_by.with_index{ |e, index| [yield(e), index] }
  end
end

とやった上で、

posts.stable_sort_by(&:created_at).stable_sort_by(&:like_count)

のほうが綺麗ですね。

awakia
検索とか推薦とかやってきたエンジニア。早稲田の山名研出身。大学院の頃、論文を書こうとしない僕を見かねた教授に、北京のMSRAに追放されるが3ヶ月後無事帰還。 大学を卒業後、エンジニアのブラックホールとの別名を持つGoogleに吸収されそうになるが1年2ヶ月後無事生還。 現在は、Wantedly(https://www.wantedly.com/ )の4番目のエージェントとして救出活動に専念。
http://awakia-n.hatenablog.com/
wantedly
「シゴトでココロオドル」ためのビジネスSNS「Wantedly」の開発・運営をしています。
https://wantedlyinc.com/ja/presentations
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした