简体   繁体   English

获取与字典嵌套的字典的排列

[英]get permutations of dict nested with dicts

Really struggling to find a good solution for this problem.真的很难为这个问题找到一个好的解决方案。

assume I have the following dict:假设我有以下字典:

items = {
    "item_name": {
        "apple": {"color": ["red", "blue"]},
        "banana": {"color": ["yellow", "red"]}
        },
    "type": ["ripe", "not_ripe"]
}

and I want to generate the following output:我想生成以下 output:

[
    {"item_name": "apple", "colors": "red", "type": "ripe"},
    {"item_name": "apple", "colors": "blue", "type": "ripe"},
    {"item_name": "apple", "colors": "red", "type": "not_ripe"},
    {"item_name": "apple", "colors": "blue", "type": "not_ripe"},
    {"item_name": "banana", "colors": "yellow", "type": "ripe"},
    {"item_name": "banana", "colors": "red", "type": "ripe"},
    {"item_name": "banana", "colors": "yellow", "type": "not_ripe"},
    {"item_name": "banana", "colors": "red", "type": "not_ripe"},
]

So I want the cartesion product of item_name x color x type , but the possible values for color are different for each item_name (possibly overlapping), so it is not sufficient to just permute all the dict keys with itertools.product as described eg in this question所以我想要item_name x color x type的笛卡尔积,但是每个item_namecolor可能值都不同(可能重叠),因此仅使用itertools.product置换所有 dict 键是不够的,例如在此问题

This is different from the problems I have found on SO, as far as I see it there are question on resolving nested dicts, but only if the sub-elements are lists (not dicts, as it is the case in my question), for example here or here .这与我在 SO 上发现的问题不同,据我所知,存在解决嵌套字典的问题,但前提是子元素是列表(不是字典,因为我的问题就是这种情况),因为此处此处的示例。

I would really like to show anything that i have tried but so far I canceled each approach for being too complex.我真的很想展示我尝试过的任何东西,但到目前为止我因为太复杂而取消了每种方法。

Is there a straightforward to achieve the desired result?有没有直接达到预期结果的方法? It would also be an option to change the structure of items in a way that the logic is preserved.以保留逻辑的方式更改items的结构也是一种选择。

Use this.用这个。 I defined a recursive function to get all the combinations from a list of buckets:我定义了一个递归 function 以从存储桶列表中获取所有组合:

def bucket(lst, depth=0):
    for item in lst[0]:
        if len(lst) > 1:
            for result in bucket(lst[1:], depth+1):
                yield [item] + result
        else:
            yield [item]

items = {
    "item_name": {
        "apple": {"color": ["red", "blue"]},
        "banana": {"color": ["yellow", "red"]}
        },
    "type": ["ripe", "not_ripe"]
}

lst = []
for item, colours in items['item_name'].items():
    combinations = list(bucket([colours['color'], items['type']]))
    for colour, ripeness in combinations:
        lst.append({'item_name': item, 'color': colour, 'type': ripeness})

print(lst)

Output: Output:

[{'color': 'red', 'item_name': 'apple', 'type': 'ripe'},
 {'color': 'red', 'item_name': 'apple', 'type': 'not_ripe'},
 {'color': 'blue', 'item_name': 'apple', 'type': 'ripe'},
 {'color': 'blue', 'item_name': 'apple', 'type': 'not_ripe'},
 {'color': 'yellow', 'item_name': 'banana', 'type': 'ripe'},
 {'color': 'yellow', 'item_name': 'banana', 'type': 'not_ripe'},
 {'color': 'red', 'item_name': 'banana', 'type': 'ripe'},
 {'color': 'red', 'item_name': 'banana', 'type': 'not_ripe'}]

This will work这将起作用

If you want it to look really bad and unreadable you could do this:如果您希望它看起来非常糟糕且难以阅读,您可以这样做:

print([{"item_name": name, "colors": color, "type": typ} for name in list(items["item_name"].keys()) for typ in items["type"] for color in items["item_name"][name]["color"]])

Which is essentially the same as this:这与此基本相同:

