LoginSignup
19
12

More than 5 years have passed since last update.

Enumerable内での各要素出現数をカウントする

Last updated at Posted at 2015-02-06

Ruby 2.2 で追加されたメソッドまとめをみてた。
New Methods in Ruby 2.2

Enumerable#slice_whenのユースケースでEnumerable内での各要素出現数をカウントするのがステキだったので、2.2以前も含めてどう書けるパターンあるんだっけ?をメモ。

カウント対象のArrayを生成

labels = ["aaa", "bbb", "ccc"]
ary = Array.new(20) { labels.sample }
puts ary.join ", "
# bbb, aaa, bbb, bbb, bbb, aaa, ccc, aaa, aaa, bbb, ccc, ccc, bbb, bbb, aaa, aaa, ccc, bbb, aaa, aaa

2.2以前

Enumerable#injectを利用

r1 = ary.inject(Hash.new(0)) {|h, e| h.tap {|h| h[e] += 1} }
puts r1
# {"bbb"=>8, "aaa"=>8, "ccc"=>4}

2.2から

Enumerable#slice_whenを利用

あえて出力結果を Hash に寄せてみる。

r2 = ary.sort.slice_when(&:!=).map {|x| [x.first, x.size] }
puts Hash[r2]
# {"aaa"=>8, "bbb"=>8, "ccc"=>4}

Enumerable#group_by + Object#itself でもイケるな

r3 = ary.group_by(&:itself).map {|k, v| [k, v.size] }
puts Hash[r3]
# {"bbb"=>8, "aaa"=>8, "ccc"=>4}

メモ

  • Enumerable#injectは副作用ベースな書き方なのでいつも一瞬「うっ」ってなってる
  • Enumerable#slice_when#sortしないといけないね
  • 要素数が大きくなった場合に計算量とメモリ使用量の観点で効率的なのはどれだろう?
  • 富豪的な実装でもいいから簡単に書きたい時もあるよね、使い分けたい

コード全体

labels = ["aaa", "bbb", "ccc"]
ary = Array.new(20) { labels.sample }
puts ary.join ", "

r1 = ary.inject(Hash.new(0)) {|h, e| h.tap {|h| h[e] += 1} }
puts r1

r2 = ary.sort.slice_when(&:!=).map {|x| [x.first, x.size] }
puts Hash[r2]

r3 = ary.group_by(&:itself).map {|k, v| [k, v.size] }
puts Hash[r3]
19
12
3

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
19
12