'How to find a single value in a nested list (and its position) with minimal iterations?

Say I have the following nested list:

my_list = [
    ['a', 'b', 'c', 'd'],
    ['a', 'c', 'd'],
    ['b', 'c', 'd', 'e'],
    ['a', 'c'],
]

I want to find any values that appear only once in the entire list, and also their position in the parent list. For example, 'e' appears only once, and it's in my_list[2].

The only ways I can think of doing this involve at least three iterations. For example:

for letter in ['a', 'b', 'c', 'd', 'e']:  # Iteration 1
    count = 0
    for child_list in my_list:            # Iteration 2
        if letter in child_list:
            count += 1
    if count == 1:
        print(letter)
        for i in range(len(my_list)):     # Iteration 3
            if letter in my_list[i]:
                print(i)

or:

counter = {}
for i in range(len(my_list)):         # Iteration 1
    for letter in my_list[i]:         # Iteration 2
        if letter not in counter:
            # Store the count and (first) position of each letter
            counter[letter] = [1, i]
        else:
            counter[letter][0] += 1
for letter in counter:                # Iteration 3
    if counter[letter][0] == 1:
        print(letter)
        print(counter[letter][1])

Is there a way of doing this that doesn't require so many iterations?



Solution 1:[1]

Here's a way to do what your question asks using a python set and dict:

my_list = [
    ['a', 'b', 'c', 'd'],
    ['a', 'c', 'd'],
    ['b', 'c', 'd', 'e'],
    ['a', 'c'],
]

visited = set()
singletons = {}
for i, L in enumerate(my_list):
    for v in L:
        if v not in visited:
            visited.add(v)
            singletons[v] = i
        else:
            singletons.pop(v, None)
print(singletons)

Output:

{'e': 2}

Solution 2:[2]

Here is a simple way with a single iteration (i.e. each element is visited only once) that creates a dictionary of {letter: [count, first position]}:

d = {}
for i, lst in enumerate(my_list):
    for item in lst:
        if item in d:
            d[item][0] +=1
        else:
            d[item] = [1, i]

output:

{'a': [3, 0], 'b': [2, 0], 'c': [4, 0], 'd': [3, 0], 'e': [1, 2]}

Solution 3:[3]

Interesting problem, not sure if this is what you are after but here it is:

x = 0
y = 0
output = {}
duplicates = []
while x < len(my_list):
    if y < len(my_list[x]):
        value = my_list[x][y]
        y = y + 1
        if value in output:
            del output[value]
            duplicates.append(value)
        elif not value in output and not value in duplicates:
            output[value] = x
    else:
        y = 0
        x = x + 1

What you get is:

{'e': 2}

It is a single loop but work involved is probably comparable to running two nested loops.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 constantstranger
Solution 2
Solution 3 Jinksy