Replace identical elements in a list without loop

Viewed 673

I'm trying to replace all identical elements in a list with a new string, and also trying to move away from using loops for everything.

# My aim is to turn:
list = ["A", "", "", "D"]
# into:
list = ["A", "???", "???", "D"]
# but without using a for-loop

I started off with variations of comprehensions:

# e.g. 1
['' = "???"(i) for i in list]
# e.g. 2
list = [list[i] .replace '???' if ''(i) for i in range(len(lst))]

Then I tried to employ Python's map function as seen here:

list[:] = map(lambda i: "???", list)
# I couldn't work out where to add the '""' to be replaced.

Finally I butchered a third solution:

list[:] = ["???" if ''(i) else i for i in list]

I feel like I'm moving further from a sensible line of attack, I just want a tidy way to complete a simple task.

5 Answers

You can try this:

list1 = ["A", "", "", "D"]

list2=list(map(lambda x: "???" if not x else x,list1))

print(list2)

Here is a longer version of the above one:

list1 = ["A", "", "", "D"]
def check_string(string):
    if not string:
        return "???"
    return string

list2=list(map(check_string,list1))
print(list2)

Taking advantage of the fact that "" strings are False value, you can then use implicit booleanness and return the value respectively. Output:

['A', '???', '???', 'D']

For concision (if we allow list comprehensions, which are a form of loop). Also, as noted correctly by @ComteHerappait, this is to replace empty strings with '???', consistent with the examples of the question.

>>> [e or '???' for e in l]
['A', '???', '???', 'D']

If instead we focus on replacing duplicate elements, then:

seen = set()
newl = ['???' if e in seen or seen.add(e) else e for e in l]
>>> newl
['A', '', '???', 'D']

Finally, the following replaces all duplicates in a list:

from collections import Counter

c = Counter(l)
newl = [e if c[e] < 2 else '???' for e in l]
>>> newl
['A', '???', '???', 'D']

You could use a list comprehension, but what you'd do is compare each element, and if its a match replace with a different string, otherwise just keep the original element.

>>> data = ["A", "", "", "D"]
>>> ['???' if i == '' else i for i in data]
['A', '???', '???', 'D']

How about this:-

myList = ['A', '', '', 'D']
myMap = map(lambda i: '???' if i == '' else i, myList)
print(list(myMap))

...will result in:-

['A', '???', '???', 'D']

If you want to avoid using loops as the title suggests, one can use np.where instead of list-comprehension, and it's faster for large arrays:

data = np.array(["A", "", "", "D"], dtype='object')
index = np.where(data == '')[0]
data[index] = "???"
data.tolist()

and the result:

['A', '???', '???', 'D']

Speed test

for rep in [1, 10, 100, 1000, 10000]:
    data = ["A", "", "", "D"] * rep
    print(f'array of length {4 * rep}')
    print('np.where:')
    %timeit data2 = np.array(data, dtype='object'); index = np.where(data2 == '')[0]; data2[index] = "???"; data2.tolist()
    print('list-comprehension:')
    %timeit ['???' if i == '' else i for i in data]

and the result:

array of length 4
np.where:
The slowest run took 11.79 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 5: 10.7 µs per loop
list-comprehension:
The slowest run took 5.75 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 5: 487 ns per loop
array of length 40
np.where:
The slowest run took 7.08 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 5: 13 µs per loop
list-comprehension:
100000 loops, best of 5: 2.99 µs per loop
array of length 400
np.where:
The slowest run took 4.83 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 5: 31 µs per loop
list-comprehension:
10000 loops, best of 5: 26 µs per loop
array of length 4000
np.where:
1000 loops, best of 5: 225 µs per loop
list-comprehension:
1000 loops, best of 5: 244 µs per loop
array of length 40000
np.where:
100 loops, best of 5: 2.27 ms per loop
list-comprehension:
100 loops, best of 5: 2.63 ms per loop

for arrays longer than 4000 np.where is faster.

Related