在字典列表中查找循环路径 (Python)
Finding a circular path in a list of dicts (Python)
我有类似这样的数据
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": None},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None},
...
]
其中 id 始终是字典在列表中的位置。
item_totals 将被聚合,“send_to_id”键影响聚合的发生方式。在这里,由于“id”= 2 的字典有“send_to_id”= 1,因此“id”= 1 的字典就是我所说的目标层,这两个项目的总和将被聚合与正常情况不同。
不允许的是这样的循环,其中第 1 项指向第 2 项,第 2 项指向第 1 项。
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 2},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None}
]
或者这个,还是圆形的,走三步
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 3},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": 2}
]
另外一个项目不能指向它自己。
我不确定该怎么做,但我想像上面那样接受一个字典列表,看看是否存在循环路径,这样我就可以向用户提供适当的错误消息。谁能帮忙?
我正在努力弄清楚如何找到下一个项目的路径,但它在我的大脑中打结了。
如果重要,列表的最大长度约为 50。
谢谢!
最好的第一步可能是创建仅包含地图的字典。我们可以用一个很好的 dictionary comprehension 来做到这一点(看下面第二个绿色代码示例块)。
def get_direct_mappings(data: list) -> dict:
return {d["id"]: d["send_to_id"] for d in data if d["send_to_id"] is not None}
现在我们有一个稍微容易解决的问题,我们可以使用 previously made solutions 来帮助我们。具体来说,我们基本上可以重复使用“Update”之后发布的解决方案,并将我们上面的代码添加到其中。
def find_cycles(original_data: list) -> list:
n = {d["id"]: d["send_to_id"] for d in original_data if d["send_to_id"] is not None}
cycles = []
while n:
visited = {}
count = 0
k, v = n.popitem()
while v is not None:
# visited[k] = (count, v)
visited[k] = count
count += 1
k = v
v = n.pop(k, None)
if k in visited:
if len(visited) == 1:
cycle = tuple(visited.keys())
else:
cycle_start = visited[k]
cycle = sorted((c, k) for k, c in visited.items() if c >= cycle_start)
cycle = tuple(k for c, k in cycle)
k = min(range(len(cycle)), key=lambda x: cycle[x])
cycle = cycle[k:] + cycle[:k]
cycles.append(cycle)
return cycles
虽然它不是最漂亮的,但它确实有效。
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 3},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": 2}
]
print(find_cycles(mydict))
# prints [(1, 3, 2)]
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 2},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None}
]
print(find_cycles(mydict))
# prints [(1, 2)]
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": None},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None},
]
print(find_cycles(mydict))
# prints []
我有类似这样的数据
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": None},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None},
...
]
其中 id 始终是字典在列表中的位置。
item_totals 将被聚合,“send_to_id”键影响聚合的发生方式。在这里,由于“id”= 2 的字典有“send_to_id”= 1,因此“id”= 1 的字典就是我所说的目标层,这两个项目的总和将被聚合与正常情况不同。
不允许的是这样的循环,其中第 1 项指向第 2 项,第 2 项指向第 1 项。
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 2},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None}
]
或者这个,还是圆形的,走三步
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 3},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": 2}
]
另外一个项目不能指向它自己。
我不确定该怎么做,但我想像上面那样接受一个字典列表,看看是否存在循环路径,这样我就可以向用户提供适当的错误消息。谁能帮忙? 我正在努力弄清楚如何找到下一个项目的路径,但它在我的大脑中打结了。
如果重要,列表的最大长度约为 50。
谢谢!
最好的第一步可能是创建仅包含地图的字典。我们可以用一个很好的 dictionary comprehension 来做到这一点(看下面第二个绿色代码示例块)。
def get_direct_mappings(data: list) -> dict:
return {d["id"]: d["send_to_id"] for d in data if d["send_to_id"] is not None}
现在我们有一个稍微容易解决的问题,我们可以使用 previously made solutions 来帮助我们。具体来说,我们基本上可以重复使用“Update”之后发布的解决方案,并将我们上面的代码添加到其中。
def find_cycles(original_data: list) -> list:
n = {d["id"]: d["send_to_id"] for d in original_data if d["send_to_id"] is not None}
cycles = []
while n:
visited = {}
count = 0
k, v = n.popitem()
while v is not None:
# visited[k] = (count, v)
visited[k] = count
count += 1
k = v
v = n.pop(k, None)
if k in visited:
if len(visited) == 1:
cycle = tuple(visited.keys())
else:
cycle_start = visited[k]
cycle = sorted((c, k) for k, c in visited.items() if c >= cycle_start)
cycle = tuple(k for c, k in cycle)
k = min(range(len(cycle)), key=lambda x: cycle[x])
cycle = cycle[k:] + cycle[:k]
cycles.append(cycle)
return cycles
虽然它不是最漂亮的,但它确实有效。
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 3},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": 2}
]
print(find_cycles(mydict))
# prints [(1, 3, 2)]
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": 2},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None}
]
print(find_cycles(mydict))
# prints [(1, 2)]
mydict = [
{"id": 0, "item_total": 10000, "send_to_id": None},
{"id": 1, "item_total": 15000, "send_to_id": None},
{"id": 2, "item_total": 30000, "send_to_id": 1},
{"id": 3, "item_total": 20000, "send_to_id": None},
]
print(find_cycles(mydict))
# prints []