Convert duplicate items in list to unique items

Viewed 64

I have the following list of duplicate items in list:

x = ['John', "Jill", "Ron", "John", "Jill", "John", "Tom", "Harry", "Harry"]

I want to get the following output:

out = ['John_1', "Jill_1", "Ron_1", "John_2", "Jill_2", "John_3", "Tom_1", "Harry_1", "Harry_2"]

I wrote the following code:

from collections import Counter
def iterate_string_duplicates(x):
    nl = []
    dct = dict(Counter(x))
    for k,v in dct.items():
        for i in list(range(1,v+1)):
            nl.append(k+"_"+str(i))
    return nl

But I get the following output because of using the dictionary approach:

iterate_string_duplicates(x)
output: ['John_1', 'John_2', 'John_3', 'Jill_1', 'Jill_2', 'Ron_1', 'Tom_1', 'Harry_1', 'Harry_2']

I'm looking for an approach where the order of the initial strings won't change.

3 Answers

A list-comprehension idea you could use is associating a counter with each name. Then it only requires a single pass through the iterable.

>>> from collections import defaultdict
>>> from itertools import count
>>> suffix = defaultdict(lambda: count(1))
>>> x = ['John', "Jill", "Ron", "John", "Jill", "John", "Tom", "Harry", "Harry"]
>>> [f"{name}_{next(suffix[name])}" for name in x]
['John_1',
 'Jill_1',
 'Ron_1',
 'John_2',
 'Jill_2',
 'John_3',
 'Tom_1',
 'Harry_1',
 'Harry_2']

Similar to @X Æ A-13's answer, we can use a collections.defaultdict starting at count 1 to determine the next count for a name.

from collections import defaultdict

names = ["John", "Jill", "Ron", "John", "Jill", "John", "Tom", "Harry", "Harry"]

counts = defaultdict(lambda : 1)

result = []
for name in names:
    result.append(f"{name}_{counts[name]}")
    counts[name] += 1

print(result)

Or keep the default 0 and increment before adding to the resultant list:

counts = defaultdict(int)

result = []
for name in names:
    counts[name] += 1
    result.append(f"{name}_{counts[name]}")

Which will reflect the actual count of names in counts, unlike the first solution.

Output:

['John_1', 'Jill_1', 'Ron_1', 'John_2', 'Jill_2', 'John_3', 'Tom_1', 'Harry_1', 'Harry_2']

you can execute the below code using the normal dict() and get the job done -

x = ['John', "Jill", "Ron", "John", "Jill", "John", "Tom", "Harry", "Harry"]
y = dict()
def get_me_number(item):
    if item in y:
        y[item] += 1
        return y[item]
    elif item not in y:
        y[item] = 1
        return 1
res = list(map(lambda x_item: x_item+str(get_me_number(x_item)),x))
print(res)
Related