合并两个对象列表

发布于 2025-01-30 09:37:56 字数 2065 浏览 3 评论 0原文

我想合并两个嵌套对象,以便将第二个对象放在第一个上的顶部。这是针对kubectl config yaml,但它可能是任何具有list> list s,dict s and Simple Data类型的嵌套对象。例如:

# 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

I want to merge two nested objects so that the 2nd is layed out on top of the 1st. This is for a kubectl config yaml but it could be any nested object that has a combination of lists, dicts and simple data types. eg:

# 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

And the merged object would be:

containers:
  - name: cron
  - volumeMounts:
      - name: external-stroage-1
        mountPath: /mnt/stroage_1
        readOnly: true
      - name: internal-storage
        mountPath: /mnt/data

And here is what I have so far:

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

Most of this can be done with simple recursion. Its easy to identify where an item should be inserted in a dict since it's indexed by keys. But how do you identify if two items should be merged when you have a list of objects (if the list order isn't guaranteed to be the same)? AKA, how do I determine if an item in a list needs to be overwritten vs appended? As it stands now, all list items are appended which leads to a bad merge:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

瀞厅☆埖开 2025-02-06 09:37:56

这最终比我希望的要多一些,并且必须对YAML结构做出一个很大的假设,如果它们有一个dict> dict> dict列表中的项目,则如果它们有一个相同的项目名称键和匹配值(这在我们的kubectl yamls中很常见)。但是有了这个假设,这做出了工作:

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

This ended up being a bit more bloated than I was hoping, and has to make a big assumption about the yaml structure, which is to assume two dict items within a list are the same item if they have a name key and matching values (which is common in our kubectl yamls). But with that assumption, this did the job:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文