2017-02-16 12 views
1

Учитывая следующий Hash hash с ключами в качестве символов и значений как массивы:Получить массив из хэша с массивами в качестве значений

hash 
#=> {:stage_item=>[:stage_batch_id, :potential_item_id], :item=>[:id, :size, :color, :status, :price_sold, :sold_at], :style=>[:wholesale_price, :retail_price, :type, :name]} 

Как получить массив с просто значением (массивы) складываются вместе?

Я знаю, что могу использовать #each_with_object и #flatten:

hash.each_with_object([]) { |(k, v), array| array << v }.flatten 
#=> [:stage_batch_id, :potential_item_id, :id, :size, :color, :status, :price_sold, :sold_at, :wholesale_price, :retail_price, :type, :name] 

Но я ожидал только #each_with_object работы:

hash.each_with_object([]) { |(k, v), array| array += v } 
#=> [] 

Я хотя точка каждого с объекта он отслеживает (в данном случае это array), поэтому я могу += это как в следующем примере:

arr = [1,2,3] 
#=> [1, 2, 3] 
arr += [4] 
#=> [1, 2, 3, 4] 

Что мне не хватает?

ответ

4

Array << .. изменяет исходный массив на месте:

irb(main):014:0> a = original = [] 
=> [] 
irb(main):015:0> a << [1] 
=> [[1]] 
irb(main):016:0> a 
=> [[1]] 
irb(main):017:0> original 
=> [[1]] 
irb(main):018:0> a.equal? original # identity check 
=> true 

время, Array += .. возвращает новый массив без изменения исходного массива:

irb(main):019:0 a = original = [] 
=> [] 
irb(main):020:0> a += [1] 
=> [1] 
irb(main):021:0> a 
=> [1] 
irb(main):022:0> original 
=> [] 
irb(main):023:0> a.equal? original 
=> false 

По словам Enumerable#each_with_object documentation,

Итерирует заданный блок для каждого элемента с произвольным объектом , и возвращает первоначально заданный объект.

Если блок не указан, возвращает перечислитель.

поэтому, в случае +=, возвращается исходный пустой массив, который не изменяется.


Кстати, вместо того, чтобы использовать each_with_object, вы можете просто использовать Hash#values method, который возвращает новый массив заполняется значениями из хэша:

hash.values.flatten 
2

Это действительно тот случай, когда inject будет лучше подходит, если вы настаиваете на использовании Array#+:

hash.inject([]) { |a, (_, v)| a + v } 

причина вы обычно используют each_with_object г ather than inject - это если вы хотите скопировать результаты на месте, а не использовать возвращаемое значение блока в качестве аккумулятора. В этом случае array1 + array2 возвращает конкатенацию, поэтому вам не нужно использовать оператор (или метод), который изменяет объект memo, потому что Array#+ возвращает то, что вы хотите передать в следующий цикл.

В то время как мы здесь, это было бы проще:

hash.values.flatten 
hash.values.inject(:+) 
h.values.flat_map(&:itself) 

и второй даже использует Array#+ если вы действительно как вызов этого метода.

 Смежные вопросы

  • Нет связанных вопросов^_^