将数组转换为哈希值

时间:2014-07-30 09:59:33

标签: ruby

我尝试学习地图和group_by,但这很难......

我的数组数组:

a = [ [1, 0, "a", "b"], [1, 1, "c", "d"], [2, 0, "e", "f"], [3, 1, "g", "h"] ]

预期结果:

b= {
     1=> {0=>["a", "b"], 1=>["c", "d"]} , 
     2=> {0=>["e", "f"]} , 
     3=> {1=>["g", "h"]}
   }

first value分组,第二个值可以是01

一个开始:

a.group_by{ |e| e.shift}.map { |k, v| {k=>v.group_by{ |e| e.shift}} }

=> [{1=>{0=>[["a", "b"]], 1=>[["c", "d"]]}},
   {2=>{0=>[["e", "f"]]}}, {3=>{1=>[["g", "h"]]}}]

我希望得到带有2个第一个值的“a”和“b”,这是我找到的唯一解决方案......(使用散列哈希)

4 个答案:

答案 0 :(得分:4)

不确定group_by是否是最简单的解决方案:

a = [ [1, 0, "a", "b"], [1, 1, "c", "d"], [2, 0, "e", "f"], [3, 1, "g", "h"] ]

result = a.inject({}) do |acc,(a,b,c,d)|
  acc[a] ||= {}
  acc[a][b] = [c,d]
  acc
end

puts result.inspect

将打印:

{1=>{0=>["a", "b"], 1=>["c", "d"]}, 2=>{0=>["e", "f"]}, 3=>{1=>["g", "h"]}}

另外,请避免直接更改您正在操作的项目(shift来电),您在代码中可能收到的馆藏可能不属于您的更改。

答案 1 :(得分:1)

如果你想要一个有点自定义的group_by我倾向于手动做。 group_by创建一个分组值数组,因此它会创建[["a", "b"]]而不是["a", "b"]。此外,您的代码具有破坏性,即它操纵a的值。如果您计划稍后以原始形式重新使用a,这只是一件坏事,但重要的是要注意。

正如我所提到的那样,您也可以只遍历a一次并构建所需的结构,而不是执行多个group_by

b = {}
a.each do |aa|
 (b[aa[0]] ||= {})[aa[1]] = aa[2..3]
end
b # => {1=>{0=>["a", "b"], 1=>["c", "d"]}, 2=>{0=>["e", "f"]}, 3=>{1=>["g", "h"]}}

使用(b[aa[0]] ||= {}),我们会检查哈希aa[0]中是否存在密钥b。如果它不存在,我们为该键分配一个空哈希({})。接下来,我们将aa(= aa[2..3])的最后两个元素插入到该哈希中,并将aa[1]作为键。

请注意,这不会考虑重复的主要/辅助密钥。也就是说,如果您有其他条目[1, 1, "x", "y"],它将覆盖 [1, 1, "c", "d"]条目,因为它们都有密钥11。你可以通过将值存储在一个数组中来解决这个问题,但是你也可以做一个双group_by。例如,对a的破坏性行为,处理“重复”:

# Added [1, 1, "x", "y"], removed some others
a = [ [1, 0, "a", "b"], [1, 1, "c", "d"], [1, 1, "x", "y"] ]
b = Hash[a.group_by(&:shift).map { |k, v| [k, v.group_by(&:shift) ] }]
#=> {1=>{0=>[["a", "b"]], 1=>[["c", "d"], ["x", "y"]]}}

答案 2 :(得分:1)

[[1, 0, "a", "b"], [1, 1, "c", "d"], [2, 0, "e", "f"], [3, 1, "g", "h"]].
    group_by{ |e| e.shift }.
    map{ |k, v| [k, v.inject({}) { |h, v| h[v.shift] = v; h }] }.
    to_h
#=> {1=>{0=>["a", "b"], 1=>["c", "d"]}, 2=>{0=>["e", "f"]}, 3=>{1=>["g", "h"]}}

答案 3 :(得分:1)

以下是如何使用两个Enumerable#group_by和一个Object#tap(非破坏性地)执行此操作的方法。 a(数组)的元素大小可能不同,每个元素的大小可能是两个或更大。

<强>代码

def convert(arr)
  h = arr.group_by(&:first)
  h.keys.each { |k| h[k] = h[k].group_by { |a| a[1] }
                               .tap { |g| g.keys.each { |j|
                                            g[j] = g[j].first[2..-1] } } }
  h
end

示例

a = [ [1, 0, "a", "b"], [1, 1, "c", "d"], [2, 0, "e", "f"], [3, 1, "g", "h"] ]
convert(a)
  #=> {1=>{0=>["a", "b"], 1=>["c", "d"]}, 2=>{0=>["e", "f"]}, 3=>{1=>["g", "h"]}}

<强>解释

h = a.group_by(&:first)
  #=> {1=>[[1, 0, "a", "b"], [1, 1, "c", "d"]],
  #    2=>[[2, 0, "e", "f"]],
  #    3=>[[3, 1, "g", "h"]]}

keys = h.keys
  #=> [1, 2, 3]

传递给块的键的第一个值将值1赋给块变量k。我们将h[1]设置为哈希f,计算如下。

f = h[k].group_by { |a| a[1] }
  #=> [[1, 0, "a", "b"], [1, 1, "c", "d"]].group_by { |a| a[1] }
  #=> {0=>[[1, 0, "a", "b"]], 1=>[[1, 1, "c", "d"]]}

我们需要对此哈希进行进一步处理,因此我们使用tap捕获它并将其分配给tap的块变量g(即{{1}最初将等于g以上)。修改后,块将返回f

我们有

g

因此g.keys #=> [0, 1] 是传递到0块的第一个值并分配给块变量each。然后我们计算:

j

同样,当g[j] = g[j].first[2..-1] #=> g[0] = [[1, 0, "a", "b"]].first[2..-1] #=> ["a", "b"] 的第二个密钥(g)传递到块中时,

1

人体工程学,

g[j] = g[j].first[2..-1]
  #=> g[1] = [[1, 1, "c", "d"]].first[2..-1]
  #=> ["c", "d"]

h[1] = g #=> {0=>["a", "b"], 1=>["c", "d"]} h[2]的计算方法类似,为我们提供了理想的结果。