首页 > 解决方案 > 获取以大列表开头的第一个值的最有效方法

问题描述

我有一个非常大的列表,其中包含超过 100M 的字符串。该列表的示例如下所示:

l = ['1,1,5.8067',
     '1,2,4.9700',
     '2,2,3.9623',
     '2,3,1.9438',
     '2,7,1.0645', 
     '3,3,8.9331',
     '3,5,2.6772',
     '3,7,3.8107',
     '3,9,7.1008']

我想得到第一个以“3”开头的字符串。

为此,我使用了一个 lambda 迭代器,后跟 next() 来获取第一项:

next(filter(lambda i: i.startswith('3,'), l))
Out[1]: '3,3,8.9331'

考虑到列表的大小,不幸的是,这种策略仍然需要相对较长的时间来完成我必须一遍又一遍地完成的过程。我想知道是否有人能想出一个更快、更有效的方法。我对替代策略持开放态度。

标签: pythonlistlambdastartswith

解决方案


由于您的实际字符串在按制表符拆分字符串后由相对较短的标记(例如301)组成,因此您可以构建一个字典,其中第一个标记的每个可能长度作为键,以便后续查找平均只需O(1)时间复杂。

以相反的顺序使用列表的值构建字典,以便列表中以每个不同字符开头的第一个值将保留在最终字典中:

d = {s[:i + 1]: s for s in reversed(l) for i in range(len(s.split('\t')[0]))}

所以给出:

l = ['301\t301\t51.806763\n', '301\t302\t46.970094\n',
     '301\t303\t39.962393\n', '301\t304\t18.943836\n',
     '301\t305\t11.064584\n', '301\t306\t4.751911\n']

d['3']将返回'301\t301\t51.806763'

如果您只需要整体测试每个第一个令牌,而不是前缀,您可以简单地将第一个令牌作为键:

d = {s.split('\t')[0]: s for s in reversed(l)}

这样d['301']就会返回'301\t301\t51.806763'


推荐阅读