Ruby Hash#keyが返すキーの順序は不定?

Python3について調べていたら、辞書(dict型)の順序は不定*1という一文を見つけました。
以前「RubyのHashの順番」について調べていた事を思い出したので、忘れないようにメモ。

生成時の要素順

結論から書くと、1.9系以降はHashの要素の順序(生成順)が保持されるようになりました。*2

Rubyリファレンスマニュアル :class Hash (Ruby 1.8.7)

ハッシュに含まれる要素の順序は保持されません。 列挙する順序は不定です。


Rubyリファレンスマニュアル:class Hash (Ruby 1.9.3)

ハッシュに含まれる要素の順序が保持されるようになりました。 ハッシュにキーが追加された順序で列挙します。

  irbで確認。

  • 1.8.7
v = {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4}
=> {"d"=>4, "c"=>3, "b"=>2, "a"=>1}

h = {:b => 2, :d => 4, :a => 1, :c => 3}
=> {:b=>2, :a=>1, :c=>3, :d=>4}
  • 1.9.3
v = {'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4}
=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4}

h = {:b => 2, :d => 4, :a => 1, :c => 3}
=> {:b=>2, :d=>4, :a=>1, :c=>3}


Hashクラスのメソッドが返す要素の順番

1.9.3以降のHashクラスのメソッドが返す結果について、リファレンスマニュアルには以下のように書いてあります。(リファレンスマニュアル((https://docs.ruby-lang.org/ja/)))

  • 1.9.3
    Hash#key => 該当するキーが複数存在する場合、どのキーを返すかは不定
    Hash#shift => どの要素を返すかは不定
    Hash#invert => 異なるキーに対して等しい値が登録されている場合の結果は不定

  • 2.1.0
    Hash#key => 該当するキーが複数存在する場合、どのキーを返すかは不定
    Hash#shift => キーが追加された順で先頭の要素をひとつ取り除き、 [key, value]という配列として返します。
    Hash#invert => 異なるキーに対して等しい値が登録されている場合、最後に定義されている値が使用されます。

実際にirbで確認。

  • 1.9.3
h = {a: 10, b: 10, c: 10, d: 20}

h.key(10)  #=> :a

h.shift  #=> [:a, 10]

h.invert  #=> {10=>:c, 20=>:d}

*2.1.0~

h = {a: 10, b: 10, c: 10, d: 20}

h.key(10)  #=> :a

h.shift  #=> [:a, 10]

h.invert  #=> {10=>:c, 20=>:d}

1.9系も不定ではないようです。

調べてみると、Hash#shift , Hash#invertはリファレンスの記述が1.8時代のままだったようで、後のリファレンスでは修正されたみたいです。*3*4

Hash#keyについては不定とありますが、挙動としては最初のキーを返しているように見えます。
これについて、過去のチケットを探したら以下のチケットを見つけました。

Bug #4760: Some documentation improvements for Hash#key - Ruby trunk - Ruby Issue Tracking System

一応ruremaにIssueを立てておいたほうがいいのか、また後日考えます。

1/17追記

とりあえずIssueを立てた*5のですが、Hash#keyに関しては#4760のチケットで

Change rdoc from "the first occurence" to "an occurrence" since first occurrence is not a specification of Hash#key.

「最初のキーを返すとは保証されていないのが仕様」と返信されていました。(読み落としていた。。)

答えてくださったsho-hさん、本当にありがとうございました。:)