【问题标题】:Merge hashes containing same key & value pair合并包含相同键值对的哈希
【发布时间】:2018-06-10 11:44:41
【问题描述】:
arr1 = [
  {entity_type: "Mac", entity_ids: [3], cascade_id: 2, location_id: 1},
  {entity_type: "Mac", entity_ids: [2], cascade_id: 2, location_id: 1},
  {entity_type: "Mac", entity_ids: [9], cascade_id: 4, location_id: 1},
  {entity_type: "Mac", entity_ids: [10], cascade_id: 4, location_id: 1}
]

这是数据的一部分,是我经过一些逻辑迭代后得到的。 对于这个例子,我想要的输出是

[{entity_type: "Mac", entity_ids: [3,2], cascade_id: 2, location_id: 1}, {entity_type: "Mac", entity_ids: [9,10], cascade_id: 4, location_id: 1}]

如果一个或两个键值对相同​​,我想知道如何合并哈希,将其他键的值合并到一个数组中。

->这是另一个实例

arr2 = [
  {entity_type: "Sub", entity_ids: [7], mac_id: 5, cascade_id: 1, location_id: 1},
  {entity_type: "Sub", entity_ids: [10], mac_id: 5, cascade_id: 1, location_id: 1},
  {entity_type: "Sub", entity_ids: [4], mac_id: 2, cascade_id: 1, location_id: 1},
  {entity_type: "Sub", entity_ids: [11], mac_id: 7, cascade_id: 2, location_id: 2}
]

此实例所需的输出是

[{entity_type: "Sub", entity_ids: [7, 10], mac_id: 5, cascade_id: 1, location_id: 1}, {entity_type: "Sub", entity_ids: [4], mac_id: 2, cascade_id: 1, location_id: 1}, {entity_type: "Sub", entity_ids: [11], mac_id: 7, cascade_id: 2, location_id: 2}]

【问题讨论】:

  • 到目前为止,您尝试了什么,也添加了这一点。我也有一些问题,比如如果 location_id 不同怎么办?还是 entity_type 改变了。?也请分享其他场景。
  • 我一次处理每个 entity_type,对于这种情况,我只想知道如何合并具有相同值的第三个键值对。按照你的要求,我又添加了一个场景。
  • 如果 location_id 不同,那么我不应该合并。

标签: ruby-on-rails arrays ruby hash key-value


【解决方案1】:

您可以按如下方式计算所需的结果。

def doit(arr)
  arr.each_with_object({}) do |g,h|
    h.update(g.reject { |k,_| k==:entity_ids }=>g) do |_,o,n|
      o.merge(entity_ids: o[:entity_ids] + n[:entity_ids])
    end
  end.values
end

doit(arr1)
  #=> [{:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1},
  #    {:entity_type=>"Mac", :entity_ids=>[9, 10], :cascade_id=>4, :location_id=>1}]
doit(arr2)
  #=> [{:entity_type=>"Sub", :entity_ids=>[7, 10], :mac_id=>5, :cascade_id=>1,
  #     :location_id=>1},
  #    {:entity_type=>"Sub", :entity_ids=>[4], :mac_id=>2, :cascade_id=>1,
  #     :location_id=>1},
  #    {:entity_type=>"Sub", :entity_ids=>[11], :mac_id=>7, :cascade_id=>2,
  #     :location_id=>2}]

这使用Hash#update(又名merge!)的形式,它使用一个块来确定在被合并的两个散列中存在的键的值。有关块变量 kon 的说明,请参阅文档。

如果doit的参数是arr1,则步骤如下。

arr = arr1
e =  arr.each_with_object({})
  #=> #<Enumerator: [{:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2,
  #                   :location_id=>1},
  #                  {:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2,
  #                   :location_id=>1},
  #                  {:entity_type=>"Mac", :entity_ids=>[9], :cascade_id=>4,
  #                   :location_id=>1},
  #                  {:entity_type=>"Mac", :entity_ids=>[10], :cascade_id=>4,
  #                  :location_id=>1}
  #                 ]:each_with_object({})>

枚举器的第一个元素被传递给块,并且值被分配给块变量。

g, h = e.next
  #=> [{:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}, {}]
g #=> {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}
h #=> {}

