1
list1 = ["green","red","yellow","purple","Green","blue","blue"]

so I got a list, I want to loop through the list and see if the colour has not been mentioned more than once. If it has it doesn't get append to the new list.

so u should be left with

list2 = ["red","yellow","purple"]

So i've tried this

list1 = ["green","red","yellow","purple","Green","blue","blue","yellow"]
list2 =[]
num = 0
for i in list1:
    if (list1[num]).lower == (list1[i]).lower:
        num +=1
    else:
        list2.append(i)
        num +=1

but i keep getting an error

ylimes
  • 77
  • 1
  • 3
  • 10
  • check this link... http://stackoverflow.com/questions/6764909/python-how-to-remove-all-duplicate-items-from-a-list – techkris Jan 25 '17 at 22:05
  • @techkris: That link wants to preserve a single copy of each unique value, where this question wants to keep only values which appear exactly once. Similar techniques apply, but it's a little more complex in this case. – ShadowRanger Jan 25 '17 at 22:36

5 Answers5

2

Use a Counter: https://docs.python.org/2/library/collections.html#collections.Counter.

from collections import Counter
list1 = ["green","red","yellow","purple","green","blue","blue","yellow"]
list1_counter = Counter([x.lower() for x in list1])
list2 = [x for x in list1 if list1_counter[x.lower()] == 1]

Note that your example is wrong because yellow is present twice.

Mikk
  • 804
  • 8
  • 23
1

The first problem is that for i in list1: iterates over the elements in the list, not indices, so with i you have an element in your hands.

Next there is num which is an index, but you seem to increment it rather the wrong way.

I would suggest that you use the following code:

for i in range(len(list1)):
    unique = True
    for j in range(len(list1)):
        if i != j and list1[i].lower() == list1[j].lower():
            unique = False
            break
    if unique:
        list2.append(list1[i])

How does this work: here i and j are indices, you iterate over the list and with i you iterate over the indices of element you potentially want to add, now you do a test: you check if somewhere in the list you see another element that is equal. If so, you set unique to False and do it for the next element, otherwise you add.

You can also use a for-else construct like @YevhenKuzmovych says:

for i in range(len(list1)):
    for j in range(len(list1)):
        if i != j and list1[i].lower() == list1[j].lower():
            break
    else:
        list2.append(list1[i])

You can make the code more elegant by using an any:

for i in range(len(list1)):
    if not any(i != j and list1[i].lower() == list1[j].lower() for j in range(len(list1))):
        list2.append(list1[i])

Now this is more elegant but still not very efficient. For more efficiency, you can use a Counter:

from collections import Counter

ctr = Counter(x.lower() for x in list1)

Once you have constructed the counter, you look up the amount of times you have seen the element and if it is less than 2, you add it to the list:

from collections import Counter

ctr = Counter(x.lower() for x in list1)

for element in list1:
    if ctr[element.lower()] < 2:
        list2.append(element)

Finally you can now even use list comprehension to make it very elegantly:

from collections import Counter

ctr = Counter(x.lower() for x in list1)

list2 = [element for element in list1 if ctr[element.lower()] < 2]
Community
  • 1
  • 1
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • You can remove `unique` flag. And `if unique:` change to `else:`. Just more pythonic way. – Yevhen Kuzmovych Jan 25 '17 at 22:07
  • Traceback (most recent call last): File "C:/Python27/testing111.py", line 6, in if i != j and list1[i].lower == list[j].lower: TypeError: 'type' object has no attribute '__getitem__' >>> – ylimes Jan 25 '17 at 22:14
  • @YevhenKuzmovych: But as far as I know, the `else` only fires if the `for` had no iteration. The `break` does not trigger the `else`. – Willem Van Onsem Jan 25 '17 at 22:15
  • @ylimes: yeah that was a typo, forgot the `1` in `list1`. – Willem Van Onsem Jan 25 '17 at 22:16
  • @WillemVanOnsem As far as I know, `else` fires if `for` finishes without `break`ing or `return`ing. – Yevhen Kuzmovych Jan 25 '17 at 22:18
  • @YevhenKuzmovych: you are right. I will add that to the answer. – Willem Van Onsem Jan 25 '17 at 22:19
  • also, the .lower isnt working it still appends green – ylimes Jan 25 '17 at 22:20
  • ['green', 'red', 'purple', 'Green'] – ylimes Jan 25 '17 at 22:20
  • @ylimes: if you could at least could do an effort to understand what it is doing instead of copying the code for your homework. You could easily have fixed it yourself: the `lower` was not called. Now it should be fixed. Not easy to work on a smartphone with no internet installed. – Willem Van Onsem Jan 25 '17 at 22:25
  • i don't go school :/ and my "homework" isn't nothing like this this just a small part which was bugging me. But thanks :) – ylimes Jan 25 '17 at 22:30
  • 1
    @ylimes: you're welcome :) But only I sometimes get a bit tired of people only looking for a quick copy-paste. The aim of SO is not to do peoples homework, etc. It is meant to explain what is wrong, why it is wrong, how it can be fixed, and why it works that way. – Willem Van Onsem Jan 25 '17 at 22:34
1

Here's another solution:

list2 = []
for i, element in enumerate(list1):
    if element.lower() not in [e.lower() for e in list1[:i] + list1[i + 1:]]:
        list2.append(element)
Rok Povsic
  • 4,626
  • 5
  • 37
  • 53
1

By combining the built-ins, you can keep only unique items and preserve order of appearance, with just a single empty class definition and a one-liner:

from future_builtins import map  # Only on Python 2 to get generator based map
from collections import Counter, OrderedDict

class OrderedCounter(Counter, OrderedDict):
    pass

list1 = ["green","red","yellow","purple","Green","blue","blue"]

# On Python 2, use .iteritems() to avoid temporary list
list2 = [x for x, cnt in OrderedCounter(map(str.lower, list1)).items() if cnt == 1]

# Result: ['red', 'yellow', 'purple']

You use the Counter feature to count an iterable, while inheriting from OrderedDict preserves key order. All you have to do is filter the results to check and strip the counts, reducing your code complexity significantly.

It also reduces the work to one pass of the original list, with a second pass that does work proportional to the number of unique items in the list, rather than having to perform two passes of the original list (important if duplicates are common and the list is huge).

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
0

Here's yet another solution...

Firstly, before I get started, I think there is a typo... You have "yellow" listed twice and you specified you'd have yellow in the end. So, to that, I will provide two scripts, one that allows the duplicates and one that doesn't.

Original:

list1 = ["green","red","yellow","purple","Green","blue","blue","yellow"]
list2 =[]
num = 0
for i in list1:
    if (list1[num]).lower == (list1[i]).lower:
        num +=1
    else:
        list2.append(i)
        num +=1

Modified (does allow duplicates):

colorlist_1 = ["green","red","yellow","purple","Green","blue","blue","yellow"]
colorlist_2 = []
seek = set()

i = 0
numberOfColors = len(colorlist_1)

while i < numberOfColors:
    if numberOfColors[i].lower() not in seek:
        seek.add(numberOfColors[i].lower())
        colorlist_2.append(numberOfColors[i].lower())

    i+=1

print(colorlist_2)

# prints ["green","red","yellow","purple","blue"]

Modified (does not allow duplicates):

EDIT

Willem Van Onsem's answer is totally applicable and already thorough.

SirJames
  • 387
  • 8
  • 27