2010-06-21 5 views
0

Эй, ребята, у меня вопрос. Я хочу преобразовать массив. [[1, [-1, 1]], [1, [20, 8]], [1, [30, 4]], [1, [40, 2]], [1, [41, 6]], [1, [70, 243]]] в этот стиль [1,[[-1,1],[20,8],[30,4]...] или хэш [1 => ...] Как я могу сделать этот трюк? Спасибо !Вопрос о рубине!

ответ

6

Dict метод:

array = [your array] 
dict = {} 
array.each{ |a| (dict[a[0]] ||= []) << a[1] } 

Для внушения ясности Чака бы довести это до:

array = [your array] 
dict = Hash.new {|h,k| h[k] = []} 
array.each{ |a| dict[a[0]] << a[1] } 

Вы можете получить массив из этого в стиле вы хотите, выполнив:

new_arr = dict.select{|k,v| [k, v]} 

Что даст вам:

[[1, [[-1, 1], [20, 8], [30, 4], [40, 2], [41, 6], [70, 243]]]] 

Обратите внимание на дополнительный массив, потому что, если у вас были массивы, начинающиеся с 2, у вас будет другой набор в конце. Итак, new_arr[0] даст вам массив, который вы искали.

+3

Вы также можете легко получить эквивалентный массив таким образом. Создайте хэш, затем сделайте 'hash.map {| ключ, значения | [ключ, значения]} '. Кроме того, вы можете значительно упростить свой «каждый», создав хэш как «Hash.new {[]}» - тогда вам не нужно «|| = []» на каждой итерации, поскольку несуществующие ключи вернутся пустой массив. – Chuck

+0

'Hash.new {[]}' будет работать только с операторами присваивания типа '+ =', но с '<<' он не будет. Я отредактировал ответ соответственно. –

0

Вы можете сделать это:

array = [[1, [-1, 1]], [1, [20, 8]], [1, [30, 4]], 
     [1, [40, 2]], [1, [41, 6]], [1, [70, 243]]] 

# map into hashes that can be merged together 
hashes = array.map do |key,value| 
    { key => [value] } 
end 

# fold all hashes into one hash by merging the values by key 
merged_hash = hashes.inject({}) do |accu,value| 
    accu.merge!(value) {|_,o,n| o|n } 
end 

Это можно записать в виде не так легко однострочника:

array.map{|k,v|{k=>[v]}}.inject({}){|a,v|a.merge!(v){|_,o,n|o|n}} 
#==> {1=>[[-1, 1], [20, 8], [30, 4], [40, 2], [41, 6], [70, 243]]} 
+0

Это кажется более сложным, чем решение Мэтта. Почему вы предпочитаете этот способ сделать это? – Chuck

+0

@Chuck Решает проблему без загрязнения локального пространства имен переменных с помощью 'injection'. И я предпочитаю метод 'merge' над || = []. Конечно, это вопрос вкуса. Но я думаю, что решение Мэтта можно было бы переписать с помощью «инъекции», тем самым предотвратив загрязнение пространства имен с помощью temp vars. – hurikhan77

2

Если вы хотите как Hash, это просто

h = Hash[ary.group_by(&:first).map {|k, v| [k, v.map(&:last)] }] 

И если вы хотите, чтобы это было как Array, вы просто конвертировали Hash в a Array:

a = *h 
+0

+1 для очень элегантного решения, но я хотел бы упомянуть, что group_by может не работать со старыми API-интерфейсами ruby. – hurikhan77

+0

Я считаю, что '.group_by' - ruby ​​1.9, хотя может быть опция rails. –

+0

Код запускается без изменений в Ruby 1.8.7 и новее. Чтобы использовать его в Ruby 1.8.6, вам потребуется потребовать backports для 'Enumerable # group_by' и' Symbol # to_proc'. – Chuck

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