在python中解析元组列表并消除双精度

在python中解析元组列表并消除双精度,python,list,python-3.x,Python,List,Python 3.x,我有以下问题: 我有一个表示软件包及其版本的元组列表。一些软件包没有指定的版本,因此没有问题,如: ('lib32c-dev', '', '', '') ('libc6-i386', '2.4', '', '') ('lib32c-dev', '', '', '') ('libc6-i386', '1.06', '', '') ('libc6-i386', '2.4', '', '') ('lib32c-dev', '', '', '')

我有以下问题:

我有一个表示软件包及其版本的元组列表。一些软件包没有指定的版本,因此没有问题,如:

    ('lib32c-dev', '', '', '')
    ('libc6-i386', '2.4', '', '')
    ('lib32c-dev', '', '', '')
    ('libc6-i386', '1.06', '', '')
    ('libc6-i386', '2.4', '', '')
    ('lib32c-dev', '', '', '')
    ('libc6-i386', '2.16', '', '')
    ('libc6-dev', '', '', '')
    ('', '', 'libc-dev', '')
    ('libc6-dev', '', '', '')
    ('', '', 'libc-dev', '')
    ('libncurses5-dev', '5.9+20150516-2ubuntu1', '', '')
    ('libc6-dev-x32', '', '', '')
    ('libc6-x32', '2.16', '', '')
    ('libncursesw5-dev', '5.9+20150516-2ubuntu1', '', '')
    ('libc6-dev-x32', '', '', '')
    ('libc6-x32', '2.16', '', '')
    ('libc6-dev-x32', '', '', '')
    ('libc6-x32', '2.16', '', '')
    ('libncurses5-dev', '5.9+20150516-2ubuntu1', '', '')
    ('libncursesw5-dev', '5.9+20150516-2ubuntu1', '', '')
如您所见,有些包多次以元组列出,但版本不同

我需要的是解析元组列表,以便在将列表转换为字典之前为每个包提供最新版本


PS:包名的位置及其版本不是固定的。但是我们可以说版本总是在包名之后,所以我们可以说版本总是在位置1和3吗


谢谢你的帮助。

你应该先把它转换成字典

data = {}
for value in my_list:
    data2 = iter(value)
    #find the first non-empty entry in our subtuple, that is our package name
    name = next(d for d in data2 if d)
    version = next(data2,'') # the version is whatever immediatly follows the package name
    data.setdefault(name,[]).append(version)
这将使您实现90%的目标,尽管这取决于包名是第一个元素。。。这显然并不总是正确的

下面是一种从字符串中获取版本号的方法

def float_version_from_string(version_string):
    try:
       return float(re.findall("\d.?\d*",version_string)[0])
    except (IndexError,ValueError):
       return -1    

这只是一个动态编写的虚拟实现。它没有经过测试,只有当元组的第一个元素是包名,第二个元素是它的版本时,它才应该工作。这可能不会给你确切的解决方案,但它肯定会帮助你解决问题

my_list_of_tuples = [...]  # some list
my_new_list = []
for tuple in my_list_of_tuples:
    version = float(tuple[1])
    package_name = tuple[0]
    for tuple in my_new_list:
        if tuple[0] == package_name and float(tuple[1]) > version:
            my_new_list.append(tuple)

当且仅当更新版本不存在时,您可以迭代列表,并将包放入dict中:

def version_as_list(s):
    """Converts string symoblizing version to list of integers
    for comparsion purposes."""
    return [int(i) for i in s.split('.')]

data = {}
for name, version, _, _:
    if vesion_as_list(data.get(name, '')) < version_as_list(version):
        data[name] = version

使用大量Python内置/库代码。这似乎是一个很长的解决方案,但实际上并非如此——这是因为我将文档放在了两者之间。代码只有7行

import re, itertools

pkgs = [('libc', '', '', ''), ... ]  # your list of tuples

# a function to extract a version number from a string
rxVSN = re.compile('^(?P<vsn>\d+(\.\d+)?)')
def version(s):
    mo = rxVSN.match(s)
    return float(mo.group('vsn')) if mo is not None else 0.0

# step one: sort the list of tuples by package name and reverse version
# uses built-in sorted() function
#     https://docs.python.org/2/library/functions.html#sorted
pkgs = sorted( pkgs, key = lambda tup: (tup[0], -version(tup[1])) )

# Now we can use the itertools.groupby() function to group the 
# tuples by package name. Then we take the first element of each
# group because that is the one with the highest version number
# (because that's how we sorted them ...)
#    https://docs.python.org/2/library/itertools.html#itertools.groupby
for (pkg, versions) in itertools.groupby( pkgs, key=lambda tup: tup[0]):
    print pkg,": ", next(versions)

我找到了理想的解决办法。我用过:

    apt_pkg.version_compare(a,b).
谢谢大家

