配列をユニークにしてその個数とともに返せ
配列をユニークにしてその個数とともに返せ。
golf - babie, you're my home
具体的には、["foo", "bar", "foo", "baz", "bar", "foo"] を、[ ["foo", 3], ["bar", 2], ["baz", 1] ] にする。
考える道筋を全部書いてみます。
(1) 配列を表示させます。
a = ["foo", "bar", "foo", "baz", "bar", "foo"] a.each do |e| puts e end
(1)の実行結果です。
foo bar foo baz bar foo
(2) Hashを使って要素の数を数えます。
h = Hash.new(0) a = ["foo", "bar", "foo", "baz", "bar", "foo"] a.each do |e| h[e] += 1 end h.keys.each do |k| puts "#{k} => #{h[k]}" end
(2)の実行結果です。
baz => 1 foo => 3 bar => 2
(3) Hashを元にして結果の配列を構成します。
h = Hash.new(0) a = ["foo", "bar", "foo", "baz", "bar", "foo"] a.each do |e| h[e] += 1 end r = [] h.keys.sort.each do |k| r << [ k, h[k]] end p r
(3)の実行結果です。
[["bar", 2], ["baz", 1], ["foo", 3]]
(4) 問題文に示されている結果とずれていることに気づいたので、元配列の初出順にした。
h = Hash.new(0) a = ["foo", "bar", "foo", "baz", "bar", "foo"] a.each do |e| h[e] += 1 end r = [] a.each do |k| next unless h[k] r << [ k, h[k]] h[k] = nil end p r
(4)の実行結果です。でもぜんぜんgolf(プログラムサイズをできるだけ小さく)じゃない。
[["foo", 3], ["bar", 2], ["baz", 1]]
(5) 発想を変えてArray#mapを使ってみることにします。まず、ri Array#mapを見て使い方を練習しましょう。
a = ["foo", "bar", "foo", "baz", "bar", "foo"] b = a.map do |e| [ e, 1 ] end p b
(5)の実行結果です。
[["foo", 1], ["bar", 1], ["foo", 1], ["baz", 1], ["bar", 1], ["foo", 1]]
(6) いやいや(5)の発想は方向が間違っているような気がします。h[e]の値を調べることで初出リストをkに作ってみました。
a = ["foo", "bar", "foo", "baz", "bar", "foo"] h = Hash.new(0) k = [] a.each do |e| k << e if h[e] == 0 h[e] += 1 end b = k.map do |e| [ e, h[e] ] end p b
(6)の実行結果です。
[["foo", 3], ["bar", 2], ["baz", 1]]
ぜんぜんgolfじゃないけれど、何となく落ち着いちゃったからいいや(←投げやり)。
追記:
(A) 田中“ハルヒコスプレイヤー”ばびえ嬢の解答が出てきました。
%w|foo bar foo baz bar foo|.inject(Hash.new 0){|r,e|r[e]+=1;r}.to_a
(B) とりあえず(A)の結果をpしてみましょう。
p %w|foo bar foo baz bar foo|.inject(Hash.new 0){|r,e|r[e]+=1;r}.to_a
(B)の実行結果です。警告(warning)が出てます。
a.rb:1: warning: parenthesize argument(s) for future version [["baz", 1], ["foo", 3], ["bar", 2]]
(C) 1バイト増えるのを我慢して括弧を付けましょう。
p %w|foo bar foo baz bar foo|.inject(Hash.new(0)){|r,e|r[e]+=1;r}.to_a
(C)の実行結果です。
[["baz", 1], ["foo", 3], ["bar", 2]]
(C)にコメントを入れてみました。かえってわかりにくくなっちゃった。
p( # では、何を表示するかというと… %w| foo bar foo baz bar foo |. # %w|...|は文字列を空白で区切って配列を作ります。 inject( # で、その配列にinjectするんですが、 Hash.new(0) # まず最初にHashオブジェクトを作ります。デフォルト値は0です(これはカウント値の初期値ということ) ) { |r, e| # 作ったHashオブジェクトはrを使って連綿と渡されていきます。eには配列の要素が毎回入って処理が繰り返されます。 r[e] += 1; # で、今回のeの処理。カウントを1個増やします。 r # 更新したHashオブジェクトを「次」に渡します。 }. # で、injectメソッドの戻り値は(すべての要素が反映された後の)Hashオブジェクトになる。 to_a # それを配列に返したもの ) # …を表示するのですね。