Сводка & TL; DR:
Этот ответ является исчерпывающим обобщением информации из других ответов.
Очень короткая версия, учитывая данные из вопроса плюс пара дополнений:
flat_array = [ apple, 1, banana, 2 ] # count=4
nested_array = [ [apple, 1], [banana, 2] ] # count=2 of count=2 k,v arrays
incomplete_f = [ apple, 1, banana ] # count=3 - missing last value
incomplete_n = [ [apple, 1], [banana ] ] # count=2 of either k or k,v arrays
# there's one option for flat_array:
h1 = Hash[*flat_array] # => {apple=>1, banana=>2}
# two options for nested_array:
h2a = nested_array.to_h # since ruby 2.1.0 => {apple=>1, banana=>2}
h2b = Hash[nested_array] # => {apple=>1, banana=>2}
# ok if *only* the last value is missing:
h3 = Hash[incomplete_f.each_slice(2).to_a] # => {apple=>1, banana=>nil}
# always ok for k without v in nested array:
h4 = Hash[incomplete_n] # or .to_h => {apple=>1, banana=>nil}
# as one might expect:
h1 == h2a # => true
h1 == h2b # => true
h1 == h3 # => false
h3 == h4 # => true
Обсуждение и подробности следуют.
Настройка: переменные
Чтобы показать данные, которые мы будем использовать заранее, я создам некоторые переменные для представления различных возможностей для данных. Они вписываются в следующие категории:
На основании того, что было непосредственно в вопросе, как a1
и a2
:
(Примечание: я предполагаю, что apple
и banana
предназначались для представления переменных. Как и другие, я буду использовать здесь строки, чтобы ввод и результаты могли совпадать.)
a1 = [ 'apple', 1 , 'banana', 2 ] # flat input
a2 = [ ['apple', 1], ['banana', 2] ] # key/value paired input
Многозначные ключи и / или значения, например a3
:
В некоторых других ответах была представлена другая возможность (которую я здесь расширяю) - ключи и / или значения могут быть массивами сами по себе:
a3 = [ [ 'apple', 1 ],
[ 'banana', 2 ],
[ ['orange','seedless'], 3 ],
[ 'pear', [4, 5] ],
]
Несбалансированный массив, как a4
:
Для удобства я подумал, что добавлю один для случая, когда у нас может быть неполный ввод:
a4 = [ [ 'apple', 1],
[ 'banana', 2],
[ ['orange','seedless'], 3],
[ 'durian' ], # a spiky fruit pricks us: no value!
]
Теперь на работу:
Начиная с изначально плоского массива, a1
:
Некоторые предлагают использовать #to_h
(который появился в Ruby 2.1.0 и может быть backported для более ранних версий). Для изначально плоского массива это не работает:
a1.to_h # => TypeError: wrong element type String at 0 (expected array)
Использование Hash::[]
в сочетании с оператором делает:
Hash[*a1] # => {"apple"=>1, "banana"=>2}
Так что это решение для простого случая, представленного a1
.
С массивом парных пар ключ / значение, a2
:
С массивом массивов типа [key,value]
можно пойти двумя путями.
Во-первых, Hash::[]
все еще работает (как это было с *a1
):
Hash[a2] # => {"apple"=>1, "banana"=>2}
А потом еще #to_h
работает сейчас:
a2.to_h # => {"apple"=>1, "banana"=>2}
Итак, два простых ответа для простого случая вложенного массива.
Это остается верным даже при использовании подмассивов в качестве ключей или значений, как с a3
:
Hash[a3] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}
a3.to_h # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}
Но у дурианов есть шипы (аномальные структуры создают проблемы):
Если мы получили несбалансированные входные данные, у нас возникнут проблемы с #to_h
:
a4.to_h # => ArgumentError: wrong array length at 3 (expected 2, was 1)
Но Hash::[]
все еще работает, просто устанавливая nil
в качестве значения для durian
(и любого другого элемента массива в a4, который является просто массивом с 1 значением):
Hash[a4] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
Сглаживание - с использованием новых переменных a5
и a6
Несколько других упомянутых ответов flatten
, с аргументом 1
или без него, поэтому давайте создадим несколько новых переменных:
a5 = a4.flatten
# => ["apple", 1, "banana", 2, "orange", "seedless" , 3, "durian"]
a6 = a4.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian"]
Я решил использовать a4
в качестве базовых данных из-за проблемы с балансом, которая возникла у нас с a4.to_h
. Я полагаю, что вызов flatten
может быть одним из подходов, который кто-то может использовать для решения этой проблемы, который может выглядеть следующим образом.
flatten
без аргументов (a5
):
Hash[*a5] # => {"apple"=>1, "banana"=>2, "orange"=>"seedless", 3=>"durian"}
# (This is the same as calling `Hash[*a4.flatten]`.)
Наивный взгляд, это похоже на работу - но это поставило нас на неверную ногу с апельсинами без косточек, таким образом также получая значение 3
key и durian
a .
А это, как и в случае a1
, просто не работает:
a5.to_h # => TypeError: wrong element type String at 0 (expected array)
Так что a4.flatten
нам не нужен, мы бы просто хотели использовать Hash[a4]
Корпус flatten(1)
(a6
):
Но как насчет только частичного сплющивания? Стоит отметить, что вызов Hash::[]
с использованием splat
для частично сплющенного массива (a6
) не совпадает с вызовом Hash[a4]
:
Hash[*a6] # => ArgumentError: odd number of arguments for Hash
Предварительно уплощенный массив, все еще вложенный (альтернативный способ получения a6
):
Но что, если бы именно так мы получили массив?
(То есть, по сравнению с a1
, это были наши входные данные - только в этот раз некоторые данные могут быть массивами или другими объектами.) Мы видели, что Hash[*a6]
не работает, но что, если мы все еще хотели чтобы получить поведение, когда последний элемент (важно! см. ниже) действовал как ключ для значения nil
?
В такой ситуации все еще есть способ сделать это, используя Enumerable#each_slice
, чтобы вернуться к ключу / значению пар в качестве элементов во внешнем массиве:
a7 = a6.each_slice(2).to_a
# => [["apple", 1], ["banana", 2], [["orange", "seedless"], 3], ["durian"]]
Обратите внимание, что в итоге получается новый массив, который не " идентичен " a4
, но имеет такие же значения :
a4.equal?(a7) # => false
a4 == a7 # => true
И, таким образом, мы снова можем использовать Hash::[]
:
Hash[a7] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
# or Hash[a6.each_slice(2).to_a]
Но есть проблема!
Важно отметить, что решение each_slice(2)
возвращает вещи в нормальное состояние, только если клавиша last была той, в которой отсутствовало значение. Если позже мы добавим дополнительную пару ключ / значение:
a4_plus = a4.dup # just to have a new-but-related variable name
a4_plus.push(['lychee', 4])
# => [["apple", 1],
# ["banana", 2],
# [["orange", "seedless"], 3], # multi-value key
# ["durian"], # missing value
# ["lychee", 4]] # new well-formed item
a6_plus = a4_plus.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian", "lychee", 4]
a7_plus = a6_plus.each_slice(2).to_a
# => [["apple", 1],
# ["banana", 2],
# [["orange", "seedless"], 3], # so far so good
# ["durian", "lychee"], # oops! key became value!
# [4]] # and we still have a key without a value
a4_plus == a7_plus # => false, unlike a4 == a7
И два хеша, которые мы получаем из этого, отличаются по важности:
ap Hash[a4_plus] # prints:
{
"apple" => 1,
"banana" => 2,
[ "orange", "seedless" ] => 3,
"durian" => nil, # correct
"lychee" => 4 # correct
}
ap Hash[a7_plus] # prints:
{
"apple" => 1,
"banana" => 2,
[ "orange", "seedless" ] => 3,
"durian" => "lychee", # incorrect
4 => nil # incorrect
}
(Примечание: я использую awesome_print
ap
просто для упрощения показа структуры здесь; для этого нет концептуальных требований.)
Таким образом, решение each_slice
для несимметричного плоского входа работает, только если несбалансированный бит находится в самом конце.
Take-сувенирной продукции:
- Когда это возможно, настройте ввод для этих вещей как
[key, value]
пары (подмассив для каждого элемента во внешнем массиве).
- Если вы действительно можете это сделать, то либо
#to_h
, либо Hash::[]
будут работать.
- Если вы не можете,
Hash::[]
в сочетании с символом сплат (*
) будет работать, , пока входы сбалансированы .
- При наличии несимметричного массива и flat в качестве входных данных единственный способ, которым это будет работать вообще, - это если last
value
пункт - единственный, который отсутствует.
Примечание: я публикую этот ответ, потому что я чувствую, что есть смысл добавить - некоторые из существующих ответов содержат неверную информацию, и ни один (который я прочитал) не дал такого полного ответа, как я пытаясь сделать здесь. Я надеюсь, что это полезно. Тем не менее, я благодарю тех, кто был до меня, некоторые из которых вдохновляли части этого ответа.