Может кто-нибудь, пожалуйста, объяснить использование each_with_index, reduce, select chained в коде вместе с OR (||)?

#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/1093640

4. нет-нет, здесь все работает нормально, потому 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]