Seite 1 von 2

Aus einer Liste Endelemente entfernen

Verfasst: Samstag 20. Mai 2017, 21:01
von Alfons Mittelmeyer
Ich wollte einmal fragen, ob es einen Python Befehl für so etwas gibt.
Ich habe eine Liste:

Code: Alles auswählen

my_list = [ (1,2,3),
            (4,5,6),
            (7,8,9),
            (0,0,0),
            (0,0,0),
            (0,0,0) ]
Und daraus möchte ich die (0,0,0) Elemente am Ende entfernen.
Gibt es für so etwas eine Python Funktion oder macht man das auf herkömmliche Art mit einer Schleife?

Meine Lösung sähe so aus:

Code: Alles auswählen

def remove_trailing_elements(some_list,trailing_value):
    for index in range(len(some_list)-1,-1,-1):
        if some_list[index] == trailing_value:
            some_list.pop(index)
Aber entspricht die der Python Philosophie, dass es nur genau eine richtige Lösung geben soll?
Aber welche ist da dann die Richtige?

Re: Aus einer Liste Endelemente entfernen

Verfasst: Samstag 20. Mai 2017, 21:42
von karolus

Code: Alles auswählen

from itertools import takewhile, filterfalse

list(takewhile(lambda x: x!=(0,0,0), my_list))
#oder
list(filterfalse(lambda x: x==(0,0,0), my_list))
takewhile nur falls die nicht gewünschten Elemente zusammen am Ende stehen.

Re: Aus einer Liste Endelemente entfernen

Verfasst: Samstag 20. Mai 2017, 22:00
von pillmuncher

Code: Alles auswählen

>>> my_list = [
...     (1, 2, 3),
...     (4, 5, 6),
...     (7, 8, 9),
...     (0, 0, 0),
...     (0, 0, 0),
...     (0, 0, 0)
... ]
>>> my_list
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (0, 0, 0), (0, 0, 0), (0, 0, 0)]
>>> while my_list[-1] == (0, 0, 0):
...     my_list.pop()                                                           
... 
(0, 0, 0)
(0, 0, 0)
(0, 0, 0)
>>> my_list
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

Re: Aus einer Liste Endelemente entfernen

Verfasst: Samstag 20. Mai 2017, 22:33
von Alfons Mittelmeyer
@pillmuncher: ja das ist kurz und prägnant. Bei der for Schleife habe ich glatt noch etwas vergessen, nämlich else: break

Re: Aus einer Liste Endelemente entfernen

Verfasst: Samstag 20. Mai 2017, 23:35
von kbr
@pillmuncher: die Lösung ist geradlinig. Als Edgecase müsste noch ein IndexError abgefangen werden.

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 07:07
von snafu

Code: Alles auswählen

def strip_end(sequence, value):
    try:
        stop = sequence.index(value)
    except ValueError:
        stop = None
    return sequence[:stop]

def main():
    my_list = [
        (1,2,3),
        (4,5,6),
        (7,8,9),
        (0,0,0),
        (0,0,0),
        (0,0,0)
    ]
    stripped = strip_end(my_list, (0, 0, 0))
    print(stripped)

if __name__ == '__main__':
    main()

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 08:44
von Alfons Mittelmeyer
@snafu: Deine Lösung funktioniert aber hier nicht:

Code: Alles auswählen

def strip_end(sequence, value):
    try:
        stop = sequence.index(value)
    except ValueError:
        stop = None
    return sequence[:stop]
 
def main():
    my_list = [
        (1,2,3),
        (4,5,6),
        (0,0,0),
        (7,8,9),
        (0,0,0),
        (0,0,0),
        (0,0,0)
    ]
    stripped = strip_end(my_list, (0, 0, 0))
    print(stripped)
 
if __name__ == '__main__':
    main()

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:09
von snafu
Richtig. Für den Fall, den du in Zeile 12 zeigst, funktioniert es nicht. Bisher war ja von Endelementen die Rede. Aber scheinbar ist die tatsächliche Anforderung anders als die zuvor beschriebene Anforderung. Du willst also nicht nur Elemente vom Ende der Liste entfernen, sondern alle Elemente, die dem angegebenen Objekt entsprechen...?

EDIT:
Im Übrigen funktionieren die anderen gezeigten Lösungen für diese neue Anforderung genau so wenig.

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:14
von snafu
Eine Möglichkeit, alle Tuple mit Nullen loszuwerden:

Code: Alles auswählen

list(filter(all, my_list))

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:24
von snafu
Am schnellsten ist übrigens eine LC ohne Funktionsaufrufe:

Code: Alles auswählen

zero_tuple = (0, 0, 0)
[tup for tup in my_list if tup != zero_tuple]

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:28
von Alfons Mittelmeyer
snafu hat geschrieben:Richtig. Für den Fall, den du in Zeile 12 zeigst, funktioniert es nicht. Bisher war ja von Endelementen die Rede. Aber scheinbar ist die tatsächliche Anforderung anders als die zuvor beschriebene Anforderung. Du willst also nicht nur Elemente vom Ende der Liste entfernen, sondern alle Elemente, die dem angegebenen Objekt entsprechen...?
Nein es ist keine neue Anforderung aber deine Lösung funktioniert nicht. Die Lösung soll trailing Elemente am Ende entfernen, vergleichber mit Leerzeichen am Stringende. Deine Lösung kappt aber alles ab dem ersten 'Leerzeichen', das soll sie natürlich nicht.

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:33
von Alfons Mittelmeyer
@pillmuncher: ich lande bei Berücksichtigung von Randbedingungen und unter Berücksichtigung dass man auch pop nicht braucht, doch bei der for Schleife:

