导读:本期聚焦于小伙伴创作的《如何高效合并包含重复姓名的嵌套列表并累加对应数值》,敬请观看详情。处理业务报表时,原始数据常以“姓名+数值”的嵌套列表形式出现,同一姓名分散在多条记录中。若直接遍历比对,时间复杂度会随数据量上升而急剧劣化。借助哈希表可将查找代价降至常数级:以姓名为键、数值为值,遇重复键则累加。相比双层循环逐次扫描,字典方案在万级记录下耗时可从数秒压缩到毫秒。下文给出常规写法与优化写法的代码对照,并分析内存与可读性的权衡,帮助你在实际脚本中快速落地这类聚合逻辑。

在数据处理脚本里,我们经常会拿到类似[["张三", 10], ["李四", 5], ["张三", 3]]这样的结构:外层是列表,里层每个子列表由一个姓名和一个数值组成。当同一个姓名出现多次时,需求通常不是保留多条记录,而是把数值加起来,得到每个姓名对应的总和。这个问题看似简单,但写法不同,运行效率可能差出几十倍。

如何高效合并包含重复姓名的嵌套列表并累加对应数值

一、最容易想到的双层循环写法

刚接触编程的人往往会用两层循环:外层遍历原始列表,内层去结果列表里找有没有同名的项,有就加,没有就添。这种思路直观,但每次插入或更新都要扫描一遍结果列表,整体时间复杂度是O(n²)。当数据只有几十行时感觉不出来,一旦到了几千、上万行,脚本就会明显卡顿。

下面是一段典型的低效实现。我们用一个result列表保存合并后的数据,每次都靠for循环去比对姓名:

data = [["张三", 10], ["李四", 5], ["张三", 3], ["王五", 8], ["李四", 2]]

result = []
for item in data:
    name = item[0]
    value = item[1]
    found = False
    for r in result:
        if r[0] == name:
            r[1] += value
            found = True
            break
    if not found:
        result.append([name, value])

print(result)
# 输出: [['张三', 13], ['李四', 7], ['王五', 8]]

这段代码逻辑上没有问题,也很容易读懂。但它的内层循环在result变长之后会做大量无用比对。假设有1000个不同姓名、每个重复5次,内层就要执行约5000次线性搜索。如果数据再翻十倍,等待时间就会让人难以接受。

此外,由于result里存的是列表,后续若想按姓名快速取值,仍然得再写循环,不利于把聚合结果接给别的模块使用。

二、用字典将查找降为O(1)

更高效的做法是利用哈希表(在Python里就是dict)。字典的键必须是不可变类型,字符串姓名正好合适。我们让姓名作键,累加值作值,每次遇到新姓名就初始化,遇到旧姓名就直接加。哈希表的查询和插入平均是常量时间,整体复杂度降到O(n),而且代码更短。

下面是用字典改写的版本,最后如果想保持“嵌套列表”的输出形态,再转回列表即可:

data = [["张三", 10], ["李四", 5], ["张三", 3], ["王五", 8], ["李四", 2]]

agg = {}
for name, value in data:
    if name in agg:
        agg[name] += value
    else:
        agg[name] = value

# 若必须返回嵌套列表
result = [[k, v] for k, v in agg.items()]
print(result)
# 输出: [['张三', 13], ['李四', 7], ['王五', 8]]

这段代码的执行过程和前面完全不同:无论数据多大,每次循环只做一次哈希计算,不会随着已处理人数变多而变慢。在万级数据下,它通常能在几毫秒内跑完,而双层循环可能要几秒。

字典方案还有一个附带好处——聚合完的数据天然按姓名建立索引,后面若要根据姓名查总额,直接agg.get("张三")就行,不用再遍历。如果业务里姓名本身可能重复但大小写不同(如"张三"和"张三 "),可以在写入前用strip()和lower()统一清洗,避免漏合。

三、使用defaultdict进一步简化

如果觉得if name in agg的判断有点啰嗦,标准库collections里的defaultdict可以帮我们省掉这个分支。它允许在访问不存在的键时自动给一个默认初值,比如int的默认是0,这样直接加就可以。

示例代码如下,逻辑和手动判断完全一致,但可读性更高:

from collections import defaultdict

data = [["张三", 10], ["李四", 5], ["张三", 3], ["王五", 8], ["李四", 2]]

agg = defaultdict(int)
for name, value in data:
    agg[name] += value

result = [[k, v] for k, v in agg.items()]
print(result)

defaultdict在内部帮你调用了int()来生成初始值0,因此第一次见到"张三"时,agg["张三"]已经是0,再加10就变成10。它并没有比普通字典更快,只是少写了判断,让聚合逻辑更聚焦于“加”这件事本身。

需要注意,defaultdict返回的键顺序在Python 3.7之前不保证与插入一致,不过现在主流版本都保留插入顺序,输出列表的顺序一般和首次出现顺序相同,满足大多数报表要求。

四、处理数值为字符串或带单位的脏数据

真实场景里,嵌套列表中的数值有时不是纯数字,可能是"10元"、"3件"这样的字符串。直接累加会报类型错误。我们可以在循环里先做清洗,把数字部分抽出来转成float或int,再走前面的字典逻辑。

下面用简单正则提取数字,并做容错处理:

import re
from collections import defaultdict

data = [["张三", "10元"], ["李四", "5"], ["张三", "3.5元"], ["王五", "8件"]]

agg = defaultdict(float)
for name, raw in data:
    num_str = re.sub(r"[^0-9.]", "", raw)
    if num_str == "":
        continue
    agg[name] += float(num_str)

result = [[k, v] for k, v in agg.items()]
print(result)
# 输出: [['张三', 13.5], ['李四', 5.0], ['王五', 8.0]]

这种写法在聚合前先归一化,避免脏数据打断主流程。如果某些记录完全无法解析,也可以选择记日志或单独收集,而不是让整个脚本崩溃。

从工程角度看,把“清洗”和“聚合”分开写函数会更清晰:一个函数负责把原始行变成(name, number),另一个只管累加。这样单测时分别验证,比揉在一起好维护。

五、性能与可读性权衡小结

面对“合并包含重复姓名的嵌套列表并累加数值”的需求,核心结论是:别用双层循环做线性搜索,改用哈希表。普通dict手动判断、defaultdict自动初值、先清洗再聚合,都是O(n)思路下的变体。它们在时间上远优于O(n²)写法,在空间上也只是多存了一份键值映射,内存开销可以忽略。

如果你的输入本来就是别的数据源(比如数据库游标、CSV读取器),甚至可以不转成嵌套列表,直接边读边合,进一步省掉中间结构。总之,认准“姓名作键、数值累加”这个模式,就能在绝大多数脚本里高效解决这类重复合并问题。

Pythonnested_listdata_aggregation修改时间:2026-08-03 02:12:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。