【问题标题】:Ruby group array of arrays to merge them into one single array based on unique key in arraysRuby 数组数组,根据数组中的唯一键将它们合并为一个数组
【发布时间】:2021-05-16 19:24:57
【问题描述】:

我尝试了其他堆栈溢出请求中可用的其他选项,但无法获得我正在寻找的结果。

我有一个数组,如下所示:

输入:

[["ABC", "5A2", nil, "88474"],
 ["ABC", nil, "2", "88474"],
 ["ABC", nil, nil, "88474"],
 ["ABC", nil, nil, "88474"],
 ["Jack", "5A2", nil, "05195"],
 ["Jack", nil, "2", "05195"],
 ["Jack", nil, nil, "05195"],
 ["Jack", nil, nil, "05195"]]

数组索引 0 ABC 或 Jack 将用作 group_by 条件,我希望输出如下所示,其中所有 ABC 数组合并以显示值,如果任何数组在该索引位置保存值,则删除 nil:

输出:

[["ABC", "5A2", "2", "88474"],
["Jack", "5A2", "2", "05195"]]

它不会总是与输入相同的格式,其中第一个元素的第二个值后面跟着第二个元素的第三个值。它可以改变,但第二个值不会在多个元素中设置两次,对于相同的索引 0 具有不同的值,同样适用于第三个,或者如果我也添加第四个或第五个元素。

我使用过哈希数组但不是数组数组,所以不知道该怎么做。

【问题讨论】:

  • 是否总是第一个元素设置了第二个值,而总是第二个元素设置了第三个值?每种类型总是有四个元素吗?是否有可能在多个元素上设置第二个(或第三个)值,可能具有不同的值?
  • @spickermann 它不会总是与输入相同的格式,其中第一个元素的第二个值后面跟着第二个元素的第三个值。它可以改变,但第二个值不会在多个元素中设置两次,对于相同的索引 0 具有不同的值,同样适用于第三个,或者如果我也添加第四个或第五个元素。
  • 好的,谢谢@CarySwoveland
  • 您遇到问题的代码是什么?你的代码有什么问题?您收到错误消息吗?错误信息是什么?你得到的结果不是你期望的结果吗?你期望什么结果,为什么,你得到的结果是什么,两者有什么不同?您正在观察的行为不是期望的行为吗?期望的行为是什么,为什么,观察到的行为是什么,它们有何不同?请提供minimal reproducible example。请注意Stack Overflow不是写代码服务,你需要努力!

标签: arrays ruby multidimensional-array


【解决方案1】:

另一种选择是使用 Enumerable#group_byHash#values 根据第一个元素对数组进行分组,然后压缩成列并使用第一个元素压缩每一列(下面有更好的解释)。


这是最后一个班轮

arr.group_by(&:first).values.map { |first, *last| first.zip(*last).map { |a| a.compact.first } }

#=> [["ABC", "5A2", "2", "88474"], ["Jack", "5A2", "2", "05195"]]

工作原理

假设您有这三个数组,您可以将它们视为表格的行:

a = [1, nil, nil]
b = [1, 2, nil]
c = [nil, 2, 3]

您可以Array#zip获取列:

a.zip(b, c) #=> [[1, 1, nil], [nil, 2, 2], [nil, nil, 3]]

另一种获取列的方法是将数组包装在另一个数组中:

tmp = [a, b, c]

这显示了如何解包数组:

tmp.then { |first, *last| p first; p *last }
#=> [1, nil, nil]
#=> [1, 2, nil]
#=> [nil, 2, 3]

所以,压缩返回:

tmp.then { |first, *last| first.zip *last }
#=> [[1, 1, nil], [nil, 2, 2], [nil, nil, 3]]

