2016-03-23 10 views
1

Ich habe folgendes Problem:Parse-Liste der Tupel in Python und beseitigen Doppel

ich eine Liste von Tupel haben repräsentiert Pakete und deren Version (einige Pakete haben keine angegebene Version so kein Problem damit) wie Also:

('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', '', '') 

Wie Sie sehen können, sind einige Pakete in Tupel mehr als einmal aufgeführt, aber mit einer anderen Version.

Was ich brauche, ist die Liste der Tupel zu analysieren, um für jedes Paket die neueste Version zu haben, bevor die Liste in ein Wörterbuch umgewandelt wird.

PS: Die Position des Paketnamens und der Version sind nicht festgelegt. Aber wir können sagen, dass die Version immer hinter dem Paketnamen steht, also können wir sagen, dass die Version immer an Position 1 und 3 sein wird?

Vielen Dank für Ihre Hilfe.

+2

Sie können über die Liste iterieren und Paket in dict wenn und nur wenn seine neuere Version nicht schon da ist. – GingerPlusPlus

+0

Vielen Dank für Ihren Kommentar. Aber das Problem ist, dass ich keinen Code erstellen kann, der abholen kann, wenn die Version neuer ist oder nicht ... – Marc

+0

Können Sie uns einen Codeabschnitt zeigen, den Sie versucht haben? – Ilyas

Antwort

-3

I die gewünschte Lösung gefunden zu haben. Ich benutzte:

apt_pkg.version_compare(a,b). 

Danke euch allen.

Funktion:

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 

Ausgang:

lib32c-dev : Not Specified 
    libc6-x32 : 2.16 
    libc6-i386 : 2.16 
    libncurses5-dev : 5.9+20150516-2ubuntu1 
    libc6-dev : Not Specified 
    libc-dev : Not Specified 
    libncursesw5-dev : 5.9+20150516-2ubuntu1 
    libc6-dev-x32 : Not Specified 
+0

Sollte 'libc6-i386: 2.4' nicht 'libc6-i386: 2.16' sein? –

+0

ja du hast Recht ... – Marc

+0

Dann ... warum postest du deine Lösung nicht? – GingerPlusPlus

0

Sie sollten es eigentlich verwandeln sich in einem Wörterbuch ersten

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) 

, dass Sie 90% der Art und Weise wird es bekommen, obwohl dies auf den Paketnamen zählt ... das erste Element sein, das immer funktioniert offenbar nicht halten wahr ...

hier ist eine Möglichkeit, die Versionsnummer aus einem String bekommen konnte

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

Ich habe das getan, aber die Das Problem von dict ist folgendes: Wenn Sie einen Schlüssel einfügen, der bereits existiert, wird der alte Wert geändert und der neue Wert hinzugefügt. Außerdem sind die Position des Pakets und seine Version nicht stabil, wie Sie in meinem Beispiel sehen können. – Marc

+0

Sie haben eindeutig nicht * this * ... getan, da dies nicht tun wird, was Sie beschreiben ... aber es wird brechen, da Sie nicht wissen, wo der Name oder die Version im Tupel sein wird –

+0

@Marc hinzugefügt einige Logik zu versuchen und (finden/erraten) Der "Name" nimmt an, dass der Name der erste nicht leere Wert im Tupel ist, und die Version folgt unmittelbar darauf –

-1

Dies ist nur eine Dummy-Implementierung auf dem geschrieben Fliege. Es wurde nicht getestet und es sollte nur funktionieren, wenn das erste Element des Tupels der Paketname und das zweite Element seine Version ist. Dies gibt Ihnen möglicherweise nicht die genaue Lösung, aber es sollte definitiv helfen, Ihr Problem anzugehen.

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) 
+0

"PS: Die Position des Paketnamens und seine Version sind nicht festgelegt. Aber wir können sagen, dass die Version immer nach dem Paketnamen ist. Also können wir sagen, dass die Version immer an Position 1 und 3 sein wird" –

+0

Auch dies fügt nur (Paket-, Versions-) Tupel an, die nicht gesehen wurden oder neuer als eine zuvor gesehene Version sind. Es ist nicht wichtig, den alten Eintrag zu entfernen. Außerdem wird davon ausgegangen, dass Versionen in Floats umgewandelt werden können, was in den Beispieldaten nicht der Fall ist. –

-1

