Ruby добавление к хешу со значением массива

Я попробовал следующий код ruby, который, как я думал, вернет хэш длин слов в слова с этими длинами. Вместо этого он пуст.

map = Hash.new(Array.new)    
strings = ["abc","def","four","five"]
strings.each do |word|
  map[word.length] << word  
end   

Однако, если я изменю его на

map = Hash.new
strings = ["abc","def","four","five"]
strings.each do |word|
  map[word.length] ||= []
  map[word.length] << word  
end

Он работает.

Разве первая версия не создает хеш, значения по умолчанию которого представляют собой пустой массив? В этом случае я не понимаю, почему 2 блока дают разные значения.

Ответ 1

Проблема в том, что вы фактически не назначаете ничего хэш-ключам, вы просто используете оператор << для изменения существующего содержимого значения по умолчанию. Поскольку вы не назначаете ничего для хэш-ключа, оно не добавляется. Фактически вы заметите, что значение по умолчанию - это модифицированное значение:

h = Hash.new []
p h[0]           # []
h[0] << "Hello"
p h              # {}
p h[0]           # ["Hello"]
p h[1]           # ["Hello"]

Это связано с тем, что тот же объект Array поддерживается как значение по умолчанию. Вы можете исправить это, используя оператор +, хотя он может быть менее эффективным:

map = Hash.new []
strings = ["abc", "def", "four", "five"]

strings.each do |word|
    map[word.length] += [word]
end

И теперь он работает так, как ожидалось.

Ответ 2

В любом случае вы должны использовать Перечислить # group_by:

["abc", "def", "four", "five"].group_by(&:length)
#=> {3=>["abc", "def"], 4=>["four", "five"]}

Ответ 3

Я думаю, что первая версия действительно означает, что значением по умолчанию является только один массив. Второй пример явно создает новый массив, если он еще не существует.

Это похоже на хорошее чтение для дальнейшего уточнить.