2015-06-29 2 views
2

У меня есть многомерный массив, такие как:Превратить многомерный массив в хэш без перезаписи значений

array = [["stop", "halt"],["stop", "red"],["go", "green"],["go","fast"],["caution","yellow"]] 

И я хочу, чтобы превратить его в хэш, как это:

hash = {"stop" => ["halt","red"], "go" => ["green","fast"], "caution" => "yellow"} 

Однако, когда я array.to_h значения перезаписывать друг друга, и я получаю:

hash = {"stop" => "red", "go" => "fast", "caution" => "yellow"} 

Как получить нужный массив?

ответ

2

Это один из способов. Он использует Enumerable#each_with_object и форму Hash#update (aka merge!), в которой используется блок для определения значений ключей, присутствующих в обоих хешах, которые сливаются.

array << ["stop", "or I'll fire!"] 

array.each_with_object({}) { |(f,l),h| 
    h.update(f=>l) { |_,ov,nv| ov.is_a?(Array) ? ov << nv : [ov, nv] } } 
    #=> {"stop"=>["halt", "red", "or I'll fire!"], 
    # "go"=>["green", "fast"], 
    # "caution"=>"yellow"} 

код упрощается, если вы хотите, чтобы все значения в хэше быть массивами (т.е. "caution"=>["yellow"]), который, как правило, более удобным для последующих расчетов:

array.each_with_object({}) { |(f,l),h| h.update(f=>[l]) {|_,ov,nv| ov+nv }} 
    #=> {"stop"=>["halt", "red", "or I'll fire!"], 
    # "go"=>["green", "fast"], 
    # "caution"=>["yellow"]} 
+0

Спасибо, Кэри! Я всего лишь несколько недель в кодировании (на любом языке), поэтому я все еще пытаюсь ознакомиться со многими методами и сокращениями. Я искал статьи для хороших объяснений each_with_object и не могу найти ничего, что действительно помогло бы мне понять это. Вы знаете, где я могу найти хорошее объяснение? Кроме того, существует ли эффективный способ сделать выше, но сохранить все значения в виде массивов? –

+0

Гаррет, нет ничего загадочного в «Перечислении # each_with_object», который дебютировал в версии 1.9. Предположим, что 'a' является массивом, и вы хотите сделать это:' b = []; a.each {| e | b << e + 1; b'; то есть 1. создать объект ('b = []'); 2. делать что-то с каждым элементом 'a'; и 3. вернуть объект ('b'). Это очень распространенная картина. 'a.each_with_object ([]) {| e, b | b << e + 1} 'позволяет сделать это в одной строке, а не в трех. Ни больше ни меньше. Подумайте о методе как «каждый» с объектом ». Я отредактирую для ответа на другой вопрос. –

+0

Спасибо за объяснение и дополнительный код выше! Это добавляет много разъяснений. –

1

Один из способов сделать это :

array.inject({}) {|r, (k, v)| r[k] &&= [*r[k], v]; r[k] ||= v; r } 

Это довольно грязно, хотя. Выписали, это выглядит следующим образом:

def to_hash_with_duplicates(arr) 
    {}.tap do |r| 
    arr.each do |k, v| 
     r[k] &&= [*r[k], v] # key already present, turn into array and add value 
     r[k] ||= v    # key not present, simply store value 
    end 
    end 
end 

Edit: Думать немного больше, обновление-с-блок решения @cary-swoveland «s лучше, потому что она обрабатывает nil и false значения правильно.

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