合并两个嵌套的对象列表
Merge two nested lists of objects
我想合并两个嵌套对象,使第二个对象位于第一个对象之上。这是针对 kubectl
配置 yaml,但它可以是具有 list
、dict
和简单数据类型组合的任何嵌套对象。例如:
# yaml 1:
containers:
- volumeMounts:
- name: external-stroage-1
mountPath: /mnt/stroage_1
readOnly: true
# Yaml 2
containers:
- name: cron
volumeMounts:
- name: internal-storage
mountPath: /mnt/data
合并后的对象将是:
containers:
- name: cron
- volumeMounts:
- name: external-stroage-1
mountPath: /mnt/stroage_1
readOnly: true
- name: internal-storage
mountPath: /mnt/data
这是我目前的情况:
def merge(object_one, object_two):
assert type(object_one) == type(object_two), "Mismatched types"
if isinstance(object_one, dict):
for key in object_two:
if key in object_one:
object_one[key] = merge(object_one[key], object_two[key])
else:
object_one[key] = object_two[key]
elif isinstance(object_one, list):
for item in object_two:
object_one.append(item) # <<<<< when should I overwrite instead of append?
else:
return object_two
return object_one
大部分都可以通过简单的递归来完成。很容易识别应该在 dict
中插入项目的位置,因为它是通过键索引的。但是,当您有 list
个对象时(如果不能保证列表顺序相同),您如何确定是否应该合并两个项目?又名,我如何确定列表中的项目是否需要覆盖或附加?就目前而言,所有列表项都会被附加,这会导致错误的合并:
containers:
- volumeMounts:
- name: external-stroage-1
mountPath: /mnt/stroage_1
readOnly: true
- name: external-stroage-2
mountPath: /mnt/stroage_2
- name: cron
volumeMounts: # This item should have been merged instead of being repeated
- name: internal-storage
mountPath: /mnt/data
这最终比我希望的更加臃肿,并且必须对 yaml 结构做出一个很大的假设,即假设列表中的两个 dict
项目是相同的项目,如果它们有一个 name
键和匹配值(这在我们的 kubectl yaml 中很常见)。但是根据这个假设,这完成了工作:
def merge(object_one, object_two):
"""
Recursively merge object_two over object one. This is not universal and makes some assumptions based on typical structure of kubectl/plato yamls
"""
assert type(object_one) == type(object_two), f"Mismatched types for object_one '{object_one}' and object_two {object_two}"
if isinstance(object_one, dict):
# If two dicts have a "name" field, and they match, its assumed they are the same field
if 'name' in object_one.keys() and 'name' in object_two.keys():
if object_one['name'] == object_two['name']:
return object_two
# Add missing keys to object_one
object_one = {**object_one, **{k: v for k, v in object_two.items() if k not in object_one}}
found = []
# If
for key in {x for x in object_one if x in object_two}:
if (tmp := merge(object_one[key], object_two[key])) is not None:
object_one[key] = tmp
found.append(True)
else:
found.append(False)
# If none is returned, the object is merged from 1 level up
if not all(found):
return None
elif isinstance(object_one, list):
# Compare every list element against the 2nd object, if no match is found, return None
for index_two in range(len(object_two)):
found = []
for index in range(len(object_one)):
try:
if tmp := merge(object_one[index], object_two[index_two]):
object_one[index] = tmp
found.append(True)
else:
found.append(False)
except Exception:
pass
# If None is returned, the object is merged from 1 level up
if not any(found):
object_one.append(object_two[index_two])
else:
# If both objects dont' match, return None which signals to the previous stack to merge one level up
if object_one == object_two:
return object_one
return
return object_one
我想合并两个嵌套对象,使第二个对象位于第一个对象之上。这是针对 kubectl
配置 yaml,但它可以是具有 list
、dict
和简单数据类型组合的任何嵌套对象。例如:
# yaml 1:
containers:
- volumeMounts:
- name: external-stroage-1
mountPath: /mnt/stroage_1
readOnly: true
# Yaml 2
containers:
- name: cron
volumeMounts:
- name: internal-storage
mountPath: /mnt/data
合并后的对象将是:
containers:
- name: cron
- volumeMounts:
- name: external-stroage-1
mountPath: /mnt/stroage_1
readOnly: true
- name: internal-storage
mountPath: /mnt/data
这是我目前的情况:
def merge(object_one, object_two):
assert type(object_one) == type(object_two), "Mismatched types"
if isinstance(object_one, dict):
for key in object_two:
if key in object_one:
object_one[key] = merge(object_one[key], object_two[key])
else:
object_one[key] = object_two[key]
elif isinstance(object_one, list):
for item in object_two:
object_one.append(item) # <<<<< when should I overwrite instead of append?
else:
return object_two
return object_one
大部分都可以通过简单的递归来完成。很容易识别应该在 dict
中插入项目的位置,因为它是通过键索引的。但是,当您有 list
个对象时(如果不能保证列表顺序相同),您如何确定是否应该合并两个项目?又名,我如何确定列表中的项目是否需要覆盖或附加?就目前而言,所有列表项都会被附加,这会导致错误的合并:
containers:
- volumeMounts:
- name: external-stroage-1
mountPath: /mnt/stroage_1
readOnly: true
- name: external-stroage-2
mountPath: /mnt/stroage_2
- name: cron
volumeMounts: # This item should have been merged instead of being repeated
- name: internal-storage
mountPath: /mnt/data
这最终比我希望的更加臃肿,并且必须对 yaml 结构做出一个很大的假设,即假设列表中的两个 dict
项目是相同的项目,如果它们有一个 name
键和匹配值(这在我们的 kubectl yaml 中很常见)。但是根据这个假设,这完成了工作:
def merge(object_one, object_two):
"""
Recursively merge object_two over object one. This is not universal and makes some assumptions based on typical structure of kubectl/plato yamls
"""
assert type(object_one) == type(object_two), f"Mismatched types for object_one '{object_one}' and object_two {object_two}"
if isinstance(object_one, dict):
# If two dicts have a "name" field, and they match, its assumed they are the same field
if 'name' in object_one.keys() and 'name' in object_two.keys():
if object_one['name'] == object_two['name']:
return object_two
# Add missing keys to object_one
object_one = {**object_one, **{k: v for k, v in object_two.items() if k not in object_one}}
found = []
# If
for key in {x for x in object_one if x in object_two}:
if (tmp := merge(object_one[key], object_two[key])) is not None:
object_one[key] = tmp
found.append(True)
else:
found.append(False)
# If none is returned, the object is merged from 1 level up
if not all(found):
return None
elif isinstance(object_one, list):
# Compare every list element against the 2nd object, if no match is found, return None
for index_two in range(len(object_two)):
found = []
for index in range(len(object_one)):
try:
if tmp := merge(object_one[index], object_two[index_two]):
object_one[index] = tmp
found.append(True)
else:
found.append(False)
except Exception:
pass
# If None is returned, the object is merged from 1 level up
if not any(found):
object_one.append(object_two[index_two])
else:
# If both objects dont' match, return None which signals to the previous stack to merge one level up
if object_one == object_two:
return object_one
return
return object_one