Sie können über die Liste iterieren und setzen Paket in dict, wenn und nur wenn seine neuere Version nicht bereits vorhanden ist:

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 
+0

Würden Sie bitte erklären, warum Sie einen Split auf ein "." ? – Marc

+0

Entschuldigung, ich habe einen Fehler im Code, siehe Bearbeiten. Zeichenfolgen werden Zeichen für Zeichen verglichen, also "10 .0" <"2.7", aber "[2, 7] <[10, 0]" und "[] GingerPlusPlus

+1

Dies funktioniert nicht für Versionen mit [^ 0-9.] Zeichen oder wenn der Name und die Version nicht die ersten beiden Einträge sind. –

-1

viel Python eingebaut/Bibliothekscode. Scheint eine lange Lösung, ist es aber wirklich nicht - es liegt an der Dokumentation, die ich eingefügt habe. Der Code besteht nur aus 7 Zeilen.

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) 

Ausgänge:

: ('', '', 'libc-dev', '') 
lib32c-dev : ('lib32c-dev', '', '', '') 
libc6-dev : ('libc6-dev', '', '', '') 
libc6-dev-x32 : ('libc6-dev-x32', '', '', '') 
libc6-i386 : ('libc6-i386', '2.4', '', '') 
libc6-x32 : ('libc6-x32', '2.16', '', '') 
libncurses5-dev : ('libncurses5-dev', '5.9+20150516-2ubuntu1', '', '') 
libncursesw5-dev : ('libncursesw5-dev', '5.9+20150516-2ubuntu1', '', '') 
+0

Sollte 'libc6-i386: 2.4'' libc6-i386: 2.16' nicht sein? –

+0

Ich dachte 2,4 war neuer als 2,16. Wenn Sie die Liste überprüfen, erscheinen sowohl 2.4 als auch 2.16. Es kann sein, _inconsistencies_ in der Liste, aber das ist ein anderes Problem ... – haavee

+0

Nicht gemäß der [Änderungsprotokoll] (http://changelogs.ubuntu.com/changelogs/pool/main/e/eglibc/eglibc_2.19-0ubuntu6 .7/changelog). –

0

Der schwierige Teil eine Vergleichsfunktion ist zu finden, die zuverlässig bestimmt, welche Version neuer ist. Zum Beispiel wollen wir 2.16 als neuer als 2.4 betrachten, aber ein naive String-Vergleich ist nicht ausreichend.Mehr noch, ein Float-Vergleich ist nicht nur unzureichend, es wird eine ValueError auslösen, wenn eine Version nicht in einen Float umgewandelt werden kann.

Die Art der gewünschten Sortierung könnte als "natürliche Sortierung" oder "menschliche Sortierung" bezeichnet werden und es gibt einige Lösungen in this question.

eine Implementierung, die genau zwei Werte verwendet werden kann, vergleichen (und nicht eine Liste sortieren) könnte so etwas aussehen:

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... 

Dies kann in einem relativ einfachen Algorithmus verwendet werden, defaultdict mit welchen Bibliotheken verfolgen ist gesehen worden. Dies setzt voraus, dass die Liste der Tupel in lib_tuples enthalten ist.

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 

Das Endergebnis ist:

'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': '' 

zu beachten, dass nicht mit compare_strings dezimalen Ordnungs nicht entspricht (z.B. 2.001 == 2.1); Die Implementierung dieses Details macht den Code viel unordentlicher (und ist wahrscheinlich irrelevant). Wenn Sie keinen Vergleich zwischen Groß- und Kleinschreibung vornehmen möchten, können Sie die Funktion tryint in der letzten Zeile verwenden, um s.lower() zu verwenden.

Bearbeiten: Ihre Lösung sollte funktionieren, aber ich würde im Allgemeinen empfehlen, ein Wörterbuch nicht zu ändern, während ich darüber iteriere. Auch Zipping keys und values scheint zuverlässig zu sein, aber es ist einfacher, items zu rufen. Schließlich ist die Zeile del list_versions[:] unsinnig; es erstellt eine brandneue Liste, nur um sie zu löschen. Sie könnten Ihre Funktion übersichtlicher schreiben als:

+0

Vielen Dank für Ihre Hilfe, ich werde versuchen zu implementieren, was Sie in meinem Projekt erstellt haben, werde ich Sie auf dem Laufenden halten. – Marc