Рейки, що відображають масив хешів на один хеш


91

У мене є масив хешів так:

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

І я намагаюся відобразити це в одному хеші так:

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

Я домігся цього за допомогою

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

Але мені було цікаво, чи можливо це зробити більш ідіоматично (бажано без використання локальної змінної).

Як я можу це зробити?

Відповіді:


159

Ви можете складати 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
Дякую, це чудова відповідь :) Дуже гарно пояснили!
Барт Платак

40
input.reduce (&: merge) достатньо.
redgetan

@redgetan чим це відрізняється від input.reduce(:merge)?
Девід ван Гіст,

1
@ Девід ван Гест: У цьому випадку вони рівнозначні. Використовуваний тут одинарний амперсанд створює блок із символу. Однак у reduce є особливий випадок, який приймає символ. Я хотів уникнути одинарного оператора амперсанда, щоб спростити приклад, але redgetan правильно, що початкове значення в цьому випадку необов’язкове.
cjhveal

1
Зверніть увагу, що якщо ви використовуєте merge!замість mergeнього, ви зміните перший хеш (чого ви можете не захотіти), але не створить посередницький хеш для кожного нового злиття.
Фрогц

50

Як щодо:

h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)

Ця схема фактично така ж, як і відповідь Джошуа, але неодноразово застосовуючи #merge (ім'я методу передається як символ) до всіх хешів (думайте про ін'єкцію як про ін'єкцію оператора між елементами). Зверніться до #inject .
shigeya

2
Як так, нам не потрібен амперсанд, як у h.inject (&: merge)?
Донато

5
Оскільки метод ін'єкції приймає символ як параметр, який також інтерпретується як ім'я методу. Це особливість ін’єкції.
shigeya

9

Використовуйте #inject

hashes = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
merged = hashes.inject({}) { |aggregate, hash| aggregate.merge hash }
merged # => {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

0

Тут ви можете використовувати або ін'єктувати або зменшити з перечислимого класу , як обидва вони є псевдонімами один одного , так що немає ніякої вигоди продуктивності або.

 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.