Rails отображает массив хэшей на один хеш

94

У меня есть такой массив хэшей:

 [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

И я пытаюсь сопоставить это с одним хешем следующим образом:

{"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

Я добился этого, используя

  par={}
  mitem["params"].each { |h| h.each {|k,v| par[k]=v} } 

Но мне было интересно, можно ли сделать это более идиоматическим способом (желательно без использования локальной переменной).

Как я могу это сделать?

Барт Платак
источник

Ответы:

164

Вы могли сочинять Enumerable#reduceи Hash#mergeвыполнять то, что хотите.

input = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
input.reduce({}, :merge)
  is {"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

Уменьшение массива похоже на вставку вызова метода между каждым его элементом.

Например [1, 2, 3].reduce(0, :+), как говорит 0 + 1 + 2 + 3и дает 6.

В нашем случае мы делаем нечто подобное, но с функцией слияния, которая объединяет два хэша.

[{:a => 1}, {:b => 2}, {:c => 3}].reduce({}, :merge)
  is {}.merge({:a => 1}.merge({:b => 2}.merge({:c => 3})))
  is {:a => 1, :b => 2, :c => 3}
cjhveal
источник
1
Спасибо, это отличный ответ :) Очень красиво объяснено!
Барт Платак 08
42
input.reduce (&: merge) достаточно.
Redgetan
@redgetan это чем-то отличается от input.reduce(:merge)?
Дэвид ван Гест
1
@ Дэвид ван Гест: В этом случае они эквивалентны. Унарный амперсанд, используемый здесь, строит блок из символа. Однако у reduce есть особый случай, когда можно использовать символ. Я хотел избежать использования унарного оператора амперсанда, чтобы упростить пример, но redgetan правильно, что начальное значение в этом случае необязательно.
cjhveal
1
Обратите внимание, что если вы используете merge!вместо mergeнего, будет изменен первый хеш (который может вам не понадобиться), но не будет создаваться промежуточный хеш для каждого нового слияния.
Phrogz
51

Как насчет:

h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)
Шигея
источник
Эта схема фактически совпадает с тем, что ответил Джошуа, но многократно применяет #merge (имя метода, переданное как символ) ко всем хешам (думайте о inject как о введении оператора между элементами). Обратитесь к #inject .
shigeya 08
2
Почему нам не нужен амперсанд, как в h.inject (&: merge)?
Donato
5
Поскольку метод inject принимает символ в качестве параметра, который также должен интерпретироваться как имя метода. Это особенность инъекции.
shigeya
9

Используйте #inject

hashes = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
merged = hashes.inject({}) { |aggregate, hash| aggregate.merge hash }
merged # => {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Джошуа Чик
источник
0

Здесь вы можете использовать либо inject, либо reduce из класса Enumerable, так как оба они являются псевдонимами друг друга, поэтому ни один из них не дает преимущества в производительности.

 sample = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

 result1 = sample.reduce(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

 result2 = sample.inject(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Нихил Мохадикар
источник