Seite 1 von 1

items aus listen and-verknüpfen...

Verfasst: Sonntag 24. April 2005, 17:06
von jens
Hi...

ich habe mehrere Listen und will das ich nur die Einträge zurück bekomme, die in allen Listen vorkommen.
Ich habe dafür zwar eine Lösung, aber diese scheint mir nicht wirklich optimal:

Code: Alles auswählen

def and_list( data ):
    if len( data ) == 1:
        # Es gibt nur eine, also sind alle Dateien treffer
        return data.values()[0]

    filelist = data.values()

    print "Listen anzeigen:"
    for i in filelist: print i
    print "-"*80

    resultlist = []
    for idlist in filelist:
        for id in idlist:
            if id in resultlist:
                # id schon vorhanden
                continue

            ok = True
            for id2 in filelist:
                if not id in id2:
                    ok = False
                    break
            if ok:
                resultlist.append( id )

    return resultlist

data = {
    "eins" : [36, 37, 38, 39],
    "zwei" : [36, 37],
    "drei" : [32, 33, 34, 35, 36, 37]
}

result = and_list( data )
print "Ergebnis:", result

print "-"*80

result = and_list( {"nureins":[1,2,3]} )
print "Ergebnis:", result
Ausgabe:
Listen anzeigen:
[32, 33, 34, 35, 36, 37]
[36, 37, 38, 39]
[36, 37]
--------------------------------------------------------------------------------
Ergebnis: [36, 37]
--------------------------------------------------------------------------------
Ergebnis: [1, 2, 3]
Die anzahl der Listen variiert und die Inhalte sind nicht unbedingt Zahlen.
Bei meiner Lösung ist es dumm, das immer alle Einträge durchgeganen werden, obwohl dies nicht immer nötig ist. Aber ich weiß noch nicht so recht, wie man es besser Lösen könnte...

Verfasst: Sonntag 24. April 2005, 18:47
von CM
Hoi Jens,

schau mal im Tutorial unter Sets. Bietet das alles was Du suchst?

Gruß,
Christian

Re: items aus listen and-verknüpfen...

Verfasst: Sonntag 24. April 2005, 18:54
von joe
jens hat geschrieben: Ich habe dafür zwar eine Lösung, aber diese scheint mir nicht wirklich optimal:
Naja, einiges ginge schon einfacher.
Die größe eines dicts: len(data)
Liste mit den werten: filelist = data.values()
Um die schnittmenge aller listen zu ermitteln, gibt es die methode intersection() des datentyps set.
Vielleicht ein aktuelles buch kaufen :wink:
joe

Verfasst: Sonntag 24. April 2005, 19:08
von jens
joe hat geschrieben:Die größe eines dicts: len(data)
Liste mit den werten: filelist = data.values()
Danke, das hätte ich eigentlich wissen sollen :oops: (Ich werde es oben mal aktualisieren)
CM hat geschrieben:schau mal im Tutorial unter Sets. Bietet das alles was Du suchst?
Eigentlich schon... Aber es soll auch mit Python 2.2.1 gehen :cry:

Verfasst: Sonntag 24. April 2005, 19:42
von joe
jens hat geschrieben:Aber es soll auch mit Python 2.2.1 gehen :cry:
Vielleicht alle werte in ein dict packen und dabei zählen:

Code: Alles auswählen

ll = [ [36, 37, 38, 39],[36, 37],[32, 33, 34, 35, 36, 37] ]
count = {}
for l in ll:
    for e in l:
        count[e] = count.get(e,0) + 1
        
res = [k for k,v in count.items() if v == len(ll)]
Geht aber nur, wenn in einer liste ein wert nur einmal vorkommt (was bei dateien eines verzeichnisses ja sichergestellt sein sollte).
joe

Verfasst: Sonntag 24. April 2005, 20:09
von jens
jens hat geschrieben:
CM hat geschrieben:schau mal im Tutorial unter Sets. Bietet das alles was Du suchst?
Eigentlich schon... Aber es soll auch mit Python 2.2.1 gehen :cry:
Ha! Man kann den Quellentext (./Lib/sets.py) auch in Python 2.2.1 benutzen :wink: Python ist ja sowas von cool :lol:

Verfasst: Sonntag 24. April 2005, 20:32
von jens
Wobei ganz zufrieden bin ich damit noch nicht:

Code: Alles auswählen

from sets import Set as set

def and_list( data ):
    filelist = data.values()

    if len( filelist ) == 1:
        # Es gibt nur eine, also sind alle Dateien treffer
        return filelist[0]

    print "Listen anzeigen:"
    for i in filelist: print i
    print "-"*80

    result = set( filelist[0] )
    for i in xrange( 1,len(filelist) ):
        result = result & set( filelist[i] )
    return list( result )


data = {
    "eins" : [36, 37, 38, 39],
    "zwei" : [36, 37],
    "drei" : [32, 33, 34, 35, 36, 37]
}

print "<pre>"
result = and_list( data )
print "Ergebnis:", result

print "-"*80

result = and_list( {"nureins":[1,2,3]} )
print "Ergebnis:", result

