如果一个值相同而另一个值较低,则从列表中删除字典

4

我有一个包含版本号字典的列表

my_list = [{'version': 'v1.2.3', 'major': '1.2'},
           {'version': 'v1.2.7', 'major': '1.2'},
           {'version': 'v1.3.7', 'major': '1.3'},
           {'version': 'v1.4.1a1', 'major': '1.4'},
           {'version': 'v1.3.8b1', 'major': '1.3'},
           {'version': 'v1.3.2', 'major': '1.3'}]

最终,我希望这个列表仅包含每个主要版本的最新版本,并删除所有alpha/beta版本。
my_list = [{'version': 'v1.2.7', 'major': '1.2'},
           {'version': 'v1.3.7', 'major': '1.3'}]

我的第一反应是创建一个新列表并遍历原列表,如果专业不在新列表中,则添加到新列表中,如果在新列表中,则比较并替换。但我认为可能有更符合Python风格的方法。

编辑:还有alpha和beta版本,我想完全从列表中删除它们。


1
我问了你一个问题。你能回答一下吗?“输出的顺序很重要吗?” - cs95
2个回答

4

还有一种情况,可以使用itertools.groupby来解决:

from itertools import groupby

my_list = [{'version': 'v1.2.3', 'major': '1.2'},
           {'version': 'v1.2.7', 'major': '1.2'},
           {'version': 'v1.3.7', 'major': '1.3'},
           {'version': 'v1.4.1a1', 'major': '1.4'},
           {'version': 'v1.3.8b1', 'major': '1.3'},
           {'version': 'v1.3.2', 'major': '1.3'}]

my_list_ = list(filter(lambda x: all(beta not in x['version'] for beta in ('a', 'b')), my_list))  # removing beta-versions

version_f = lambda y: [0 if any(beta in x for beta in ('a', 'b')) else int(x) for x in y['version'].replace('v', '').split('.')]
grouper = lambda x: x['major']

d = [max(k, key=version_f) for _, k in groupby(sorted(my_list, key=grouper), key=grouper)]
print(d)  # -> [{'version': 'v1.2.7', 'major': '1.2'}, {'version': 'v1.3.7', 'major': '1.3'}, {'version': 'v1.4.1a1', 'major': '1.4'}]

注意:

  • 不要使用名称list。您正在覆盖Python内置。
  • 如果list-comprehension过于复杂,请使用good-oldfor循环进行分解。这样做没有任何问题。
  • 正如@Coldspeed所提到的,比较版本并不是那么直截了当,所以为了展示这一点,我稍微修改了您的输入(添加了{'version': 'v1.3.12', 'major': '1.3'}),并让lambda更加聪明1

1.这基于Python整数列表的内置排序方案工作([1, 3, 10] > [1, 3, 7]返回True)。


2
@cᴏʟᴅsᴘᴇᴇᴅ,你提供的两个例子都可以运行。请看这里。而你链接的帖子也使用了同样的原理。 - Ma0
这几乎完美!但是我刚刚发现,我还有a1和b2版本,我希望在这种情况下完全忽略它们。你目前的解决方案会抛出一个错误,因为它不是一个整数。 - Andreas Hubert
1
@AndreasHubert,您能否编辑您的问题并包括那些边缘情况 - Ma0
1
没问题,我喜欢苛刻;) 顺便说一下,在max里面不需要调用list()。 最后,lambda是匿名函数。如果你给这个匿名函数命名,那你最好还是使用一个函数。 - Eric Duminil
1
@AndreasHubert 现在怎么样了? - Ma0
显示剩余9条评论

2
您的想法
您的想法实际上是一个不错的想法。它高效,可以相对Pythonic地实现:
import re
releases = [{'version': 'v1.2.3', 'major': '1.2'},
            {'version': 'v1.2.7', 'major': '1.2'},
            {'version': 'v1.3.7', 'major': '1.3'},
            {'version': 'v1.4.1a1', 'major': '1.4'},
            {'version': 'v1.3.8b1', 'major': '1.3'},
            {'version': 'v1.3.2', 'major': '1.3'}]

stable_releases = [r for r in releases if 'a' not in r['version']
                                      and 'b' not in r['version']]

latest = {}

def major_minor_build(version):
    return [int(d) for d in re.findall('\d+', version)]

for release in stable_releases:
    version, major = release['version'], release['major']
    latest[major] = max([version, latest.get(major, '')],
                                  key=major_minor_build)

print(latest)
# {'1.2': 'v1.2.7', '1.3': 'v1.3.7'}

输出的数据是一组 (major, latest) 的字典对,相比于一组字典列表来说更易于处理。

SetuptoolsVersion

版本可能会很棘手。我们不需要重复造轮子,可以使用 pkg_resources.SetuptoolsVersion。比较已经实现了,所以 maxsort 不需要任何关键字。作为奖励,如果版本是 alpha 或 beta,is_prerelease 将为 True:

from pkg_resources import SetuptoolsVersion, parse_version
from itertools import groupby

def get_major(release):
    return release._version.release[:2]

mylist = [{'version': 'v1.2.3', 'major': '1.2'},
         {'version': 'v1.2.7', 'major': '1.2'},
         {'version': 'v1.3.7', 'major': '1.3'},
         {'version': 'v1.4.1a1', 'major': '1.4'},
         {'version': 'v1.3.8b1', 'major': '1.3'},
         {'version': 'v1.3.2', 'major': '1.3'}]

releases = [parse_version(r['version']) for r in mylist]
stable_releases = [r for r in releases if not r.is_prerelease]
stable_releases.sort()

print({major:max(group) for major, group in groupby(stable_releases, key=get_major)})
# {(1, 2): <Version('1.2.7')>, (1, 3): <Version('1.3.7')>}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接