arr = [1, 2, 1, 3, 2, 1]
Вы можете написать свой код без наворотов
def duped_index(arr)
result = {}
arr.each_with_index do |ele, idx|
result[ele] = [] unless result.key?(ele)
result[ele] << idx
end
result.select { |ele, indices| indices.length > 1 }
end
duped_index(arr)
#=> {1=>[0, 2, 5], 2=>[1, 4]}
Другой способ заключается в создании пустых массивов на лету, по мере необходимости
def duped_index(arr)
result = {}
arr.each_with_index { |ele, idx| (result[ele] ||= []) << idx }
result.select { |ele, indices| indices.length > 1 }
end
duped_index(arr)
#=> {1=>[0, 2, 5], 2=>[1, 4]}
Ruby синтаксический анализатор расширяет сокращение присваивание result[ele] ||= []
:
result[ele] = result[ele] || = []
Если result
не имеет ключа ele
, result[ele] #=> nil
, поэтому
result[ele] = nil || = []
#=> []
Если result
имеет ключ ele
result[ele]
остается без изменений. Следовательно,
(result[ele] ||= []) << idx
приводит к добавлению idx
к массиву (пустому или иному), который является значением result
для ключа ele
.
Этот метод будет чаще пишется так:
def duped_index(arr)
arr.each_with_index.with_object({}) { |(ele, idx), result|
(result[ele] ||= []) << idx }.
select { |ele, indices| indices.length > 1 }
end
Третий способ - создать га sh с про c по умолчанию, как в вопросе
Предположим:
result = Hash.new { |hash, key| hash[key] = [] }
#=> {}
Теперь выполните следующую операцию:
result['dog'] << 'woof'
#=> ["woof"]
result
#=> {"dog"=>["woof"]}
Когда выполняется result['dog']
Ruby видит это result.key? #=> false
, поэтому она выполняет блок, сначала присваивание значений переменным блока:
hash, key = [result, 'dog']
#=> [{}, 'dog']
hash
#=> {}
key
#=> 'dog'
Затем выполняется:
hash['key'] = []
, что приводит к:
result
#=> { 'dog'=>[] }
Затем она выполняет:
result['dog'] << 'woof'
result
#=> {"dog"=>["woof"]}
Теперь предположим, что мы выполняем:
result['dog'] << 'I love kibble!'
result
#=> {"dog"=>["woof", "I love kibble!"]}
На этот раз Ruby видит, что result
имеет ключ 'dog'
, поэтому она просто добавляет "I love kibble!"
в массив result['dog']
, не ссылаясь блок.
Давайте рассмотрим другой пример:
result = Hash.new do |hash, key|
puts "I just launched the missiles...just kidding"
hash[key] = []
end
result['dog'] << 'woof'
I just launched the missiles...just kidding
#=> ["woof"]
Поведение такое же, как и раньше, за исключением отображения сообщения зыбь. Дело в том, что вы можете поместить любой код, который вам нравится, в блок, извлекая данные из базы данных в качестве примера (хотя я не думаю, что это обычное использование процедур по умолчанию).
Метод, использующий эту форму Hash # new обычно пишут:
def duped_index(arr)
arr.each_with_index.
with_object(Hash.new { |h,k| h[k]=[] }) { |(ele,idx), result|
result[ele] << idx }.
select { |ele, indices| indices.length > 1 }
end
Выбор того, какой подход выбрать, в основном зависит от вкуса, но я ожидаю, что большинство Rubyists выберет # 2 или # 3.