Verfasst: Sonntag 24. April 2005, 21:20
von CM
Wieso kannst Du Sets importieren? "sets" steht doch gar nicht im globule module index von 2.2.1 - zumindest laut Doku ( http://www.python.org/doc/2.2.1/modindex.html ). Das verwirrt mich etwas.
Jedenfalls finde ich Deine Lösung nicht schlecht. Alternativ vielleicht noch folgende Lösung:

Code: Alles auswählen

def f(*args):
	for l in args[1:]: args[0].extend(l)
	l = []
	for x in args[0]:
		if x not in l and args[0].count(x) == len(args): l.append(x)
	return l
	
a = [1,2,3]
b = [3,2]
c = [6,5,4,3,2,1]

print f(a,b,c)
Gruß,
Christian

Verfasst: Sonntag 24. April 2005, 23:09
von BlackJack
jens hat geschrieben:Wobei ganz zufrieden bin ich damit noch nicht:
Ich finde den Namen verwirrend. Ich fragte mich zuerst was `values()` sollte weil Listen diese Methode ja gar nicht haben.

Den Spezialfall in der ersten if-Abfrage kannst Du Dir sparen. Es sei denn das umwandeln Liste->Set->Liste ist wirklich zu langsam. Das Ergebnis ist jedenfalls das gleiche. Problematischer ist der Fall, wenn das Dictionary leer ist, dann gibts einen `IndexError`. Für den Fall ist im folgenden die Ausnahmebehandlung:

Code: Alles auswählen

def and_list( data ): 
    filelist_iter = iter(map(set, data.values()))
    # ``filelist_iter = imap(set, data.itervalues())`` in Python >= 2.3
    
    try:
        first = filelist_iter.next()
    except StopIteration:
        return list()
    
    return list(reduce(operator.and_, filelist_iter, first))

Verfasst: Montag 25. April 2005, 06:26
von jens
CM hat geschrieben:Wieso kannst Du Sets importieren? "sets" steht doch gar nicht im globule module index von 2.2.1 - zumindest laut Doku ( http://www.python.org/doc/2.2.1/modindex.html ).
OK, vielleicht hätte ich noch einen Satz dazu schreiben können. Ich habe die Datei ./Lib/sets.py von der Version 2.4 einfach zu meinem Skript gelegt. Durch den manuellen import mit "from sets import Set as set" steht es ganz normal zu verfügung...

Übrigends steht in sets.py ein Hinweis, der mich erst darauf gebracht hat:

Code: Alles auswählen

# Code to make the module run under Py2.2
@BlackJack: Du hast recht mit dem spezial Fall, mit nur einer Liste... Aber das Abfragen, wenn die Liste leer ist, kann ich mir in meinem Fall sparen. Das wird in meinem Programm schon vorher abgefangen...

Nun Frage ich mich, wie ich abfragen kann, ob es "set()" schon gibt oder nicht. Meine Variante:

Code: Alles auswählen

if not "set" in dir(__builtins__):
    from sets import Set as set

Verfasst: Montag 25. April 2005, 12:49
von Leonidas
jens hat geschrieben:Nun Frage ich mich, wie ich abfragen kann, ob es "set()" schon gibt oder nicht. Meine Variante:

Code: Alles auswählen

if not "set" in dir(__builtins__):
    from sets import Set as set
Ich würds nach EAFP machen:

Code: Alles auswählen

try:
    set
except NameError:
    from sets import Set as set

Verfasst: Montag 25. April 2005, 13:27
von jens
Leonidas hat geschrieben:Ich würds nach EAFP machen
So geht's natürlich etwas weniger kompliziert... Ich find nur immer, das das so'n bischen Hammer-Methodik ist ;)

Verfasst: Montag 25. April 2005, 13:33
von Leonidas
jens hat geschrieben:So geht's natürlich etwas weniger kompliziert... Ich find nur immer, das das so'n bischen Hammer-Methodik ist ;)
Naja, so muss man nicht Python's __magische__ Variablen nutzen, außerdem ist das Verfahren ziemlich wasserdicht.

Verfasst: Dienstag 25. April 2006, 21:30
von Michael Schneider
CM hat geschrieben: Jedenfalls finde ich Deine Lösung nicht schlecht. Alternativ vielleicht noch folgende Lösung:

Code: Alles auswählen

def f(*args):
	for l in args[1:]: args[0].extend(l)
	l = []
	for x in args[0]:
		if x not in l and args[0].count(x) == len(args): l.append(x)
	return l
	
a = [1,2,3]
b = [3,2]
c = [6,5,4,3,2,1]

print f(a,b,c)
Hallo,

zum einjährigen Jubiläum des Threads und weil ich mich gerade mit der Sache beschäftige, wollte ich meine Lösung, aufbauend auf CMs Vorschlag vorstellen:

Code: Alles auswählen

def f(*args):
	d, arg_num = {}, len(args)
	for l in args:
            for x in l:
                try:    d[x] += 1
                except: d[x]  = 1
	return filter(lambda x: arg_num == d[x], d.keys())
	
a = [1,2,3]
b = [3,2]
c = [6,5,4,3,2,1]

print f(a,b,c)
Wenn wir schon davon ausgehen, dass in den einzelnen Listen maximal ein Vorkommen existiert, kann man zum Zusammenfassen auch gleich ein Dict nehmen. Das geht schneller als die Verwendung von Listen und man braucht nicht immer die beiden Listen zu durchsuchen/-zählen, da der Vereinzellungseffekt beim Dict inklusive ist. Ansonsten ist das Prinzip wie bei CM, das kann man wohl nicht toppen. :-)

Grüße,
Michael