#ruby-on-rails #ruby
#ruby-on-rails #ruby
Вопрос:
Может кто-нибудь объяснить мне этот код? Что означает ||
синтаксис и как он работает?
array = [1, 2, 3, 4, 5, 3, 6, 7, 2, 8, 1, 9]
array.each_with_index.reduce({}) { |hash, (item, index)|
hash[item] = (hash[item] || []) << index
hash
}.select{ |key, value| value.size > 1 }
Комментарии:
1. Это не конвейер, это логическое OR , оно работает аналогично большинству других языков программирования.
2. Можете ли вы объяснить в этом коде, как сохраняются значения в хэше
3. @VijaySharma: это может помочь понять, если вы «распакуете» эту строку на 3-4 строки (путем извлечения переменных). Но сначала ознакомьтесь с этим логическим OR и как это работает в ruby (истинные / ложные значения)
Ответ №1:
Я бы переписал это так
array.each_with_index.reduce(Hash.new { Array.new }) do |hash, (item, index)|
hash.merge(item => hash[item] << index)
end.select { |_, indexes| indexes.size > 1 }
- Мы используем
each_with_index
, потому что хотим получить доступ к индексу во время цикла массива. Вы можете увидеть это позже рядом с item в качестве параметра в блокеreduce
‘s . reduce
позволяет нам «преобразовывать» коллекцию во что-то другое. В нашем случае мы хотим создать хэш из массива.- В блоке
reduce
‘s мы добавляем текущий индекс к паре ключ-значение для текущего элемента. Раньше яmerge
делал это только в одном выражении (обновлял хэш и использовал его как выражение для возврата). - В конце концов, мы сохраняем только пары ключ-значение, значения которых (и это массивы) имеют более одного элемента. Обратите внимание, что здесь нас не волнуют ключи, поэтому я вызвал параметр key
_
.
Комментарии:
1.
Hash.new { Array.new }
— осторожно с этим, это ловушка 🙂2. Что вы имеете в виду? Я бы сказал, что
Hash.new(Array.new)
🙂3. Это тоже, но этот хуже. 🙂 Ничего не сохраняется без явного
[]=
. eval.in/10936404. нет-нет, здесь все работает нормально, потому
Hash#merge
что . Но нужно быть начеку, это все, что я говорю 🙂
Ответ №2:
array = [1, 2, 3, 4, 5, 3, 6, 7, 2, 8, 1, 9]
array.each_with_index.reduce({}) do |hash, (item, index)|
hash[item] = (hash[item] || []) << index
hash
end.select do |key, value|
value.size > 1
end
Прежде всего, более чистый способ написания счетчиков, в которые вложена одна строка {}
, и многострочный do; end
.
array.each_with_index.reduce({}) do |hash, (item, index)|
# hash[item] gets either itself and if itself is nil it gets an empty
# array assigned and additionally the index gets added to this array
hash[item] = (hash[item] || []) << index
# return the hash for the reduce enumerator
hash
end
В этой части вы перебираете массив и передаете начальный пустой хэш с .reduce({})
помощью . Затем хэш преобразуется и возвращается в L3 в «цикле» и передается на следующую итерацию reduce
. Тогда ваш результат — это наращивание hash
, которое затем сразу же перечисляется с select
помощью where возвращаются только пары ключ-значение, размер значения которых больше 1.
Лучше всего было бы прочитать о Enumerators#reduce и о том, как объекты передаются в «цикл».
Комментарии:
1. «и, кроме того, индекс добавляется в этот массив» — и этот массив сохраняется обратно в хэш. Я думаю, что в этом случае не помешало бы быть более подробным 🙂
Ответ №3:
hash[item]
nil
сначала. nil || [] => []
, поэтому hash[item]
становится массивом. <<
вставьте элемент в массив.
надеюсь, это поможет вам понять, как хэш хранит значение
require "pp"
hash = {}
pp hash[1] # => nil
pp hash[1] || [] # => []
pp (hash[1] || []) << 1 # => [1]
pp hash[1] # => nil
hash[1] = (hash[1] || []) << 1
pp hash[1] # => [1]