Массив для Hash Ruby

Хорошо, так вот, сделка, я уже давно искал поисковые запросы, чтобы найти решение этой проблемы, и, хотя их много, они, похоже, не выполняют работу, которую я ищу.

В основном у меня есть массив, структурированный таким образом

["item 1", "item 2", "item 3", "item 4"] 

Я хочу преобразовать это в хеш, чтобы он выглядел как

{ "item 1" => "item 2", "item 3" => "item 4" }

то есть. элементы, находящиеся на "четных" индексах, являются ключами, а значениями "нечетных" индексов являются значения.

Любые идеи, как сделать это чисто? Я полагаю, что метод грубой силы должен просто вытащить все четные индексы в отдельный массив, а затем объединить их, чтобы добавить значения.

Ответ 1

a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # => { "item 1" => "item 2", "item 3" => "item 4" }

Вот оно. * называется оператором сплат.

Одно предостережение в отношении @Mike Lewis (в комментариях): "Будьте очень осторожны с этим. Ruby расширяет знаки в стеке. Если вы делаете это с большим набором данных, ожидайте, что ваш стек будет взорван".

Таким образом, для большинства общих случаев использования этот метод хорош, но используйте другой метод, если вы хотите выполнить преобразование для большого количества данных. Например, @Łukasz Niemier (также в комментариях) предлагает этот метод для больших наборов данных:

h = Hash[a.each_slice(2).to_a]

Ответ 2

Ruby 2.1.0 представил метод to_h в массиве, который делает то, что вам нужно, если ваш исходный массив состоит из массивов пар ключ-значение: http://www.ruby-doc.org/core-2.1.0/Array.html#method-i-to_h.

[[:foo, :bar], [1, 2]].to_h
# => {:foo => :bar, 1 => 2}

Ответ 3

Просто используйте Hash.[] со значениями в массиве. Например:

arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 4}

Ответ 4

Или, если у вас есть массив массивов [key, value], вы можете сделать:

[[1, 2], [3, 4]].inject({}) do |r, s|
  r.merge!({s[0] => s[1]})
end # => { 1 => 2, 3 => 4 }

Ответ 5

Это то, что я искал при поиске по этой теме:

[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v } => {:a=>1, :b=>2}

Ответ 6

Enumerator включает Enumerable. Поскольку 2.1, Enumerable также имеет метод #to_h. Вот почему мы можем написать: -

a = ["item 1", "item 2", "item 3", "item 4"]
a.each_slice(2).to_h
# => {"item 1"=>"item 2", "item 3"=>"item 4"}

Потому что #each_slice без блока дает нам Enumerator, и в соответствии с приведенным выше объяснением мы можем вызвать метод #to_h для объекта Enumerator.

Ответ 7

Вы можете попробовать как это, для одиночного массива

irb(main):019:0> a = ["item 1", "item 2", "item 3", "item 4"]
  => ["item 1", "item 2", "item 3", "item 4"]
irb(main):020:0> Hash[*a]
  => {"item 1"=>"item 2", "item 3"=>"item 4"}

для массива массива

irb(main):022:0> a = [[1, 2], [3, 4]]
  => [[1, 2], [3, 4]]
irb(main):023:0> Hash[*a.flatten]
  => {1=>2, 3=>4}

Ответ 8

a = ["item 1", "item 2", "item 3", "item 4"]
Hash[ a.each_slice( 2 ).map { |e| e } ]

или, если вы ненавидите Hash[ ... ]:

a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end

или, если вы ленивый поклонник сломанного функционального программирования:

h = a.lazy.each_slice( 2 ).tap { |a|
  break Hash.new { |h, k| h[k] = a.find { |e, _| e == k }[1] }
}
#=> {}
h["item 1"] #=> "item 2"
h["item 3"] #=> "item 4"

Ответ 9

Все ответы предполагают, что начальный массив уникален. В OP не указано, как обрабатывать массивы с дублирующимися записями, что приводит к дублированию ключей.

Давайте посмотрим на:

a = ["item 1", "item 2", "item 3", "item 4", "item 1", "item 5"]

Вы потеряете пару item 1 => item 2, так как она переопределена bij item 1 => item 5:

Hash[*a]
=> {"item 1"=>"item 5", "item 3"=>"item 4"}

Все методы, включая reduce(&:merge!), приводят к одинаковому удалению.

Вполне возможно, что это именно то, что вы ожидаете. Но в других случаях вы, вероятно, захотите получить результат с Array вместо значения:

{"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

Наивным способом было бы создать вспомогательную переменную, хэш, который имеет значение по умолчанию, и затем заполнить его в цикле:

result = Hash.new {|hash, k| hash[k] = [] } # Hash.new with block defines unique defaults.
a.each_slice(2) {|k,v| result[k] << v }
a
=> {"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

Возможно, можно использовать assoc и reduce для выполнения вышеупомянутых действий в одной строке, но это становится гораздо труднее рассуждать и читать.