阵列到哈希

时间:2014-10-02 20:07:50

标签: ruby arrays hash

我有两个数组:

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

我需要哈希:

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

主要问题是键重复并且可以有多个值。我尝试过zipinjectmap,但我无法接近我需要的地方。我需要使用Ruby。

5 个答案:

答案 0 :(得分: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,它收集符合给定条件的所有元素,在本例中,所有元素元素都与给定名称匹配。一旦这些被分组,那么它只是清理生成的数组并将其转换为哈希的情况。

答案 1 :(得分: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] }

使用Ruby 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(a.k.a。merge!)的形式进行阻止。

答案 2 :(得分: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]]}

答案 3 :(得分:0)

有两种变体,最快和最短:

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

答案 4 :(得分:0)

问题得到了很好的回答,但这是我的代码高尔夫尝试:)

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

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

一般的想法非常接近Tin Man's,但不是几个中间步骤,它只需要拉链阵列并将它们直接缩小为目标形式。