假设我在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之类的东西?
答案 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']