def get_combos(items):
    names = list(items["item_name"].keys())
    types = items["type"]
    current = []
    for name in names:
        for typ in types:
            for color in items["item_name"][name]["color"]:
                current.append({"item_name": name, "colors": color, "type": typ})
    return current

print(get_combos(items))

OUTPUT OUTPUT

[
  {'item_name': 'apple', 'colors': 'red', 'type': 'ripe'}, 
  {'item_name': 'apple', 'colors': 'blue', 'type': 'ripe'}, 
  {'item_name': 'apple', 'colors': 'red', 'type': 'not_ripe'}, 
  {'item_name': 'apple', 'colors': 'blue', 'type': 'not_ripe'}, 
  {'item_name': 'banana', 'colors': 'yellow', 'type': 'ripe'}, 
  {'item_name': 'banana', 'colors': 'red', 'type': 'ripe'}, 
  {'item_name': 'banana', 'colors': 'yellow', 'type': 'not_ripe'}, 
  {'item_name': 'banana', 'colors': 'red', 'type': 'not_ripe'}
]

Since each item has unique properties (ie 'colors'), looping over the items is the intuitive solution:由于每个项目都有独特的属性(即“颜色”),循环项目是直观的解决方案:

import itertools

items = {
    "item_name": {
        "apple": {"color": ["red", "blue"]},
        "banana": {"color": ["yellow", "red"]}
        },
    "type": ["ripe", "not_ripe"]
}

items_product = []
for item, properties in items["item_name"].items():
    items_product.extend(
        [dict(zip(("type", *properties.keys(), "item_name"), (*values, item)))
         for values in itertools.product(items["type"], *properties.values())])

print(items_product)

Horrendous one-liner is also an option:可怕的单线也是一种选择:

items_product = [
    comb for item, properties in items["item_name"].items()
    for comb in (
        dict(
            zip(("item_name", "type", *properties.keys()),
                (item, *values))
        )
         for values in itertools.product(items["type"], *properties.values())
    )
]

I hope you understand this is a fertile soil for having nightmares.我希望你明白这是做噩梦的沃土。

First, we define the structure of the dictionary you provide and let it is T .首先,我们定义您提供的字典的结构,并将其设为T We can find that the value of T can be a list or a dictionary, but the value type of the dictionary must be T :我们可以发现T的值可以是列表也可以是字典,但是字典的值类型必须是T

T = dict[str, dict[str, 'T'] | list[str]]

After the recursive definition of the type is clear, we can easily (maybe not, I'll explain it in my spare time.) use the recursive function to solve it:明确了类型的递归定义后,我们就可以轻松(可能不会,我会在空闲时间解释)使用递归的function来解决它:

from functools import reduce
from operator import ior
from itertools import product


def flat(mapping: T) -> list[dict[str, str]]:
    collection = [[{k: vk} | mp for vk, vv in v.items() for mp in flat(vv)]
                  if isinstance(v, dict)
                  else [{k: elem} for elem in v]
                  for k, v in mapping.items()]
    return [reduce(ior, mappings, {}) for mappings in product(*collection)]

Test:测试:

>>> items = {
...     "item_name": {
...         "apple": {"color": ["red", "blue"]},
...         "banana": {"color": ["yellow", "red"]}
...         },
...     "type": ["ripe", "not_ripe"]
... }
>>> from pprint import pp
>>> pp(flat(items))
[{'item_name': 'apple', 'color': 'red', 'type': 'ripe'},
 {'item_name': 'apple', 'color': 'red', 'type': 'not_ripe'},
 {'item_name': 'apple', 'color': 'blue', 'type': 'ripe'},
 {'item_name': 'apple', 'color': 'blue', 'type': 'not_ripe'},
 {'item_name': 'banana', 'color': 'yellow', 'type': 'ripe'},
 {'item_name': 'banana', 'color': 'yellow', 'type': 'not_ripe'},
 {'item_name': 'banana', 'color': 'red', 'type': 'ripe'},
 {'item_name': 'banana', 'color': 'red', 'type': 'not_ripe'}]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM