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}

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"}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.