计算要与h 合并的哈希的(唯一)键。

a = g.reject { |k,_| k==:entity_ids }
  #=> {:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}

执行更新操作。

h.update(a=>g)
  #=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
  #    {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}}

这是h 的新值。由于h(为空)没有密钥

{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}

该块未用于确定该键在合并哈希中的值。

现在生成枚举器e的下一个值,将其传递给块,为块变量赋值并执行块计算。

g, h = e.next
  #=> [{:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1},
  #    {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
  #     {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}}]
g #=> {:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1}
h #=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
  #    {:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1}}
a = g.reject { |k,_| k==:entity_ids }
  #=> {:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}
h.update(a=>g) do |_,o,n|
  puts "_=#{_}, o=#{o}, n=#{n}"
  o.merge(entity_ids: o[:entity_ids] + n[:entity_ids])
end
  #=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
  #    {:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1}}

这是h 的新值。由于gh 都具有a 键,因此会查询块以获取合并散列中该键的值(新h)。该块变量的值被打印出来。

_={:entity_type=>"Mac", :cascade_id=>2, :location_id=>1},
o={:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1},
n={:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1}

h[:entity_ids] 因此被替换为

o[:entity_ids] + o[:entity_ids]
  #=> [3] + [2] => [3, 2]

e 的剩余两个元素的计算是相似的,此时

h #=> {{ :entity_type=>"Mac", :cascade_id=>2, :location_id=>1 }=>
  #      { :entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1 },
  #    { :entity_type=>"Mac", :cascade_id=>4, :location_id=>1 }=>
  #      { :entity_type=>"Mac", :entity_ids=>[9, 10], :cascade_id=>4, :location_id=>1 }}

最后一步是返回这个哈希值。

h.values
  #=> <as shown above>

请注意,一些块变量是下划线 (_)。虽然它们是有效的局部变量,但它们通常用于指示它们不用于块计算。另一种约定是让未使用的块变量以下划线开头,例如_key

【讨论】:

  • 感谢您的解决方案,它就像一个魅力。解释也很好。真的很感激。
【解决方案2】:

这将起作用:

  def combine(collection)
    return [] if collection.empty?
    grouping_key = collection.first.keys - [:entity_ids]

    grouped_collection = collection.group_by do |element|
      grouping_key.map { |key| [key, element[key]] }.to_h
    end

    grouped_collection.map do |key, elements|
      key.merge(entity_ids: elements.map { |e| e[:entity_ids] }.flatten.uniq)
    end
  end

这是怎么回事:

首先,我们通过对第一个元素的键进行采样并删除 :entity_ids 来确定集合的“分组键”。组合的所有其他键构成组合所依赖的分组键。

Enumerable#group_by 方法遍历一个集合并按我们刚刚构建的分组键对其进行分组。

然后我们遍历分组集合并合并到一个新的 entity_ids 属性中,该属性由来自每个组的组合实体 ID 组成。

【讨论】:

    【解决方案3】:

    您的问题有两个不同的挑战。

    1. 合并哈希。
    2. 仅当其他值不匹配时才合并。

    问题一:

    要在合并时获得任何自定义行为,您可以将块传递给合并方法。在您的情况下,您想为实体 ID 组合数组。该块采用 key 和 left 和 right 值。在你的场景中,如果 key == :entity_ids,你想组合数组。

    one_entity.merge(other_entity){ |key, left, right|
      key== :entity_ids ? left + right : left
    }
    

    问题 2:

    要根据是否具有不同属性或相同属性来合并实体,我使用的是 group_by。这将给我一个哈希组合实体,可以合并到我可以映射和合并的数组中。

    actual.group_by {|x| [x[:entity_type], x[:mac_id], x[:location_id]]}
    

    将两者结合起来将为我提供完整的解决方案。如果需要,您可以在 group_by 块中添加更多属性。

    actual.group_by {|x| [x[:entity_type], x[:mac_id], x[:location_id]]}
          .map{|_, entities| entities.reduce({}) { |result, entity|
            result.merge(entity){|key, left, right|
              key== :entity_ids ? left + right : left
            }
          }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-02-24
      • 1970-01-01
      • 2020-11-16
      • 2018-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-12
      相关资源
      最近更新 更多