Учитывая, что у вас есть ответы, которые дают хорошие объяснения вашей проблемы, я хотел бы представить еще несколько подобных Ruby-подходов, которые можно было бы использовать.Все эти методы создают хэш-код h
, значения которого представляют собой массивы слов, которые являются анаграммами друг друга, которые могут быть извлечены из хэша, выполнив h.values
.
Использование Enumerable#group_by и Array#sort
Это, возможно, самый прямой подход.
words.group_by { |w| w.each_char.sort }.values
#=> [["demo", "dome", "mode"], ["none", "neon"], ["tied", "diet", "edit", "tide"],
# ["evil", "live", "veil", "vile"], ["fowl", "wolf", "flow"]]
group_by
производит
words.group_by { |w| w.each_char.sort }
#=> {["d", "e", "m", "o"]=>["demo", "dome", "mode"],
# ["e", "n", "n", "o"]=>["none", "neon"],
# ["d", "e", "i", "t"]=>["tied", "diet", "edit", "tide"],
# ["e", "i", "l", "v"]=>["evil", "live", "veil", "vile"],
# ["f", "l", "o", "w"]=>["fowl", "wolf", "flow"]}
, после чего это просто вопрос извлечения значения этой хэш.
построить хеш путем добавления слова в массивы, которые являются значением хэша
words.each_with_object({}) { |w,h| (h[w.each_char.sort] ||= []) << w }.values
#=> [["demo", "dome", "mode"], ["none", "neon"], ["tied", "diet", "edit", "tide"],
# ["evil", "live", "veil", "vile"], ["fowl", "wolf", "flow"]]
Когда «демо» передаются в блок хэш h
пуст, поэтому блок переменных присваиваются значение
w = "demo"
h = {}
и расчет выполняется блок:
h[["d", "e", "m", "o"]] ||= []) << w
, как
w.each_char.sort
#=> ["d", "e", "m", "o"]
Рубин первым расширяет это
h[["d", "e", "m", "o"]] = (h[["d", "e", "m", "o"]] ||= []) << "demo"
В этот момент h
не имеет ключей, поэтому h[["d", "e", "m", "o"]]
вычисляется в nil
. Таким образом, выражение становится
h[["d", "e", "m", "o"]] = (nil ||= []) << "demo"
= [] << "demo"
= ["demo"]
Позже, когда «купол» встречается,
w = "dome"
w.each_char.sort
#=> ["d", "e", "m", "o"]
и так h
уже имеет этот ключ, расчет блока заключается в следующем.
h[["d", "e", "m", "o"]] = (h[["d", "e", "m", "o"]] ||= []) << "dome"
= (["demo"] ||= []) << "dome"
= ["demo"] << "dome"
= ["demo", "dome"]
Получает
words.each_with_object({}) { |w,h| (h[w.each_char.sort] ||= []) << w }
#=> {["d", "e", "m", "o"]=>["demo", "dome", "mode"],
# ["e", "n", "n", "o"]=>["none", "neon"],
# ["d", "e", "i", "t"]=>["tied", "diet", "edit", "tide"],
# ["e", "i", "l", "v"]=>["evil", "live", "veil", "vile"],
# ["f", "l", "o", "w"]=>["fowl", "wolf", "flow"]}
, после чего извлекается значение.
Вариант этого заключается в следующем.
words.each_with_object(Hash.new { |h,k| h[k] = []}) { |w,h|
h[w.each_char.sort] << w }.values
Смотрите документ для Hash::new для объяснения, в частности, обсуждение значений по умолчанию данных блоком.
Для каждого слова, объединить хэш, имеющий один ключ в первоначально пустой хэш
words.each_with_object({}) { |w,h|
h.update(w.each_char.sort=>[w]) { |_,o,n| o+n } }.values
Аргумент w.each_char.sort=>[w]
представляет собой сокращенную { w.each_char.sort=>[w] }
.
Это использует форму Hash#update (ака merge!
), который использует блок «разрешение» (здесь { |_,o,n| o+n }
) для определения значений ключей, которые присутствуют в обоих хешей начинают слиты. См. Документ для описания трех ключей этого блока (первая блок-переменная, общий ключ, не используется в этом расчете, поэтому я использовал символ подчеркивания).
Мне нравится титул этого «вопроса», –