首页 > 解决方案 > 在具有匹配属性的 dict 数组中查找最小值,返回最大的分组

问题描述

这很容易通过几个循环来做到这一点,但我确信有一种更有效的方法可以实现这一点,我很想学习。

考虑以下 dict 数组,它表示从 nosql 数据库中提取的数据。

x = [
    {
        "loc" : "alpha",
        "tag" : 1,
        "dist" : 5
    },
    {
        "loc" : "bravo",
        "tag" : 0,
        "dist" : 2
    },
    {
        "loc" : "charlie",
        "tag" : 5,
        "dist" : 50
    },
    {
        "loc" : "delta",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "echo",
        "tag" : 2,
        "dist" : 30
    },
    {
        "loc" : "foxtrot",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "gamma",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "hotel",
        "tag" : 0,
        "dist" : 2
    },
]

我想找到所有具有最低“dist”值的项目,并且如果有多个具有相同最低值的字典,我希望对具有相同最小值的最多字典的属性“标签”进行分组.

例如,从上面返回的所需数据将是:

r = [
    {
        "LocationName" : "delta",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "foxtrot",
        "tag" : 4,
        "dist" : 2
    },
    {
        "loc" : "gamma",
        "tag" : 4,
        "dist" : 2
    }
]

总结:dist:2是最小值,[bravo, delta, foxtrot, gamma, hotel]的dist都是2,[bravo, hotel]的tag是:0,[delta, foxtrot, gamma]有tag的:4 。返回一个 dicts [delta, foxtrot, gamma] 的数组,因为它们有更多具有相同匹配标签和最低 dist 的数组。

我正在使用python 3.6。

感谢您的帮助和兴趣!

标签: pythonarrayspython-3.xdictionary

解决方案


您可以为and指定一个key(即 lambda 函数)来帮助解决这个问题。对于您的第一次测试,max()min()

lowest_single_dist = min(x, key=lambda i: i["dist"])

x返回 中具有最低值的元素"dist"。如果您想要所有具有该标记值的元素,则可以使用列表推导:

lowest_dists = [i for i in x if i["dist"] == lowest_single_dist["dist"]]

为了获得最大的分组,我将首先在该子集中创建一组可能的值"tag",然后检查每个中有多少lowest_dists,然后取具有最高计数的那个:

tags = [i["tag"] for i in lowest_dists]              # get a list of just the tags
ct = {t: tags.count(t) for t in set(tags)}           # make a dict of tag:count for each unique tag
max_tag = max(ct, key=lambda x: ct[x])               # find the largest count and get the largest tag
r = [i for i in lowest_dists if i["tag"] == max_tag] # use another list comprehension to get all the max tags

如果您想将其全部缩短为两条线,则可以不那么 Pythonic 并执行以下操作:

m = min(x, key=lambda i: (i["dist"], -1 * max([j["tag"] for j in x if j["dist"] == i["dist"]].count(i["tag"])))
r = [i for i in x if i["tag"] == m["tag"] and i["dist"] == m["dist"]]

这利用了您可以返回一个元组作为排序键的事实,并且只有第一个相等时才会检查元组的第二个值。我将扩展第一行并解释每个部分在做什么:

m = min(x, key=lambda i: (
    i["dist"], -1 * max(
        [j["tag"] for j in x if j["dist"] == i["dist"]].count(i["tag"])
    ))
  • 最里面的列表推导为所有元素生成一个标签列表,x其中的值与"dist"as相同i
  • 然后,计算与i
  • 乘以 -1 使其为负数,以便min()正确运行
  • 制作一个元组i["dist"]和我们刚刚计算的值(i["tag"]in的频率x),并为每个元素返回它
  • 分配给m列表中具有最低值"dist"和最频繁值的元素"tag"
  • 分配给具有相同值的r元素的子列表x"dist""tag"

所以基本上与上面的过程相同,但更短,效率更低,而且更复杂一些。


推荐阅读