现在,一旦我们有了列,我们需要压缩 (Array#compact) 每个列并获取第一个元素。

例如:

[1, 1, nil].compact.first #=> 1

tmp数组的情况下:

tmp.then { |first, *last| first.zip(*last).map { |column| column.compact.first } }
#=> [1, 2, 3]

把所有东西放在一起,你就得到了上面显示的最后一个衬里。

【讨论】:

    【解决方案2】:

    我不确定我是否理解这个问题,但我希望您可能正在寻找以下内容。

    arr = [
      ["ABC", "5A2", nil, "88474"],
      ["ABC", nil, "2", "88474"],
      ["ABC", nil, nil, "88474"],
      ["ABC", nil, nil, "88474"],
      ["Jack", "5A2", nil, "05195"],
      ["Jack", nil, "2", "05195"],
      ["Jack", nil, nil, "05195"],
      ["Jack", nil, nil, "05195"]
    ]
    
    arr.each_with_object({}) do |a, h|
      h.update(a.first=>a) { |_k, oa, na| oa.zip(na).map { |ov, nv| ov.nil? ? nv : ov } }
    end.values 
      #=> [["ABC", "5A2", "2", "88474"], ["Jack", "5A2", "2", "05195"]] 
    

    这使用了Hash#update(又名merge!)的形式,它使用了块

    { |_k, oa, na| oa.zip(na).map { |ov, nv| ov.nil? ? nv : ov } }
    

    确定正在构建的哈希 (h) 和正在合并的哈希 ({ a.first=>a }) 中存在的键的值。有关三个块变量 _koana 的说明,请参阅文档。1

    我可以通过使用 puts 语句加盐代码并使用缩写数组 arr 运行它来最好地解释计算是如何进行的。

    arr = [
      ["ABC", "5A2", nil, "88474"],
      ["ABC", nil, "2", "88474"],
      ["Jack", "5A2", nil, "05195"],
      ["Jack", nil, "2", "05195"],
    ]
    
    arr.each_with_object({}) do |a, h|
      puts "\na = #{a}"
      puts "h = #{h}"
      puts "a.first=>a = #{a.first}=>#{a}"
      h.update(a.first=>a) do |_k, oa, na|
        puts "_k = #{_k}"
        puts "oa = #{oa}"
        puts "na = #{na}"
        a = oa.zip(na)
        puts "oa.zip(na) = #{a}"
        a.map do |ov, nv|
          puts "  ov = #{ov}, nv = #{nv}"
          puts "  ov.nil? ? nv : ov = #{ov.nil? ? nv : ov}"
          ov.nil? ? nv : ov
        end
      end
    end.tap { |h| puts "h = #{h}" }.values 
      #=> [["ABC", "5A2", "2", "88474"], ["Jack", "5A2", "2", "05195"]]
    

    显示如下。

    a = ["ABC", "5A2", nil, "88474"]
    h = {}
    a.first=>a = ABC=>["ABC", "5A2", nil, "88474"]
    (The block is not called here because h does not have a key "ABC")
    
    a = ["ABC", nil, "2", "88474"]
    h = {"ABC"=>["ABC", "5A2", nil, "88474"]}
    a.first=>a = ABC=>["ABC", nil, "2", "88474"]
    _k = ABC
    oa = ["ABC", "5A2", nil, "88474"]
    na = ["ABC", nil, "2", "88474"]
    oa.zip(na) = [["ABC", "ABC"], ["5A2", nil], [nil, "2"], ["88474", "88474"]]
      ov = ABC, nv = ABC
      ov.nil? ? nv : ov = ABC
      ov = 5A2, nv = 
      ov.nil? ? nv : ov = 5A2
      ov = , nv = 2
      ov.nil? ? nv : ov = 2
      ov = 88474, nv = 88474
      ov.nil? ? nv : ov = 88474
    
    a = ["Jack", "5A2", nil, "05195"]
    h = {"ABC"=>["ABC", "5A2", "2", "88474"]}
    a.first=>a = Jack=>["Jack", "5A2", nil, "05195"]
    (The block is not called here because h does not have a key "Jack")
    
    a = ["Jack", nil, "2", "05195"]
    h = {"ABC"=>["ABC", "5A2", "2", "88474"], "Jack"=>["Jack", "5A2", nil, "05195"]}
    a.first=>a = Jack=>["Jack", nil, "2", "05195"]
    _k = Jack
    oa = ["Jack", "5A2", nil, "05195"]
    na = ["Jack", nil, "2", "05195"]
    oa.zip(na) = [["Jack", "Jack"], ["5A2", nil], [nil, "2"], ["05195", "05195"]]
      ov = Jack, nv = Jack
      ov.nil? ? nv : ov = Jack
      ov = 5A2, nv = 
      ov.nil? ? nv : ov = 5A2
      ov = , nv = 2
      ov.nil? ? nv : ov = 2
      ov = 05195, nv = 05195
      ov.nil? ? nv : ov = 05195
    
    h = {"ABC"=>["ABC", "5A2", "2", "88474"], "Jack"=>["Jack", "5A2", "2", "05195"]}
    

    1.按照惯例,我在公共密钥名称_k 的开头加上一个下划线,以向读者表明它不用于块计算。通常你会看到这样一个单独用下划线表示的块变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-03
      • 2021-07-16
      • 2020-10-11
      • 2020-03-16
      • 1970-01-01
      • 2019-05-29
      • 1970-01-01
      • 2018-02-20
      相关资源
      最近更新 更多