Darwin Tech
Darwin Tech

Reputation: 18919

Python loop through list and shorten by one

I have a list:

 mylist = ['apple', 'orange', 'dragon', 'panda']

I want to be able to is loop over the list, do something on each element and then remove the element. I tried this:

for l in mylist:
    print l
    list.remove(l)

but the my output is:

apple
dragon

EDIT

I actually want to be able to do some comparisons in the loop. So basically I want to be able to take each element, one-by-one, remove that element for the list and compare it against all the other elements in the list. The comparison is a little complex so I don't want to use list comprehension. And I want to be reducing the list by one each time until the list is empty and all elements have been compared with each other.

What is the best way to get each element, work with it and remove it without skipping elements in the list?

Any help, much appreciated.

REDIT

Just to make clear - the real point of this is to go through each element, which is a string fragment and match it with other fragments which have overlapping sequences on either end, thereby building up a complete sequence. The element being processed should be removed from the list prior to looping so that it isn't compared with itself, and the list should shrink by 1 element each processing loop.

In the case a better list example would be:

mylist = ['apples and or', 'oranges have', 'in common', 'e nothing in c']

to give:

'apples and oranges have nothing in common'

Apologies for not being clear from the outset, but it was a specific part of this larger problem that I was stuck on.

Upvotes: 0

Views: 6100

Answers (9)

Chris Webster
Chris Webster

Reputation: 918

You can just reverse the list order (if you want to process the items in the original order), then use pop() to get the items and remove them in turn:

my_list = ['apple', 'orange', 'dragon', 'panda']
my_list.reverse()
while my_list:
    print(my_list.pop())

Upvotes: 3

hexparrot
hexparrot

Reputation: 3417

Based on your requirement that you want to "be able to take each element, one-by-one, . . . for the list and compare it against all the other elements in the list", I believe you're best suited to use itertools. Here, without the inefficiency of removing elements from your list, you gain the fool-proof ability to compare every combination to eachother once and only once. Since your spec doesn't seem to provide any use for the deletion (other than achieving the goal of combinations), I feel this works quite nicely.

That said, list comprehensions would be the most python way to approach this, in my opinion, as it does not compromise any capability to do complex comparisons.

import itertools

l = ['apple', 'orange', 'dragon', 'panda']

def yourfunc(a,b):
    pass

for a, b in itertools.combinations_with_replacement(l, 2):
    yourfunc(a,b)

A list comprehension approach would have this code instead:

[yourfunc(a, b) for a,b in itertools.combinations(l, 2)]

EDIT: Based on your additional information, I believe you should reconsider itertools.

import itertools

l =  ['apples and or', 'oranges have', 'in common', 'e nothing in c', 'on, dont you know?']

def find_overlap(a,b):
    for i in xrange(len(a)):
        if a[-i:] == b[0:i]:
            return a + b[i:]
    return ''

def reduce_combinations(fragments):
    matches = []
    for c in itertools.combinations(fragments, 2):
        f = reduce(find_overlap, c[1:], c[0])
        if f: matches.append(f)
    return matches

copy = l
while len(copy) > 1:
    copy = reduce_combinations(copy)

print copy

returns

['apples and oranges have nothing in common, dont you know?']

**EDIT: (again). **This permutation is a practical solution and has the added benefit of--while having more computations than the above solution, will provide all possible technical matches. The problem with the above solution is that it expects exactly one answer, which is evidenced by the while loop. Thus, it is much more efficient, but also potentially returning nothing if more than one answer exists.

import itertools

l =  ['apples and or', 'oranges have', 'in common', 'e nothing in c', 'on, dont you know?']

def find_overlap(a,b):
    for i in xrange(len(a)):
        if a[-i:] == b[0:i]:
            return a + b[i:]
    return ''

matches = []
for c in itertools.combinations(l, 2):
    f = reduce(find_overlap, c[1:], c[0])
    if f: matches.append(f)

for c in itertools.combinations(matches, len(matches)):
    f = reduce(find_overlap, c[1:], c[0])
    if f: print f

Upvotes: 3

Meh
Meh

Reputation: 607

Hmm.. Seeing as you are not able to remove all items this way, even though you iterate through all of them.. try this:

#! C:\python27

list1 = ["apple","pear","falcon","bear"] #define list 1
list2 = [] #define list 2
item2 ="" #define temp item

for item in list1[:]:
    item2 = item+"2" #take current item from list 2 and do something. (Add 2 in my case)
    list2.append(item2) #add modified item to list2
    list1.remove(item)  #remove the un-needed item from list1

print list1 #becomes empty
print list2 #full and with modified items.

Im assuming if you are running a comparison, you can dump an ''if'' clause after ''for'' to run the comparison or something. But that seems to be the way to do it.

Upvotes: 0

senderle
senderle

Reputation: 150987

If forward order doesn't matter, I might do something like this:

l = ['apple', 'orange', 'dragon', 'panda']
while l:
    print l.pop()

Given your edit, an excellent alternative is to use a deque instead of a list.

>>> import collections
>>> l = ['apple', 'orange', 'dragon', 'panda']
>>> d = collections.deque(l)
>>> while d:
    i = d.popleft()
    for j in d:
        if i > j: print (i, j)
... 
('orange', 'dragon')

Using a deque is nice because popping from either end is O(1). Best not to use a deque for random access though, because that's slower than for a list.

On the other hand, since you're iterating over the whole list every time anyway, the asymptotic performance of your code will be O(n ** 2) anyway. So using a list and popping from the beginning with pop(0) is justifiable from an asymptotic point of view (though it will be slower than using a deque by some constant multiple).

But in fact, since your goal seems to be generating combinations, you should consider hexparrot's answer, which is quite elegant -- though performance-wise, it shouldn't be too different from the above deque-based solution, since removing items from a deque is cheap.

Upvotes: 0

Gordon Bailey
Gordon Bailey

Reputation: 3911

Is there any reason you can't simply loop through all of the elements, do something to them and then reset the list to an empty list afterwards? Something like:

for l in my_list:
   print l
my_list = []
# or, if you want to mutate the actual list object, and not just re-assign
# a blank list to my_list
my_list[:] = []

EDIT

Based on your update, what you need to do is use the popping approach that has been mentioned:

while len(my_list):
   item = my_list.pop()
   do_some_complicated_comparisons(item)

if you do care about order, then just pop from the front:

my_list.pop(0)

or reverse the list before looping:

my_list.reverse()

Upvotes: 1

Meh
Meh

Reputation: 607

#! C:\python27

import string


list = ['apple', 'orange', 'dragon', 'panda']

print list
myLength = len(list) -1
print myLength
del list[myLength]
print list

[EDIT]

Heres the code to loop through and find a word which a user input and remove it.

#! C:\python27

import string

whattofind = raw_input("What shall we look for and delete?")
myList = ['apple', 'orange', 'dragon', 'panda']

print myList
for item in myList:
    if whattofind in myList:
        myList.remove(whattofind)
        print myList

Upvotes: 0

Constantinius
Constantinius

Reputation: 35039

You could use the stack operations to achieve that:

while len(mylist):
    myitem = mylist.pop(0)
    # Do something with myitem 
    # ...

Upvotes: 0

g.d.d.c
g.d.d.c

Reputation: 47988

By making a copy:

for l in original[:]:
  print l
  original.remove(l)

Upvotes: 0

Simeon Visser
Simeon Visser

Reputation: 122336

You can't remove elements while iterating over the list. Process the elements and then take care of the list. This is the case in all programming languages, not just Python, because it causes these skipping issues.

As an alternative, you can do list = [] afterwards when you're done with the elements.

Upvotes: 0

Related Questions