В Ruby задан массив в одной из следующих форм ...
[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
... как лучше всего преобразовать это в хеш в форме ...
{apple => 1, banana => 2}
ПРИМЕЧАНИЕ . Краткое и эффективное решение см. В ответе Марка-Андре Лафортуна ниже.
Этот ответ изначально предлагался в качестве альтернативы подходам с использованием flatten, которые на момент написания получили наибольшее количество голосов. Мне следовало пояснить, что я не намеревался представить этот пример как передовой опыт или эффективный подход. Оригинальный ответ следует.
Предупреждение! Решения, использующие flatten , не сохраняют ключи или значения массива!
Основываясь на популярном ответе @John Topley, давайте попробуем:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
Это вызывает ошибку:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
Конструктор ожидал массив четной длины (например, ['k1', 'v1,' k2 ',' v2 ']). Что еще хуже, другой массив, который сглаживается до одинаковой длины, просто молча дает нам хэш с неправильными значениями.
Если вы хотите использовать ключи или значения массива, вы можете использовать карту :
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
Это сохраняет ключ массива:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
h3 = Hash[*a3.flatten(1)]
вместо этогоh3 = Hash[*a3.flatten]
выдает ошибку.to_h
лучше.Просто используйте
Hash[*array_variable.flatten]
Например:
Использование
Array#flatten(1)
ограничивает рекурсию, поэтомуArray
ключи и значения работают должным образом.источник
Hash[*ary.flatten(1)]
, которая сохранит ключи и значения массива. Ихflatten
разрушает рекурсивный метод , которого достаточно легко избежать.Лучше всего использовать
Array#to_h
:Обратите внимание, что
to_h
также принимает блок:Примечание :
to_h
принимает блок в Ruby 2.6.0+; для ранних рубинов вы можете использовать мойbackports
драгоценный камень иrequire 'backports/2.6.0/enumerable/to_h'
to_h
без блока был представлен в Ruby 2.1.0.До Ruby 2.1 можно было использовать менее разборчивый
Hash[]
:Наконец, будьте осторожны с любыми решениями, которые используют
flatten
, это может создать проблемы со значениями, которые сами по себе являются массивами.источник
to_h
метод нравится больше, чем приведенные выше ответы, потому что он выражает намерение преобразования после работы с массивом.Array#to_h
ни другогоEnumerable#to_h
.[[apple, 1], [banana, 2], [apple, 3], [banana, 4]]
и я хочу получить результат как{"apple" =>[1,3], "banana"=>[2,4]}
?Обновить
Сегодня выпущен Ruby 2.1.0 . И у меня есть
Array#to_h
( примечания к выпуску и ruby-doc ), который решает проблему преобразования файлаArray
вHash
.Пример документации Ruby:
источник
Вторая форма более простая:
a = массив, h = хэш, r = хэш возвращаемого значения (тот, который мы накапливаем), i = элемент в массиве
Самый простой способ, который я могу придумать для создания первой формы, выглядит примерно так:
источник
a.inject({})
однострочника, который позволяет более гибко присваивать значения.h = {}
a.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
a.each_slice(2).to_h
Вы также можете просто преобразовать 2D-массив в хэш, используя:
источник
Резюме и TL; DR:
Этот ответ надеется быть исчерпывающим обобщением информации из других ответов.
Очень короткая версия, учитывая данные из вопроса и пару дополнений:
Обсуждение и подробности следуют далее.
Настройка: переменные
Чтобы показать данные, которые мы будем использовать заранее, я создам несколько переменных, представляющих различные возможности для данных. Они попадают в следующие категории:
Основываясь на том , что непосредственно в вопросе, как
a1
иa2
:(Примечание: я предполагаю, что
apple
иbanana
были предназначены для представления переменных. Как и другие, я буду использовать строки с этого момента, чтобы ввод и результаты могли совпадать.)Многозначные ключи и / или значения, например
a3
:В некоторых других ответах была представлена другая возможность (которую я здесь расширяю) - ключи и / или значения могут быть массивами сами по себе:
Несбалансированный массив, как
a4
:Для удобства я подумал, что добавлю один на случай, когда у нас может быть неполный ввод:
Теперь поработаем:
Начиная с изначально плоского массива
a1
:Некоторые предлагали использовать
#to_h
(который появился в Ruby 2.1.0 и может быть перенесен в более ранние версии). Для изначально плоского массива это не работает:Использование в
Hash::[]
сочетании с оператором splat :Итак, это решение для простого случая, представленного
a1
.С массивом массивов пар ключ / значение
a2
:С массивом
[key,value]
массивов типов можно пойти двумя путями.Во-первых,
Hash::[]
все еще работает (как и с*a1
):А потом тоже
#to_h
работает сейчас:Итак, два простых ответа для случая простого вложенного массива.
Это остается верным даже для подмассивов в качестве ключей или значений, например
a3
:Но у дурианов есть шипы (аномальные структуры создают проблемы):
Если мы получили несбалансированные входные данные, мы столкнемся с проблемами
#to_h
:Но
Hash::[]
все еще работает, просто устанавливаяnil
в качестве значения дляdurian
(и любого другого элемента массива в a4, который является просто массивом с 1 значением):Сглаживание - использование новых переменных
a5
иa6
Упомянутые несколько других ответов
flatten
с1
аргументом или без него , поэтому давайте создадим несколько новых переменных:Я решил использовать
a4
в качестве базовых данных из-за проблемы с балансом, которая у нас возниклаa4.to_h
. Я думаю звонюflatten
может быть одним из подходов, которые кто-то может использовать, чтобы попытаться решить эту проблему, что может выглядеть следующим образом.flatten
без аргументов (a5
):На наивном взгляде, это , кажется, работает - но у нас на неправильной ноге с бессемонными апельсинами, таким образом , также делает
3
на ключ иdurian
на значение .А это, как и с
a1
, просто не работает:Так что
a4.flatten
это бесполезно для нас, мы просто хотим использоватьHash[a4]
flatten(1)
Случай (a6
):Но как насчет частичного сглаживания? Стоит отметить, что вызов
Hash::[]
usingsplat
в частично сглаженном массиве (a6
) - это не то же самое, что вызовHash[a4]
:Предварительно сплющенный массив, все еще вложенный (альтернативный способ получения
a6
):Но что, если бы мы получили массив именно так? (То есть, по сравнению с
a1
нашими входными данными - только на этот раз некоторые данные могут быть массивами или другими объектами.) Мы видели, чтоHash[*a6]
это не работает, но что, если бы мы все еще хотели получить поведение, при котором последний элемент (важный! см. ниже) выступал в роли ключа дляnil
значения?В такой ситуации все еще есть способ сделать это, используя
Enumerable#each_slice
для возврата к парам ключ / значение как элементам внешнего массива:Обратите внимание, что это приводит к получению нового массива, который не " идентичен "
a4
, но имеет те же значения :Таким образом, мы снова можем использовать
Hash::[]
:Но есть проблема!
Важно отметить, что
each_slice(2)
решение возвращает все к здравому смыслу только в том случае, если в последнем ключе отсутствовало значение. Если позже мы добавим дополнительную пару ключ / значение:И два хэша, которые мы получим из этого, очень сильно отличаются:
(Примечание: я использую
awesome_print
'sap
только для того, чтобы упростить отображение структуры здесь; для этого нет никаких концептуальных требований.)Таким образом,
each_slice
решение проблемы небалансного плоского входа работает только в том случае, если несимметричный бит находится в самом конце.Take-сувенирной продукции:
[key, value]
элементов в виде пар (подмассив для каждого элемента внешнего массива).#to_h
илиHash::[]
оба будут работать.Hash::[]
комбинация со splat (*
) будет работать, пока входы сбалансированы .value
элемент будет единственным, который отсутствует.Боковое примечание: я публикую этот ответ, потому что чувствую, что есть ценность, которую нужно добавить - некоторые из существующих ответов содержат неверную информацию, и ни один (который я прочитал) не дал столь полного ответа, как я пытаюсь сделать здесь. Я надеюсь, что это поможет. Тем не менее, я благодарю тех, кто был до меня, некоторые из которых вдохновили меня на части этого ответа.
источник
Добавляем к ответу, но используя анонимные массивы и аннотируя:
Разбирая этот ответ, начиная с внутренней стороны:
"a,b,c,d"
на самом деле строка.split
по запятым в массив.zip
это вместе со следующим массивом.[1,2,3,4]
это фактический массив.Промежуточный результат:
Flatten затем преобразует это в:
а потом:
*["a",1,"b",2,"c",3,"d",4]
разворачивает это в"a",1,"b",2,"c",3,"d",4
которые мы можем использовать в качестве аргументов
Hash[]
метода:что дает:
источник
*
) и flatten:Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]
=>{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
. Более подробно в ответе я добавил.если у вас есть массив, который выглядит так -
и вы хотите, чтобы первые элементы каждого массива стали ключами для хеша, а остальные элементы стали массивами значений, тогда вы можете сделать что-то вроде этого:
источник
Не уверен, что это лучший способ, но это работает:
источник
Если числовые значения являются индексами seq, тогда у нас могут быть более простые способы ... Вот мой код, My Ruby немного ржавый
источник