合并包含相同键值对的哈希
Merge hashes containing same key & value pair
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}]
这会起作用:
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 来确定集合的 "grouping key"。所有其他键的组合构成组合所依赖的分组键。
Enumerable#group_by
方法迭代一个集合并通过我们刚刚构建的分组键对其进行分组。
然后我们遍历分组集合并合并到一个新的 entity_ids 属性中,该属性由来自每个组的组合实体 ID 组成。
您可以按如下方式计算所需的结果。
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!
)的形式,它使用一个块来确定要合并的两个哈希中存在的键的值。有关块变量 k
、o
和 n
.
的说明,请参阅文档
如果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
的新值。由于 g
和 h
都有键 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 }}
最后一步是 return 这个散列的值。
h.values
#=> <as shown above>
请注意,某些块变量是下划线 (_
)。尽管它们是有效的局部变量,但它们通常用于指示它们不用于块计算。另一种约定是让未使用的块变量以下划线开头,例如 _key
。
你的问题有两个不同的挑战。
- 合并哈希。
- 仅当其他值不匹配时才合并。
问题 1:
要在合并时获得任何自定义行为,您可以将块传递给合并方法。在您的情况下,您想要组合实体 ID 的数组。此块采用键和左右值。在您的场景中,如果 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
}
}
}
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}]
这会起作用:
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 来确定集合的 "grouping key"。所有其他键的组合构成组合所依赖的分组键。
Enumerable#group_by
方法迭代一个集合并通过我们刚刚构建的分组键对其进行分组。
然后我们遍历分组集合并合并到一个新的 entity_ids 属性中,该属性由来自每个组的组合实体 ID 组成。
您可以按如下方式计算所需的结果。
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!
)的形式,它使用一个块来确定要合并的两个哈希中存在的键的值。有关块变量 k
、o
和 n
.
如果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
的新值。由于 g
和 h
都有键 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 }}
最后一步是 return 这个散列的值。
h.values
#=> <as shown above>
请注意,某些块变量是下划线 (_
)。尽管它们是有效的局部变量,但它们通常用于指示它们不用于块计算。另一种约定是让未使用的块变量以下划线开头,例如 _key
。
你的问题有两个不同的挑战。
- 合并哈希。
- 仅当其他值不匹配时才合并。
问题 1:
要在合并时获得任何自定义行为,您可以将块传递给合并方法。在您的情况下,您想要组合实体 ID 的数组。此块采用键和左右值。在您的场景中,如果 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
}
}
}