Code: Alles auswählen

def strip_end(sequence, value):
    for index in range(len(sequence)-1,-1,-1):
        if sequence[index] != value:
            return sequence[0:index+1]
    return []


def main():
    my_list = [
        (1,2,3),
        (4,5,6),
        (0,0,0),
        (7,8,9),
        (0,0,0),
        (0,0,0),
        (0,0,0)    ]

    stripped = strip_end(my_list,(0,0,0))
    print(stripped)
    stripped = strip_end([(0,0,0)],(0,0,0))
    print(stripped)
    stripped = strip_end([],(0,0,0))
    print(stripped)

if __name__ == '__main__':
    main()

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:33
von snafu
@Alfons:
Sorry, hab meinen Fehler jetzt erkannt. :oops:

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:40
von Alfons Mittelmeyer
@snafu: und nach was suchst Du mit: stop = sequence.index(value)

Nach dem ersten Vorkommen des Elementes das aber hinten als trailing zu entfernen ist. Deine Lösung funktioniert nicht.
Python wird es ja wohl wissen und zeigt: [(1, 2, 3), (4, 5, 6)]

Das war eine Überschneidung mit Deinem letzten Post. Meiner war vor deinem letzten Post gedacht.

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 09:56
von Alfons Mittelmeyer
Unter Berücksichtigung, dass die ursprüngliche Liste abzuschneiden ist und dass man nur in einem Spezialfall solche trailing Elemente hat, ist wohl doch am Besten:

Code: Alles auswählen

def remove_trailing(sequence, value):
    for index in range(len(sequence)-1,-1,-1):
        if sequence[index] != value:
            break
        else:
            sequence.pop()
 
def main():
    my_list = [
        (1,2,3),
        (4,5,6),
        (0,0,0),
        (7,8,9),
        (0,0,0),
        (0,0,0),
        (0,0,0)    ]

    remove_trailing(my_list,(0,0,0))
    print(my_list)
 
if __name__ == '__main__':
    main()

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 10:00
von Sirius3
Am besten schreibt man solche Funktionen so, dass man jedes iterierbare Objekt übergeben kann.

Code: Alles auswählen

def strip_trailing(sequenze, value):
    result = []
    saved = []
    for element in sequenze:
        saved.append(element)
        if element != value:
            result.extend(saved)
            saved = []
    return result
    
def istrip_trailing(sequenze, value):
    saved = []
    for element in sequenze:
        saved.append(element)
        if element != value:
            for el in saved:
                yield el
            saved = []
So fumktioniert das ganze auch, wenn man Leerzeilen am Ende einer Datei ignorieren will:

Code: Alles auswählen

with open("some_file.txt") as lines:
    for line in istrip_trailing(lines, "\n"):
        print line

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 10:05
von snafu
Ohne explizite Schleife ist die Aufgabe mit dem Mitteln von Python wohl nicht sinnvoll zu lösen.

Daher hier als Schleife mit reversed(), die zumindest das unpythonische Holen einzelner Elemente über den Index vermeidet:

Code: Alles auswählen

def strip_end(sequence, value):
    for i, item in enumerate(reversed(sequence)):
        if item != value:
            # `i` might be 0, so don't use negative index
            stop = len(sequence) - i
            return sequence[:stop]
    return sequence
 
def main():
    my_list = [
        (1,2,3),
        (4,5,6),
        (0,0,0),
        (7,8,9),
        (0,0,0),
        (0,0,0),
        (0,0,0)
    ]
    stripped = strip_end(my_list, (0, 0, 0))
    print(stripped)
 
if __name__ == '__main__':
    main()

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 10:15
von kbr
Hier pillmunchers stabilisierte Variante, ich im Sinne der originalen Frage als die pythonischste sehe (und die zudem ziemlich schnell ist):

Code: Alles auswählen

def strip_end(sequence, value):
    try:
        while sequence[-1] != value:
            sequence.pop()
    except IndexError:
        pass
    return sequence
Alternativ ist 'del sequence[-1]' anstelle von .pop() noch marginal flotter.

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 10:27
von snafu
@kbr:
Das löst aber leider das Problem nicht. Schau dir mal genauer an, was in Zeile 3 bei dir passiert.

Re: Aus einer Liste Endelemente entfernen

Verfasst: Sonntag 21. Mai 2017, 10:29
von Sirius3
Das ganze noch etwas schöner, mit groupby

Code: Alles auswählen

from itertools import groupby, chain

def istrip_trailing(sequenze, value):
    saved = []
    for is_value, elements in groupby(sequenze, value.__eq__):
        if is_value:
            saved = list(elements)
        else:
            for element in chain(saved, elements):
                yield element
@kbr: Funktionen, die Listen verändern, sollten diese nicht auch noch als Rückgabewert haben (siehe list.sort vs. sorted)