用关键字替换Python列表元素

4

我有一个非唯一字符串列表:

list = ["a", "b", "c", "a", "a", "d", "b"]

我希望用一个整数键替换每个元素,以唯一标识每个字符串:

list = [0, 1, 2, 0, 0, 3, 1]

重要的是它是一个唯一标识符,而不是其数量。

到目前为止,我所想到的方法就是将列表复制到集合中,并使用集合的索引来引用列表。虽然我相信还有更好的方法。


你这里的“字符串”都是单个字符吗?如果是,你可以考虑使用ord函数。集合不支持索引。 - rkersh
不一定,不是的。 - Rachie
1
顺便提一下,不要使用 list 作为变量名,因为它会遮盖内置的 list 类型。这里不会有任何问题,但如果您的脚本稍后尝试使用 list 类型构造列表,则可能会导致神秘的错误。 - PM 2Ring
5个回答

10

这将确保唯一性,并且id从0开始连续:

id_s = {c: i for i, c in enumerate(set(list))}
li = [id_s[c] for c in list]

另外值得注意的是,您不应该将 'list' 作为变量名称,因为它会掩盖内置类型 list


5

这是一个使用 defaultdict 的单遍解决方案:

from collections import defaultdict
seen = defaultdict()
seen.default_factory = lambda: len(seen)  # you could instead bind to seen.__len__

In [11]: [seen[c] for c in list]
Out[11]: [0, 1, 2, 0, 0, 3, 1]

这有点诡计,但值得一提!


另一种方法,由@user2357112在相关的问题/答案中建议, 是使用itertools.count递增。这允许您仅在构造函数中执行此操作:

from itertools import count
seen = defaultdict(count().__next__)  # .next in python 2

这可能更可取,因为default_factory方法不会在全局范围内查找seen。

1
非常聪明,我喜欢它!我从未想过在default_factory中使用那种反身力量。 - user2390182
@schwobaseggl 我猜测这就是属性存在的原因(而不是私有),但我希望它们有一种单一构造函数的方法来实现(并引用self)……感觉有点不太好/老派。 :/ - Andy Hayden
3
itertools.count().next也可以用于default_factory,或者您可以使用seen = defaultdict(lambda: len(seen)),因为创建lambda时不需要存在seen。我更喜欢itertools.count().next而不是lambda: len(seen),因为它在可变操作中间不需要检查字典的状态,但是无论哪个版本都感觉default_factory中有太多的魔法。 - user2357112
@user2357112 我觉得这并不是太神奇,它就是为此而存在的!很烦人的一点是 itertools.count API 在 Python 3 中有所不同(你需要使用 __next__),但我同意 itertools.count 比 len 好得多(虽然两者都是 O(1))。 - Andy Hayden
@codesparkle 如果您这样做,请考虑将其定义为一个函数(这样计数变量就不会泄漏,就像上面的seen变量一样)。 - Andy Hayden
显示剩余2条评论

4
>>> lst = ["a", "b", "c", "a", "a", "d", "b"]
>>> nums = [ord(x) for x in lst]
>>> print(nums)
[97, 98, 99, 97, 97, 100, 98]

4
只有当列表中的每个项都是单个字符时,此方法才有效,而原帖作者已经在评论中表示可能并非如此。 - Rory Daulton
1
在我看来,这也需要一点解释。 - Andy Hayden

2

如果您不挑剔,那么可以使用哈希函数:它返回一个整数。对于相同的字符串,它返回相同的哈希值:

li = ["a", "b", "c", "a", "a", "d", "b"]
li = map(hash, li)                # Turn list of strings into list of ints
li = [hash(item) for item in li]  # Same as above

这个可以工作,假设动态结果是可接受的。不错。 - Chris
3
无效,散列值不能保证唯一。 - user2357112

1
一种功能性方法:

l = ["a", "b", "c", "a", "a", "d", "b", "abc", "def", "abc"]
from itertools import count
from operator import itemgetter

mapped = itemgetter(*l)(dict(zip(l, count())))

您可以使用一个简单的生成器函数:

from itertools import count

def uniq_ident(l):
    cn,d  = count(), {}
    for ele in l:
        if ele not in d:
            c = next(cn)
            d[ele] = c
            yield c
        else:
            yield d[ele]


In [35]: l = ["a", "b", "c", "a", "a", "d", "b"]

In [36]: list(uniq_ident(l))
Out[36]: [0, 1, 2, 0, 0, 3, 1]

尝试使用l = ["\t\t", "c"] - Andy Hayden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接