0

Possible Duplicate:
Modifying list while iterating

I have been given a task to make a code in python that will remove all members that occures more than once in a list and leave 1 copy of it. Condition: it should be case-insesitive

So I've written down the following code:

string = raw_input()
list1 = string.split(",")
low_case_list = list1[:] #for case-insesitive
for i in range(len(low_case_list)):
    low_case_list[i] = low_case_list[i].lower()
for member in low_case_list:
    if(low_case_list.count(member) > 1):
        del list1[low_case_list.index(member)]
        del low_case_list[low_case_list.index(member)]

after the input I get this list: [a,b,c,d,A,B,C,D,a,b,c,d] and after I do the operation on it: [B,D,a,b,c,d]

my question is, why it skips 'B' and 'D' when it removes the members?

Community
  • 1
  • 1

3 Answers3

1

You could try something like this instead:

input = raw_input().split(',')
unique = set([s.lower() for s in input])
result = list(unique)
JeffS
  • 2,647
  • 2
  • 19
  • 24
  • 1
    You don't really need that list comprehension. – Rohit Jain Dec 18 '12 at 18:31
  • You're right, the generator comprehension in your answer is going to be more efficient – JeffS Dec 18 '12 at 18:34
  • @JeffS -- Careful. What do you mean by "more efficient"? In my experience, list-comprehensions actually take less time than generators if they're small and you actually iterate over the whole thing. However, the generator is cleaner to look at and is definitely more efficient with memory. – mgilson Dec 18 '12 at 19:11
1

Why not just convert your list into a set with all elements converted to lower-case, and then back to a list. You can use a generator for converting every element to lowercase.

You can do it like this: -

>>> l = ['a', 'b', 'c', 'A', 'B', 'C', 'a', 'b', 'c']
>>> new_list = list(set(elem.lower() for elem in l))
>>> new_list
['a', 'c', 'b']

Note that, order may be changed because, set does not maintain the order of it's elements.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
1

Try this, should be simple. Given your list li:

lowcase = [elem.lower() for elem in li]
output = []
for el in lowcase:
  if el not in output: output.append(el)
return output # if necessary, otherwise a simple li = output

Or, in a faster and more elegant way, you could replace the whole for loop with:

[output.append(el) for el in lowcase if el not in output]

Your code should be buggy because you refer to the index of the element, but the list changes size during the loop, so indices change too.

EDIT: didn't think about sets, obviously they're the best solution here.

mgilson
  • 300,191
  • 65
  • 633
  • 696
whatyouhide
  • 15,897
  • 9
  • 57
  • 71
  • I wouldn't say that your list comprehension is "more elegant". List-comps for their side-effects are usually to be avoided. However, it is worth pointing out that this is the only answer here which will work for unhashable items. (and the only one which preserves order). – mgilson Dec 18 '12 at 19:13
  • What side effects are you talking about? I use them frequently in my code so are they dangerous? – whatyouhide Dec 18 '12 at 22:30
  • In general, a list-comprehension should be used to build a list and return it. so `foo = [ x for x in ... ]` is good since we're building a list and getting the result with `foo`. You're list comprehension doesn't return anything meaningful (it returns a bunch of `None` values), but has a side-effect of modifying the `output` list. Note that in this case, it's better written as `[el for el in lowcase if el not in output]` – mgilson Dec 18 '12 at 22:35
  • You're right, assigning a list built with list-comp to another variable would have been a better way to go. Anyway I often use list-comp for making changes in lists, is that wrong/bad procedure? – whatyouhide Dec 18 '12 at 22:38
  • I don't think it's very idiomatic ... If you're not going to use the list that the list-comp builds, it's probably best to avoid using a list comp (in my opinion). – mgilson Dec 18 '12 at 22:41
  • what did you mean by "but the list changes size during the loop"? That's why I use .index(member) to find the first index of it and delete that element. It deletes 'A' and 'C' but not 'B' and 'D' –  Dec 19 '12 at 13:59
  • Mmm first `member` is not the same for both lists (in one it could be upper or lowercase, in the other one it's lowercase for sure. Now I can't remember but maybe I was wrong about wrong indeces. – whatyouhide Dec 19 '12 at 17:54
  • @mgilson I use list-comp like C programmers use `i++;`, which returns a value which gets thrown in the wind and often not caught by anyone. – whatyouhide Dec 19 '12 at 17:56