11 ответов:
Примечание: для краткого и эффективного решения, пожалуйста, смотрите ответ Марка-Андре Лафорт ниже.
этот ответ был первоначально предложен в качестве альтернативы подходам с использованием 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}
просто использовать
Hash[*array_variable.flatten]например:
a1 = ['apple', 1, 'banana', 2] h1 = Hash[*a1.flatten(1)] puts "h1: #{h1.inspect}" a2 = [['apple', 1], ['banana', 2]] h2 = Hash[*a2.flatten(1)] puts "h2: #{h2.inspect}"используя
Array#flatten(1)ограничивает рекурсию такArrayключи и значения работают, как ожидалось.
лучший способ-использовать
Array#to_h:[ [:apple,1],[:banana,2] ].to_h #=> {:apple => 1, :banana => 2}Примечание: это было введено в Ruby 2.1.0. Для старых Руби, вы можете использовать мой
backportsкамень иrequire 'backports/2.1.0/array/to_h', или же использоватьHash[]:array = [ [:apple,1],[:banana,2] ] Hash[ array ] #= > {:apple => 1, :banana => 2}это доступно в Ruby 1.8.7 и выше. Если вы все еще используете Ruby 1.8.6, вы можете
require "backports/1.8.7/hash/constructor", но тогда вы могли бы также использоватьto_hbackport.наконец, в то время как многие решения использовать
flatten, это может создать проблемы со значениями, которые сами массивы.
обновление
Ruby 2.1.0 выпущен сегодня. И я иду с
Array#to_h(заметки и ruby-doc), что решает проблему преобразованияArrayдоHash.Ruby docs пример:
[[:foo, :bar], [1, 2]].to_h # => {:foo => :bar, 1 => 2}
Edit: видел ответы, опубликованные в то время как я писал, хэш[a.flatten] кажется, путь. Должно быть, пропустил этот бит в документации, когда я думал над ответом. Думал, что решения, которые я написал могут быть использованы в качестве альтернативы, если требуется.
вторая форма проще:
a = [[:apple, 1], [:banana, 2]] h = a.inject({}) { |r, i| r[i.first] = i.last; r }a = array, h = hash, r = return-value hash (тот, в котором мы накапливаемся), i = item in the array
самый аккуратный способ, которым я можно подумать, что делать первую форму это что-то вроде этого:
a = [:apple, 1, :banana, 2] h = {} a.each_slice(2) { |i| h[i.first] = i.last }
вы также можете просто преобразовать 2D массив в хэш с помощью:
1.9.3p362 :005 > a= [[1,2],[3,4]] => [[1, 2], [3, 4]] 1.9.3p362 :006 > h = Hash[a] => {1=>2, 3=>4}
добавление к ответу, но с использованием анонимных массивов и аннотирования:
Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]разбирая этот ответ на части, начиная изнутри:
"a,b,c,d"на самом деле строка.splitна запятых в массив.zipэто вместе со следующим массивом.[1,2,3,4]фактический массив.промежуточный результат:
[[a,1],[b,2],[c,3],[d,4]]сгладить затем преобразует это:
["a",1,"b",2,"c",3,"d",4]и затем:
*["a",1,"b",2,"c",3,"d",4]разворачивает это в"a",1,"b",2,"c",3,"d",4которые мы можем использовать в качестве аргументов
Hash[]способ:Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]что дает:
{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
резюме & 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::[]в сочетании с оператор splat тут: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]`.)на наивный взгляд, это, кажется, работает – но он получил нас на неправильной ноге с бессемянными апельсинами, таким образом, также делает
3a ключ иduriana стоимостью.и это, как с
a1, просто не работает:a5.to_h # => TypeError: wrong element type String at 0 (expected array)так
a4.flattenне полезно для нас, мы просто хотим использоватьHash[a4]The
flatten(1)case (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)решение только возвращает вещи к здравомыслию, если последние ключ был один отсутствует значение. Если мы позже добавили дополнительную пару ключ / значение: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_printsapпросто чтобы было легче показать структуру здесь; для этого нет концептуального требования.)так
each_sliceрешение для несбалансированного плоского входа работает только в том случае, если несбалансированный бит находится в самом конце.
Take-aways:
- всякий раз, когда это возможно, установите вход для этих вещей как
[key, value]пары (суб-массив для каждого элемента во внешнем массиве).- когда вы действительно можете это сделать, либо
#to_hилиHash::[]будет обе работы.- если вы не можете,
Hash::[]в сочетании с splat (*) будет работать, пока входы сбалансированы.- с несбалансированного и квартира массив в качестве входных данных, единственный способ это будет работать на всех разумно-если последние
valueitem-единственный, который отсутствует.
Side-note: я публикую этот ответ потому что я чувствую, что есть ценность, которую нужно добавить – некоторые из существующих ответов имеют неверную информацию, и ни один (который я читал) не дал такого полного ответа, как я пытаюсь сделать здесь. Я надеюсь, что это поможет. Тем не менее я благодарю тех, кто пришел до меня, некоторые из которых вдохновили меня на часть этого ответа.
Если у вас есть массив, который выглядит вот так -
data = [["foo",1,2,3,4],["bar",1,2],["foobar",1,"*",3,5,:foo]]и вы хотите, чтобы первые элементы каждого массива стали ключами для хэша, а остальные элементы стали массивами значений, тогда вы можете сделать что-то вроде этого -
data_hash = Hash[data.map { |key| [key.shift, key] }] #=>{"foo"=>[1, 2, 3, 4], "bar"=>[1, 2], "foobar"=>[1, "*", 3, 5, :foo]}
Не уверен, что это лучший способ, но это работает:
a = ["apple", 1, "banana", 2] m1 = {} for x in (a.length / 2).times m1[a[x*2]] = a[x*2 + 1] end b = [["apple", 1], ["banana", 2]] m2 = {} for x,y in b m2[x] = y end
если числовые значения являются индексами seq, то мы могли бы иметь более простые способы... Вот мой код представления, мой Рубин немного ржавый
input = ["cat", 1, "dog", 2, "wombat", 3] hash = Hash.new input.each_with_index {|item, index| if (index%2 == 0) hash[item] = input[index+1] } hash #=> {"cat"=>1, "wombat"=>3, "dog"=>2}
Comments