功能:

    def comparePackages(package_dictionary):
     #loop in keys and values of package_dictionary
        for package_name, list_versions in zip(package_dictionary.keys(), package_dictionary.values()) :
            #loop on each sublist
            for position in xrange(len(list_versions)) :
                a = str(list_versions[position])
                b = str(list_versions[position-1])
                #the only way it worked was by using a and b
                vc = apt_pkg.version_compare(a,b)
                if vc > 0:
                    #a>b
                    max_version = a
                elif vc == 0:
                    #a==b
                    max_version = a         
                elif vc < 0:
                    #a<b
                    max_version = b

            del list_versions[:]
            if(max_version is '') :
                max_version = 'Not Specified'

            package_dictionary[package_name] = max_version

棘手的部分是找到一个比较函数来可靠地确定哪个版本更新。例如,我们想把2.16作为新的2.4,但是一个幼稚的字符串比较是不够的。更重要的是,浮点比较不仅不够,而且在无法将版本转换为浮点时还会引发ValueError

所需的排序类型可以称为自然排序或人工排序,在中有一些解决方案

可用于精确比较两个值而不是对列表排序的实现可能类似于:

import re

def tryint(s):
    try:
        return int(s)
    except:
        return s

def getchunks(s):
    return [tryint(c) for c in re.split('([0-9]+)', s)]

def compare_strings(s1, s2):
    return getchunks(s1) > getchunks(s2)

# 2.4 < 2.16
# 2.4 < 2.4.1
# a_1 < a_2
# and so on...
最终结果是:

'lib32c-dev': ''
'libc6-x32': '2.16'
'libc6-i386': '2.16'
'libncurses5-dev': '5.9+20150516-2ubuntu1'
'libc6-dev': ''
'libc-dev': ''
'libncursesw5-dev': '5.9+20150516-2ubuntu1'
'libc6-dev-x32': ''
请注意,比较_字符串不符合十进制顺序,例如2.001==2.1;实现这个细节会使代码更加混乱,而且可能与此无关。此外,如果不希望进行区分大小写的比较,可以更新tryint函数,在最后一行中使用s.lower

编辑:您的解决方案应该可以工作,但我通常建议在迭代字典时不要修改它。此外,压缩键和值似乎是可靠的,但调用项更容易。最后,行del list_versions[:]是荒谬的;它创建一个全新的列表只是为了删除它。您可以用更简洁的方式重写函数,如下所示:

from functools import cmp_to_key

def compare_packages(package_dictionary):
    new_dictionary = {}
    for package, versions in package_dictionary.items():
        version = max(versions, key=cmp_to_key(apt_pkg.version_compare))
        new_dictionary[package] = version or 'Not Specified'
    return new_dictionary

您可以在列表上迭代,并将包放入dict中,当且仅当其较新版本尚未存在时。谢谢您的评论。但问题是,我无法创建一个代码,如果版本更新或不更新,就可以获取…你能给我们看一段你已经尝试过的代码吗?我能想到的唯一方法是将元组视为一个列表并直观地解析它,但我不能这样做,因为包名的位置和它的版本(如果可用)是不固定的,我已经完成了但dict的问题如下:如果插入一个已经存在的键,它将修改旧值并放入新值。此外,软件包的位置和它的版本也不稳定,正如你在我的例子中看到的那样。。。因为这和你描述的不一样。。。但是它会崩溃,因为你不知道名字或版本会在哪里tuple@Marc添加了一些逻辑来尝试查找/猜测名称,它假定名称将是元组中的第一个非空值,并且版本立即跟随它是的,我在代码中使用了一个字典,如下所示:dictionnaire_package=dictTuple[I:I+2]对于xrange0中的i,lenTuple,2,但正如我之前在注释中所说的,如果该键已经存在于字典中,那么它的值将被自动替换。通过执行我的代码,我从每个元组中得到2个键和2个值,然后我进行了清理,这很好,但是有一个被替换的版本却无法控制替换,这真的是一个大问题,这是什么?这不是我答案的一部分。。。是的,你试过字典的答案。。。但这根本不是我的解决方案所做的。。。事实上,我不知道你粘贴的代码正在做什么,或者你希望它做什么。。。而且它与我的代码完全不同!您能解释一下为什么对“.”进行拆分吗?对不起,代码中有错误,请参阅编辑。字符串是一个字符接一个字符进行比较的,因此“10.0”<“2.7”,但[2,7]<[10,0],是一个
nd[]from collections import defaultdict lib_ver_dict = defaultdict(str) for lib_tuple in lib_tuples: generator = (string for string in lib_tuple if string) lib, ver = next(generator), next(generator, '') if compare_strings(ver, lib_ver_dict[lib]): lib_ver_dict[lib] = ver
'lib32c-dev': ''
'libc6-x32': '2.16'
'libc6-i386': '2.16'
'libncurses5-dev': '5.9+20150516-2ubuntu1'
'libc6-dev': ''
'libc-dev': ''
'libncursesw5-dev': '5.9+20150516-2ubuntu1'
'libc6-dev-x32': ''
from functools import cmp_to_key

def compare_packages(package_dictionary):
    new_dictionary = {}
    for package, versions in package_dictionary.items():
        version = max(versions, key=cmp_to_key(apt_pkg.version_compare))
        new_dictionary[package] = version or 'Not Specified'
    return new_dictionary