1

I recently was working on a code problem on codewars to find a unique number in a list. My code works, however it is incredibly inefficient. I am not sure why this is the case. Below is my code posted:

I think the problem might be with the fact that I am copying the list every single time I iterate (maybe).

def find_uniq(arr):
    equal_check = 0
    for i in arr:
        arr_new = arr.copy()
        arr_new.remove(i)
        if i not in arr_new:
            equal_check = i
    return equal_check

2 Answers2

2

Use collections.Counter, get the ones with count of 1:

from collections import Counter 

def find_uniq(arr):
    c = Counter(arr)
    return [number for number,count in c.most_common() if count == 1]


print(find_uniq( [1,2,3,4,2,3,4,5,6,4,5,6,7,8,9])) # [1, 7, 8, 9]

This takes about O(2*n) so O(n) as 2 is constant.


collection.defaultdict with int, get the ones with count of 1:

# defaultdict
from collections import Counter , defaultdict

def find_uniq(arr):
    c = defaultdict(int)
    for a in arr:
        c[a] += 1
    return [number for number,count in c.items() if count == 1]


print(find_uniq( [1,2,3,4,2,3,4,5,6,4,5,6,7,8,9])) # [1, 7, 8, 9]

This takes about O(2*n) so O(n) as 2 is constant - it is slighty faster then Counter because of C-optimizations inside the implementation (see f.e. Surprising results with Python timeit: Counter() vs defaultdict() vs dict()).


normal dicts and setdefault or test/add, get the ones with count of 1:

# normal dict - setdefault
def find_uniq(arr):
    c = dict()
    for a in arr:
        c.setdefault(a,0)
        c[a] += 1
    return [number for number,count in c.items() if count == 1]


print(find_uniq( [1,2,3,4,2,3,4,5,6,4,5,6,7,8,9])) # [1, 7, 8, 9]

# normal dict - test and add 
def find_uniq(arr):
    c = dict()
    for a in arr:
        if a in c:
            c[a] += 1
        else:
            c[a] = 1

    return [number for number,count in c.items() if count == 1]


print(find_uniq( [1,2,3,4,2,3,4,5,6,4,5,6,7,8,9])) # [1, 7, 8, 9]

Setdefault creates the defaultvalue every time - it is slower then Counter or defaultdict and faster then using test/add.


itertools.groupby (needs sorted list!), get the ones with count of 1:

from itertools import groupby

def find_uniq(arr):
    return [k for (k,p) in groupby(sorted(arr)) if len(list(p)) == 1]

print(find_uniq( [1,2,3,4,2,3,4,5,6,4,5,6,7,8,9])) # [1, 7, 8, 9]

groupby needs a sorted list, list sort alone is O(n * log n) and in combination this is slower then the other approaches.

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
0

One approach is to use two sets, one to stored those values that are seen once, and those that are more that once, then just find the difference:

data = [1, 2, 3, 4, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9]

first, second = set(), set()
for e in data:
    if e not in first:
        first.add(e)
    else:
        second.add(e)

res = first - second
print(res)

Output

{8, 1, 9, 7}
Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76