基于Ruby的类似位置合并数组的哈希值

时间:2018-04-29 22:39:09

标签: arrays ruby hash merge

我有以下两个哈希值,数组为值。

a = {
  "Us" => [["1", ["1", "2"]], ["2", ["1"]]],
  "Pa" => [["1", ["1", "3", "5"]], ["4", ["7"]]]
}
b = {
  "Us" => [["1", ["F", "O"]], ["2", ["N"]]],
  "Pa" => [["1", ["S", "D", "H"]], ["4", ["K"]]]
}

我正在尝试合并哈希以得到一个像这样的决赛:

c = {
  "Us" => [["1", ["1|F", "2|O"]], ["2", ["1|N"]]],
  "Pa" => [["1", ["1|S", "3|D", "5|H"]], ["4", ["7|K"]]]
}

我在merge找到了以下代码,并尝试将其应用于我的问题,但是我收到了错误:

a.merge(b) {|key, a_val, b_val| a_val.merge b_val }
# >> NoMethodError: undefined method `merge' for [["1", ["1", "2"]], ["2", ["1"]]]:Array

我在a + b时遇到错误:

a + b
# >> NoMethodError: undefined method `+' for #<Hash:0x0000060078e460>

&LT;&LT;&LT;&LT;更新&gt;&gt;&gt;&gt;

感谢Cary和tadman。在原始问题之外,我显示了我的输入文件和我尝试获取的输出。我展示的是为了让你知道为什么 我以这种方式生成了2个哈希。在输出中,我创建了块,其中父亲是第1列的唯一值,在子节点下面(第2列中与col 1相关的唯一值)。 第3列是属于col2中的值的子子,第4列是与col3相关的文本内容。

可能哈希“c”从一开始就更容易生成。

这是我的输入文件

Main,Stage1,Stage2,Description
Us,1,1,F
Us,1,2,O
Us,2,1,N
Pa,1,1,S
Pa,1,3,D
Pa,1,5,H
Pa,4,7,K

这是我几乎得到的输出。

Main..Stage1..Stage2..Description       
Us      
......1
..............1.......F
..............2.......O
......2 
..............1.......N
Pa
......1
..............1.......S
..............3.......D
..............5.......H
......4
..............7.......K

然后我就能够创建这个代码了,但是像tadman所说的那样,我需要重新排序,这样才能让事情变得更容易,因为 我用了4个哈希。在我创建哈希“a”和“b”后,我被卡住了,因为我需要一个唯一的哈希来迭代并且能够在上面显示的输出结构中打印。

发布问题之前

我的代码

X = Hash.new{|hsh,key| hsh[key] = [] }
Y = Hash.new{|hsh,key| hsh[key] = [] }
a = Hash.new{|hsh,key| hsh[key] = [] }
b = Hash.new{|hsh,key| hsh[key] = [] }

File.foreach('file.txt').with_index do
    |line, line_num|

    if line_num > 0
        r = line.split(",")

        X[r[0] + "°" + r[1]].push r[2]
        Y[r[0] + "°" + r[1]].push r[3].strip
    end
end

X.each{ |k,v|
    lbs = k.split("°")
    a[lbs[0]].push [ lbs[1], v] #Here I generate hash a
}

Y.each{ |k,v|
    lbs = k.split("°")
    b[lbs[0]].push [ lbs[1], v] #Here I generate hash b
}

3 个答案:

答案 0 :(得分:3)

由于所有复杂的嵌套,你在这里需要一些工作来解决。如果你做了一些工作来重新排序数据的存储方式,这将会容易得多。

但你可以这样做:

a={"Us"=>[["1", ["1", "2"]], ["2", ["1"]]], "Pa"=>[["1", ["1", "3", "5"]], ["4", ["7"]]]}
b={"Us"=>[["1", ["F", "O"]], ["2", ["N"]]], "Pa"=>[["1", ["S", "D", "H"]], ["4", ["K"]]]}

c = a.keys.map do |k|
  ah = a[k].to_h
  bh = b[k].to_h

  [
    k,
    ah.keys.map do |ka|
      [
        ka,
        ah[ka].zip(bh[ka]).map do |pair|
          pair.join('|')
        end
      ]
    end
  ]
end.to_h

# => {"Us"=>[["1", ["1|F", "2|O"]], ["2", ["1|N"]]], "Pa"=>[["1", ["1|S", "3|D", "5|H"]], ["4", ["7|K"]]]}

这里的关键是严格使用map将每个图层转换为zip以将两个数组“拉链”成对,然后将其与join组合成所需的字符串目标。最后使用to_h投回一个哈希,你得到你想要的东西。

对于散列的每个子集都有一个中间转换,以处理无序情况,其中可以在不同的序列中指定明显的“键”。

您要做的是将其包装在一个带有描述性名称的方法中:

def hash_compactor(a,b)
  # ... (code) ...
end

这将有助于保持模块化。通常我尝试创建处理 N 参数的解决方案,方法是将其定义为:

def hash_compactor(*input)
  # ...
end

其中input是您给出的表格中的各种集合的数组。结果代码令人惊讶地复杂得多。

请注意,这会对输入完全匹配做出很多假设,如果情况并非如此,则会爆炸。

答案 1 :(得分:2)

我建议你首先将其中一个哈希的值转换为哈希值,我将解释。假设我们创建了一个 new b

newbie = b.transform_values(&:to_h)
  #=> {"Us"=>{"1"=>["F", "O"], "2"=>["N"]},
  #    "Pa"=>{"1"=>["S", "D", "H"], "4"=>["K"]}}

我们现在可以使用anewbie来生成所需的返回值。

a.each_with_object({}) do |(k,v),h|
  h[k] = v.map do |first, arr|
    [first, arr.zip(newbie[k][first]).map { |pair| pair.join('|') }]
  end
end
  #=> {"Us"=>[["1", ["1|F", "2|O"]], ["2", ["1|N"]]],
  #    "Pa"=>[["1", ["1|S", "3|D", "5|H"]], ["4", ["7|K"]]]}

如果a可以变异,那就稍微容易了。

a.each do |k,v|
  v.map! do |first, arr|
    [first, arr.zip(newbie[k][first]).map { |pair| pair.join('|') }]
  end
end

方法Hash#trasform_values在Ruby v2.4中首次亮相。要支持旧版本,请按以下方式计算newbie

newbie = b.each_with_object({}) {|(k,v),h| h[k] = v.to_h }

答案 2 :(得分:1)

在此解决方案中,我们将保留原始结构。

我已经按照你的第一次尝试而不是:

a.merge(b) {|key, a_val, b_val| a_val.merge b_val }

考虑使用新的自定义合并功能,如:

c = a.merge(b) {|key, a_val, b_val| myMergeArray(a_val, b_val) }

然后新的合并函数是一个简单的递归函数:

def myMergeArray(a,b,sep = '|')
 c = a
 c.each_with_index { |e, i|
    if c[i].is_a? Array 
        c[i] = myMergeArray(c[i], b[i], sep)
    else
        c[i] = c[i] + sep + b[i] if c[i] != b[i]
    end
        }
 return c
end

我假设在相同元素的情况下,只需保存一个,例如“Y”和“Y”仅产生“Y”而不是“Y | Y”

干杯!