Feature #1840
closedHash に要素追加して self を返すメソッド
Description
=begin
配列から Hash を作るときなどに、よく
a.inject({}) {|h, x| h[x] = ...; h }
みたいにするのはもはやイディオムと言ってよいと思いますが(*1)、
「; h」の部分が少々オマジナイっぽいのが気になっていました。
気になったついでに検索してみたら他にもそのような意見を目にしたので(*2)、
Hash に「要素追加して self を返す」ようなメソッドを追加することを
提案してみます。
いくつか選択肢を考えてみました:
-
Hash#store が self を返す
仕様が変わるので互換性の面で問題が出るかもしれません。
しかも h.store(x, ...) だと字面が多くなるという。
h.store(*v) のように書けるケースなら短かくなるかもしれません。 -
Array#insert に合わせて Hash#insert を追加する
[]=, store, insert と似たようなのが複数あるのは、初心者にとって
単にわかりにくいだけかもしれません。 -
Hash#<< を追加する
ML を検索したら、[ruby-list:44686] でまつもとさんが、ここからの類推で言うとHashに「<<」演算子を追加したとしても、
a << b
は a.store(*b) と期待されてしまうのではないでしょうか。少な
くとも私は最初そう思いました。とおっしゃっているのを見つけました。
これを使うと、h << [k, v] << ... して h.assoc(k) した結果が Array
とまったく同じになる、という美しさがあります。
また、キーと値の配列に対して a.inject({}, &:<<) で済んでしまうのは
うれしいかもしれません。 -
Enumerable#each_with_object を使う
それはそれでありかもしれません。個人的には、each のついでに… と
いう名前を object が欲しい局面で使うのはなんとなく違和感があります。
似た名前の each_with_index が self を返すからかもしれません。 -
その他
このイディオムを使う局面は、キーの集合を別の集合にマッピングした新
しい Hash が欲しい、というケースだと思われます。それを
「キーの集合に {}を inject する」と表現することが、そもそも直感的で
ないとも考えられます。「zip して flatten して * 展開」というのと同様に¶
Hash 生成用のクラスメソッドとして、例えばこんなのがあると便利かも
しれません(*3)。def Hash.map(enum)
if block_given?
enum.inject({}) {|h, k| h[k] = yield k; h }
else
enum.inject({}) {|h, v| h.store(*v); h }
end
endex¶
Hash.map(string_keys, &:size) # 文字列 => サイズ
Hash.map(values.each_with_index) # 値 => インデクス
Hash.map(other_hash.map {|k, v| [v, k] }) # 値 => キー
他の方の意見やアイデアも聞いてみたいです。
(1)
Google Code Search で「lang:"ruby" inject({}).+;\s\w+\s*}」
とやったら 2,000 件ヒットしました。
(*2)
http://subtech.g.hatena.ne.jp/cho45/20071128/1196239612 とか
http://d.hatena.ne.jp/takkan_m/20071212/1197466247 とか
http://wota.jp/ac/?date=20081025#p09 とか
某chとか
(*3)
過去、某chに似たようなのを書きこんだのは私です
=end