Swift字典数组去重报错:移除重复元素时程序崩溃求解
解决Swift字典数组去重时的崩溃问题
嗨,我来帮你搞定这个问题!你的代码出现崩溃和去重失效的原因其实很典型,咱们拆解开来看:
为什么你的代码会崩溃?
你在正向遍历数组的同时直接移除元素,这会导致两个核心问题:
- 索引越界:原数组长度是4,循环范围是
0...3。当你移除索引2的元素后,数组长度变成3,此时循环走到i=3时,这个索引已经超出了数组的有效范围(现在最大索引是2),直接触发致命错误。 - 漏删元素:就算没崩溃,移除元素后后面的元素会往前移位。比如第二个"Nyan"原本在索引3,移除索引2的元素后它会移到索引2,但循环已经走到i=3了,就会跳过这个元素,导致去不干净。
另外,你用了as!强制解包name字段,如果数组里某个字典没有name键,或者name不是String类型,也会直接崩溃,这个风险一定要避免。
正确的解决方案
方法1:反向遍历数组移除元素
从后往前遍历,这样移除元素不会影响前面未遍历到的索引,完美避开索引混乱的问题:
var myArray = [ [ "name": "John", "age": 21 ], [ "name": "Bob", "age": 35 ], [ "name": "Nyan", "age": 20 ], [ "name": "Nyan", "age": 20 ] ] // 反向遍历数组,安全移除目标元素 for i in (0..<myArray.count).reversed() { // 用可选绑定替代强制解包,避免崩溃 if let name = myArray[i]["name"] as? String, name == "Nyan" { myArray.remove(at: i) } } print(myArray) // 输出:[["name": "John", "age": 21], ["name": "Bob", "age": 35]]
方法2:用filter方法(更简洁的Swift风格)
Swift的filter方法是处理这类需求的首选,它会返回一个新数组,包含所有符合条件的元素,完全不用手动管理索引:
var myArray = [ [ "name": "John", "age": 21 ], [ "name": "Bob", "age": 35 ], [ "name": "Nyan", "age": 20 ], [ "name": "Nyan", "age": 20 ] ] // 过滤掉所有name为"Nyan"的元素 myArray = myArray.filter { dict in guard let name = dict["name"] as? String else { return true // 如果字典没有name字段,保留该元素;可根据需求改成false移除 } return name != "Nyan" } print(myArray)
扩展:移除所有重复name的元素(不仅仅是"Nyan")
如果你想实现更通用的去重——只要name重复就只保留第一个出现的,可以用Set记录已经见过的name:
var myArray = [ [ "name": "John", "age": 21 ], [ "name": "Bob", "age": 35 ], [ "name": "Nyan", "age": 20 ], [ "name": "Nyan", "age": 20 ] ] var seenNames = Set<String>() myArray = myArray.filter { dict in guard let name = dict["name"] as? String else { return true } // 如果name没出现过,记录并保留;否则过滤掉 if seenNames.contains(name) { return false } else { seenNames.insert(name) return true } } print(myArray) // 输出:[["name": "John", "age": 21], ["name": "Bob", "age": 35], ["name": "Nyan", "age": 20]]
总结一下
- 永远不要在正向遍历可变数组时直接移除元素,这是踩坑重灾区
- 优先使用Swift标准库的
filter这类声明式方法,代码更简洁、可读性更高,还能避免手动操作索引的错误 - 尽量避免
as!强制解包,用可选绑定处理可能的nil类型,提升代码稳定性
内容的提问来源于stack exchange,提问作者Nyan Lin Tun
相关产品推荐
相关产品推荐

