LoginSignup
6
2

More than 3 years have passed since last update.

要素とその個数をペアにしたハッシュを求めるコードが試行錯誤ののちにだんだん読みやすくなっていく例

Last updated at Posted at 2017-11-29

次の配列があったとして、

a = [:c, :b, :c, :c, :a, :b]

要素とその個数をペアにしたハッシュを求めるコードが試行錯誤ののちにだんだん読みやすくなっていく例です。(※主観です)

h = {}; a.each { |e| h[e] = a.count(e) }; h                # => {:c=>3, :b=>2, :a=>1}
h = {}; a.each { |e| h[e] ||= a.count(e) }; h              # => {:c=>3, :b=>2, :a=>1}
h = {}; a.uniq.each { |e| h[e] = a.count(e) }; h           # => {:c=>3, :b=>2, :a=>1}
h = {}; a.each {|e| h[e] = (h[e] || 0) + 1 }; h            # => {:c=>3, :b=>2, :a=>1}
h = {}; a.each {|e| h[e] = h.fetch(e, 0) + 1 }; h          # => {:c=>3, :b=>2, :a=>1}
h = {}; a.each {|e| h[e] = h.fetch(e) { 0 } + 1 }; h       # => {:c=>3, :b=>2, :a=>1}
h = {}; a.each {|e| h[e] = h[e].to_i.next }; h             # => {:c=>3, :b=>2, :a=>1}
h = Hash.new(0); a.each { |e| h[e] += 1 }; h               # => {:c=>3, :b=>2, :a=>1}
Hash.new(0).tap { |h| a.each { |e| h[e] += 1 } }           # => {:c=>3, :b=>2, :a=>1}
Hash[*a.flat_map { |e| [e, a.count(e)] }]                  # => {:c=>3, :b=>2, :a=>1}
a.zip(a.collect { |e| a.count(e) }).to_h                   # => {:c=>3, :b=>2, :a=>1}
a.map { |e| [e, a.count(e)] }.to_h                         # => {:c=>3, :b=>2, :a=>1}
a.uniq.map { |e| [e, a.count(e)] }.to_h                    # => {:c=>3, :b=>2, :a=>1}
a.each_with_object(Hash.new(0)) { |e, m| m[e] += 1 }       # => {:c=>3, :b=>2, :a=>1}
a.each_with_object({}) { |e, m| m[e] = (m[e] || 0) + 1 }   # => {:c=>3, :b=>2, :a=>1}
a.inject(Hash.new(0)) { |a, e| a[e] += 1; a }              # => {:c=>3, :b=>2, :a=>1}
a.inject(Hash.new(0)) { |a, e| a.tap { |a| a[e] += 1 } }   # => {:c=>3, :b=>2, :a=>1}
a.inject({}){|a, e| a.merge(e => 1) { |_, a, b| a + b } }  # => {:c=>3, :b=>2, :a=>1}
a.group_by{|e|e}.collect { |k, v| [k, v.count] }.to_h      # => {:c=>3, :b=>2, :a=>1}
a.group_by{|e|e}.inject({}){|a,(k,v)|a.merge(k => v.size)} # => {:c=>3, :b=>2, :a=>1}
a.group_by{|e|e}.transform_values { |e| e.size }           # => {:c=>3, :b=>2, :a=>1}
a.group_by(&:itself).transform_values(&:size)              # => {:c=>3, :b=>2, :a=>1}
a.tally                                                    # => {:c=>3, :b=>2, :a=>1}

ついでにそれぞれの速度は次のようになりました。

RUBY_VERSION                    # => "2.7.0"
require "active_support/core_ext/benchmark"
def _; "%7.2f ms" % Benchmark.ms { 50_0000.times { yield } } end
_ { h = {}; a.each { |e| h[e] = a.count(e) }; h                } # => " 528.82 ms"
_ { h = {}; a.each { |e| h[e] ||= a.count(e) }; h              } # => " 437.72 ms"
_ { h = {}; a.uniq.each { |e| h[e] = a.count(e) }; h           } # => " 408.69 ms"
_ { h = {}; a.each {|e| h[e] = (h[e] || 0) + 1 }; h            } # => " 371.57 ms"
_ { h = {}; a.each {|e| h[e] = h.fetch(e, 0) + 1 }; h          } # => " 412.14 ms"
_ { h = {}; a.each {|e| h[e] = h.fetch(e) { 0 } + 1 }; h       } # => " 475.63 ms"
_ { h = {}; a.each {|e| h[e] = h[e].to_i.next }; h             } # => " 529.86 ms"
_ { h = Hash.new(0); a.each { |e| h[e] += 1 }; h               } # => " 418.89 ms"
_ { Hash.new(0).tap { |h| a.each { |e| h[e] += 1 } }           } # => " 452.42 ms"
_ { Hash[*a.flat_map { |e| [e, a.count(e)] }]                  } # => " 835.85 ms"
_ { a.zip(a.collect { |e| a.count(e) }).to_h                   } # => " 934.52 ms"
_ { a.map { |e| [e, a.count(e)] }.to_h                         } # => " 685.76 ms"
_ { a.uniq.map { |e| [e, a.count(e)] }.to_h                    } # => " 510.16 ms"
_ { a.each_with_object(Hash.new(0)) { |e, m| m[e] += 1 }       } # => " 503.50 ms"
_ { a.each_with_object({}) { |e, m| m[e] = (m[e] || 0) + 1 }   } # => " 452.22 ms"
_ { a.inject(Hash.new(0)) { |a, e| a[e] += 1; a }              } # => " 522.28 ms"
_ { a.inject(Hash.new(0)) { |a, e| a.tap { |a| a[e] += 1 } }   } # => " 761.82 ms"
_ { a.inject({}){|a, e| a.merge(e => 1) { |_, a, b| a + b } }  } # => "1017.48 ms"
_ { a.group_by{|e|e}.collect { |k, v| [k, v.count] }.to_h      } # => " 866.90 ms"
_ { a.group_by{|e|e}.inject({}){|a,(k,v)|a.merge(k => v.size)} } # => "1075.56 ms"
_ { a.group_by{|e|e}.transform_values { |e| e.size }           } # => " 541.91 ms"
_ { a.group_by(&:itself).transform_values(&:size)              } # => " 689.96 ms"
_ { a.tally                                                    } # => " 260.09 ms"

バージョン毎の速度比較

2.4.1 2.6.5 2.7.0
1034.64 487.52 528.82
826.21 421.96 437.72
950.99 382.07 408.69
580.79 366.15 371.57
626.53 393.59 412.14
697.68 451.79 475.63
694.42 494.54 529.86
617.90 395.69 418.89
644.93 417.29 452.42
1406.28 764.19 835.85
1499.53 907.43 934.52
1238.92 652.00 685.76
1006.41 490.25 510.16
724.30 472.74 503.50
686.70 451.96 452.22
733.23 477.62 522.28
944.87 667.43 761.82
4706.51 889.69 1017.48
1290.49 772.25 866.90
3063.37 953.74 1075.56
1016.31 551.76 541.91
1021.88 607.18 689.96
260.09
6
2
8

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
6
2