标记列表

时间:2016-10-25 20:11:40

标签: python python-3.x

假设我在python中有一个名单列表,如下所示:

names = ['Alice','Bob','Carl','Dave','Bob','Earl','Carl','Frank','Carl']

现在,我想摆脱这个列表中有重复名称的事实,但我不想删除它们。相反,对于在此列表中出现多次的每个名称,我想在该名称后附加一个后缀,其中后缀是名称出现的第n次,同时保留列表的顺序。由于列表中有3个Carls,我希望能够分别将它们称为Carl_1,Carl_2和Carl_3。所以在这种情况下,所需的输出如下:

names = ['Alice','Bob_1','Carl_1','Dave','Bob_2','Earl','Carl_2','Frank','Carl_3']

我可以通过循环遍历列表并修改每个名称(如果需要修改)来完成此操作,例如使用类似下面的代码。

def mark_duplicates(name_list):
    output = []
    duplicates = {}
    for name in name_list:
        if name_list.count(name) = 1:
            output.append(name)
        else:
            if name in duplicates:
                duplicates['name'] += 1
            else:
                duplicates['name'] = 1
            output.append(name + "_" + str(duplicates['name']))
    return output

然而,这是一项很多工作,而且我认为应该不是很难做很多代码。有没有更简单的方法来完成我想做的事情?例如,使用列表推导或像itertools之类的东西?

5 个答案:

答案 0 :(得分:8)

collections.Counter可以帮助减少簿记:

In [106]: out = []

In [107]: fullcount = Counter(names)

In [108]: nc = Counter()

In [109]: for n in names:
     ...:     nc[n] += 1
     ...:     out.append(n if fullcount[n] == 1 else '{}_{}'.format(n, nc[n]))
     ...:

In [110]: out
Out[110]:
['Alice', 'Bob_1', 'Carl_1', 'Dave', 'Bob_2', 'Earl', 'Carl_2', 'Frank', 'Carl_3']

答案 1 :(得分:0)

以下代码应该执行您正在寻找并使用理解的内容:

def get_duplicates(names):
    counts = { k: 0 for k in names }
    output = []
    for name in names:
        if count[name] == 0:
            output.append(name)
            counts[name] += 1
        else:
            output.append("{}_{}".format(name, counts[name]))
            counts[name] += 1
    return output

更新:我修复了我的答案中的代码,以正确返回OP正在寻找的内容。不是最好的方法,但它不需要使用另一个库并使用1 dict理解和1循环。

答案 2 :(得分:0)

如果您不关心初始订单,您可以这样想:

  • 计算每个名字出现的次数
  • 生成一个列表,如果名称只显示一次,我们不会添加任何内容,但如果它出现多次,则会将void lower(zstring s) { for (char *p = s; *p; ++p) *p = std::tolower((unsigned char)*p); } _1 ...添加到第二个随后出场。

这意味着,您可以使用collections.Counter来完成工作:

_2

哪个输出:

import collections

names = ['Alice', 'Bob', 'Carl', 'Dave', 'Bob', 'Earl', 'Carl', 'Frank', 'Carl']

counter = collections.Counter(names)
print("Counter: %s" % counter)

result = []
for name, counts in counter.iteritems():
    result.append(name)
    for i in range(1, counts):
        result.append("%s_%d" % (name, i))
print(result)

如果您要将Counter: Counter({'Carl': 3, 'Bob': 2, 'Earl': 1, 'Frank': 1, 'Alice': 1, 'Dave': 1}) ['Earl', 'Frank', 'Alice', 'Dave', 'Carl', 'Carl_1', 'Carl_2', 'Bob', 'Bob_1'] _1后缀添加到 all 列表中有多个匹配项的名称,但保留仅出现一次的名称没事,你可以这样做:

_2

哪个输出:

import collections

names = ['Alice', 'Bob', 'Carl', 'Dave', 'Bob', 'Earl', 'Carl', 'Frank', 'Carl']

counter = collections.Counter(names)
print("Counter: %s" % counter)

result = []
for name, counts in counter.iteritems():
    if counts == 1:
        result.append(name)
    else:
        for i in range(counts):
            result.append("%s_%d" % (name, i + 1))
print(result)

答案 3 :(得分:0)

如果['Alice', 'Bob', 'Carl', 'Dave', 'Bob_2', 'Earl', 'Carl_2', 'Frank', 'Carl_3']是可接受的输出(第一个没有添加_1的人),那么我建议如下:

counts = {}
def append(name):
    try:
        counts[name] += 1
        return True
    except:
        counts[name] = 1
        return False

def get_duplicates():
    return ['_'.join([name, str(counts[name])]) if append(name) else name for name in names]

这种方法的好处是我一次只能通过names,这就是为什么我不能提前知道是否会出现更多。

为了符合规范,我可以进一步修改追加:

def append(name):
    if names.count(name) != 1:
        try:
            counts[name] += 1
        except:
            counts[name] = 1
        return True
    else:
        return False

将给出预期的结果:

['Alice', 'Bob_1', 'Carl_1', 'Dave', 'Bob_2', 'Earl', 'Carl_2', 'Frank', 'Carl_3']

答案 4 :(得分:0)

另一种使用enumerate的解决方案:

>>> names = ['Alice','Bob','Carl','Dave','Bob','Earl','Carl','Frank','Carl']
>>> processed = []
>>> for n in names:
...     if n not in processed:
...         indices = [i for i,name in enumerate(names) if name == n]
...         if len(indices) > 1:
...             suffix = 1
...             for i in indices:
...                 names[i] = "{}_{}".format(names[i], suffix)
...                 suffix += 1
...     if n.split('_')[0] not in processed:
...         processed.append(n)
...
>>>
>>> names
['Alice', 'Bob_1', 'Carl_1', 'Dave', 'Bob_2', 'Earl', 'Carl_2', 'Frank', 'Carl_3']