0

I have a list of items like this:

['T1','T2','T2','T2','T2','T3','T3' ]

I need to make sure that duplicates are renamed with a progressive letter added like this:

['T1','T2A','T2B','T2C','T2D','T3A','T3B']

but only if there is more than 1 occurrence of the same item.

Also, is it possible to do so without generating a new list?

Any ideas?

yurisich
  • 6,991
  • 7
  • 42
  • 63
  • 1
    Is there anything you have done to try to solve this problem? We will be more willing to answer your question if you tell us what you have tried so far. (Helpful links for asking better questions: [ask], [FAQ]) – tckmn Jun 19 '13 at 22:30
  • 1
    What if you had ['T1','T2','T2','T2','T2','T3','T3','T2A'] in input ? – SylvainD Jun 19 '13 at 22:30
  • Items are always sorted? – Ashwini Chaudhary Jun 19 '13 at 22:35
  • no the order of items does not matter. Just the number of occurrences. – Ariel Salvo Caliban Jun 19 '13 at 22:37
  • @ArielSalvoCaliban Welcome to StackOverflow. Whilst you will find the community happy to help with specific programming problems, they won't do all of your work for you. Please improve this question by making an attempt at the problem and sharing your code. – marko Jun 19 '13 at 22:48
  • @Marko, thanks for the welcome. I was really far from having an idea of how to solve the issue. The only thing I was sure of was that I needed to use import collections. Now I will have to study all the answers to understand them, my python is sadly not that good. – Ariel Salvo Caliban Jun 19 '13 at 22:52

3 Answers3

3
from collections import Counter
from string import ascii_uppercase as letters

def gen(L):
    c = Counter(L)
    for elt, count in c.items():
        if count == 1:
            yield elt
        else:
            for letter in letters[:count]:
                yield elt + letter

Now:

>>> L = ['T1','T2','T2','T2','T2','T3','T3']
>>> list(gen(L))
['T2A', 'T2B', 'T2C', 'T2D', 'T3A', 'T3B', 'T1']
michaelmeyer
  • 7,985
  • 7
  • 30
  • 36
  • I the simplicity of this. If I get its logic, you are creating a counter and you feed the list into it. Then you check if the occurrence of each element is just once, if it is then you return that element , otherwise you return the element plus a letter which has the same order as the number of occurrences. – Ariel Salvo Caliban Jun 19 '13 at 22:57
  • The passage `for letter in letters[:count]`? This means: take as many letters in the alphabet as there are occurrences of the element in the list, and, for each of them, output the element plus the letter. – michaelmeyer Jun 19 '13 at 23:02
  • I like the elegance of this logic, however it reorders the list. I would rather see the output as ['T1', 'T2A', 'T2B', 'T2C', 'T2D', 'T3A', T3B']. Is this possible? – panofish Jun 04 '15 at 16:58
0

Considering the list is sorted, this will modify the list in-place. If the list is not sorted then you can sort it first using lis.sort():

>>> from string import ascii_uppercase
>>> from itertools import groupby
>>> from collections import Counter
>>> lis = ['T1', 'T2', 'T2', 'T2', 'T2', 'T3', 'T3']
>>> c = Counter(lis)
>>> for k, v in groupby(enumerate(lis), key = lambda x:x[1]):
    l = list(v)
    if c[k] > 1:
        for x, y in zip(l, ascii_uppercase):
            lis[x[0]] = x[1] + y
...             
>>> lis
['T1', 'T2A', 'T2B', 'T2C', 'T2D', 'T3A', 'T3B']
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • This logic requires that the list be sorted. I would like to see a variation where the list does not need to be sorted. – panofish Jun 04 '15 at 17:01
0
def fix(L):
    d = {}
    for i in xrange(len(L)):
        d[L[i]] = d.get(L[i],0)+1
        if d[L[i]] > 1:
            if d[L[i]] == 2: L[L.index(L[i])] += 'A'
            L[i] += chr(64+d[L[i]])