2014-10-02 6 views
0

У меня есть два массива:Массивы в хэши

a=["joe","mark","mark","wilson","joe"] 
b=[1,2,2,3,4] 

мне нужно хэш:

h={"joe"=>[1,4],"mark"=>[2,2],"wilson"=>[3]} 

Основной проблемой является ключи повторить и может иметь несколько значений. Я пробовал zip, inject и map, но я не могу прийти даже удаленно близко к тому, что мне нужно. Мне нужно использовать Ruby.

+0

На каком основании «joe» присвоено 1,4, отметка 2, 2 и т. Д.? – daremkd

+0

@daremkd Это набор данных, который у меня есть, к сожалению, я не участвовал в создании данных.Но как это будет иметь отношение к проблеме здесь? –

+0

как вы строите хэш, не зная, как связаны два массива. На каком основании вы бы построили значения для ключа «joe»? – usha

ответ

2

Я хотел бы использовать:

a=["joe","mark","mark","wilson","joe"] 
b=[1,2,2,3,4] 

a.zip(b).group_by{ |i,j| i }.map{ |k, v| [k, v.map(&:last)] }.to_h 
# => {"joe"=>[1, 4], "mark"=>[2, 2], "wilson"=>[3]} 

Если вы не на Ruby, 2.1+, вы не будете иметь Array#to_h так, вместо этого, вы можете сделать:

Hash[a.zip(b).group_by{ |i,j| i }.map{ |k, v| [k, v.map(&:last)] }] 
# => {"joe"=>[1, 4], "mark"=>[2, 2], "wilson"=>[3]} 

Вот что это делают в некоторых промежуточных шагов:

a.zip(b) # => [["joe", 1], ["mark", 2], ["mark", 2], ["wilson", 3], ["joe", 4]] 
a.zip(b).group_by{ |i,j| i } # => {"joe"=>[["joe", 1], ["joe", 4]], "mark"=>[["mark", 2], ["mark", 2]], "wilson"=>[["wilson", 3]]} 
a.zip(b).group_by{ |i,j| i }.map{ |k, v| [k, v.map(&:last)] } # => [["joe", [1, 4]], ["mark", [2, 2]], ["wilson", [3]]] 

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

+0

: Спасибо, очень ясный ответ. Я просто изучаю Ruby. Кажется, это было простое решение. –

+1

Это довольно легко, как только вы поймете шаблон. Класс Enumerable Ruby имеет ряд очень мощных методов, которые полезны для реорганизации и модификации массивов и хэшей, а также всего, что реализует перечислитель. –

0

Существует несколько способов сделать это. Вот два.

# 1

a = ["joe","mark","mark","wilson","joe"] 
b = [1,2,2,3,4] 

Hash[a.zip(b) 
     .group_by(&:first) 
     .values 
     .map { |arr| [arr.first.first, arr.map(&:last)] }] 
    #=> { "joe"=>[1, 4], "mark"=>[2, 2], "wilson"=>[3] } 

С Рубин 2.1:

a.zip(b) 
.group_by(&:first) 
.values 
.map { |arr| [arr.first.first, arr.map(&:last)] } 
.to_h 

# 2

a.zip(b) 
.each_with_object({}) { |(name,val),h| 
    h.update({name=>[val]}) { |_,ov,nv| ov+nv } } 
    => {"joe"=>[1, 4], "mark"=>[2, 2], "wilson"=>[3]} 

Этот второй подход использует форму Hash#update (а.к.а. merge!), который принимает блок.

+0

Im ищет, чтобы хранить соответствующие значения в виде массива (не как сумму). Думаю, я мог бы это изменить ваше решение, хотя –

+0

Спасибо, это было незначительное исправление. –

0

Если то, что вы пытаетесь достичь, это иметь несколько значений для (же) ключа, то хранящего значение как двойной массив и немного модификаций могут работать:

class Hash 
    def add_key_value(key, value) 
    if self.has_key?(key) 
     self[key] << value 
    else 
     self[key] = [value] 
    end 
    end 
end 

h = Hash.new() 
h.add_key_value('joe', [1,2]) 
p h #=> {"joe"=>[[1, 2]]} 
h.add_key_value('joe', [3]) 
p h #=> {"joe"=>[[1, 2], [3]]} 
h.add_key_value('mark', [4,5]) 
p h #=> {"joe"=>[[1, 2], [3]], "mark"=>[[4, 5]]} 
h.add_key_value('mark', [1,2]) 
p h #=> {"joe"=>[[1, 2], [3]], "mark"=>[[4, 5], [1, 2]]} 
0

Есть 2 варианта, самый быстрый и самый короткий:

def fastest(a, b) 
    result = {} 
    i = 0 

    a.each do |elem| 
    result[elem] ? result[elem] << b[i] : result[elem] = [b[i]] 
    i += 1 
    end 

    result 
end 

def shortest(a, b) 
    a.zip(b).group_by{ |i,j| i }.map{ |k, v| [k, v.map(&:last)] }.to_h 
end 

и результаты тестов:

require 'benchmark/ips' 

Benchmark.ips do |x| 
    %w(fastest shortest).each do |method| 
    x.report(method) { send method, ["joe","mark","mark","wilson","joe"], [1,2,2,3,4] } 
    end 
end 

fastest 185435.9 (±19.9%) i/s -  892388 in 5.017412s 
shortest 93222.0 (±20.1%) i/s -  445398 in 5.011817s 
+1

В качестве варианта 'fastest', рассмотрим:' a.each_with_index.with_object ({}) {| (name, i), h | (h [имя] || = []) << b [i]} '. –

0

Th е вопросы это были дан ответ хорошо, но вот мой код гольф попытка :)

a=["joe","mark","mark","wilson","joe"] 
b=[1,2,2,3,4] 

a.zip(b).reduce({}) {|o,(k,v)| (o[k] ||= []) << v; o} 

Основная идея довольно близко к Оловянному Человеку, а не несколько промежуточных шагов, он просто принимает сжатые массивы и уменьшает их в целевой формы.

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