Arrays 使用Ruby合并基于相似位置的数组哈希

Arrays 使用Ruby合并基于相似位置的数组哈希,arrays,ruby,hash,merge,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"]]] } 我正在尝试合并散列以获得最终结果,

我有以下两个以数组作为值的散列

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>
这是我几乎得到的输出

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

我的代码在发布问题之前


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

但你可以做到这一点:

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
是您给定形式的各种集合的数组。结果代码出人意料地要复杂得多


请注意,这会对输入进行很多假设,假设输入完全匹配,如果不是这样,则会爆炸。

我建议您首先将其中一个哈希值转换为哈希值,我将对此进行解释。假设我们创建一个新的
b

我们现在可以使用
a
newbie
生成所需的返回值

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
该方法在Ruby v2.4中首次出现。为了支持旧版本,一个计算
newbie
,如下所示

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

在这个解决方案中,我们将保持原始结构

我遵循了您的第一次尝试,但没有:

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”


干杯

非常感谢塔德曼花时间来帮助和解释。我从这类解决方案中学习。非常感谢Cary,您的解决方案更短,我将测试这两种解决方案,以更好地理解所使用的逻辑。谢谢你的帮助和时间!我在原始帖子中展示了一个更新,输入和代码我用来获取散列a y b作为参考。感谢L.Jacob的解决方案。这三种解决方案都能满足我的需求。对于每个解决方案的逻辑,我都学到了。非常适合所有贡献者!不客气!很高兴知道这有帮助。我想我的解决方案比较简单,但对你来说,另一种结构可能更好。干杯
newbie = b.each_with_object({}) {|(k,v),h| h[k] = v.to_h }
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