我正在尝试从数组中删除所有 7
:
a = [1,2,3,4,5,7,7,7,7,7,7,7,7,7,9,10,11,12,13]
我做到了:
a.each_with_index do |item, index|
if item == 7
a.delete_at(index)
end
end
a # => [1, 2, 3, 4, 5, 7, 7, 7, 7, 9, 10, 11, 12, 13]
这是怎么发生的?
最佳答案
事实上只有大约一半 (5/9) 的项目消失了,这表明问题是在遍历集合时删除。
迭代将处理索引 1、2、3、4 等。如果您在处理索引 2 时将其删除,则所有后面的索引都会向下移动一个。
因此,当您在下一次迭代中移动到索引 3 时,您将跳过原始索引 3,因为它已向下移动到索引 2。
换句话说,让我们从一个更简单的示例开始,其中包含要删除的两个连续项目:
index | 0 | 1 | 2 | 3 |
value | 1 | 7 | 7 | 9 |
您检查了第一个索引并且值为 1
所以您什么都不做。然后您检查第二个索引并且值为 7
因此您将其删除,给出:
index | 0 | 1 | 2 |
value | 1 | 7 | 9 |
然后您检查第三个 索引并且值为9
,因此您什么都不做。您也已到达终点,因此它停止了。
所以你可以看到你实际上跳过了你想删除的第二个项目,因为你在迭代时移动了一些东西。这不是 Ruby 特有的问题,很多语言都有同样的问题。
一般来说,每对完整的相邻项目只会删除该对中的第一个项目,而单独的项目(后面没有另一个具有相同值的项目)将被正常删除。这就是为什么只有 5/9
的 7
被删除,四对中的每一对和最后一个独立的一对。
删除单个给定值的所有项的正确方法(在 Ruby 中)是使用 array delete方法:
a.delete(7)
您还可以使用 conditional delete对于更复杂的条件,例如删除大于 7
的所有内容:
a.delete_if {|val| val > 7}
而且,如果您真的想自己做(作为一种教育练习),您只需要意识到问题是因为您以正向方式处理数组 - 当您这样做时,超出删除范围的更改可能会导致问题。
如果您找到某种方法以反向方式处理数组,则不会出现此问题。幸运的是,Ruby 有这样一个野兽:
a.to_enum.with_index.reverse_each do |item, index|
该行将以删除不会影响 future 操作的方式处理数组。请注意,如果您正在处理的数据结构不是简单的索引数组,那么在迭代时删除仍然是一个问题。
我仍然保证 delete
和 delete_if
是正确的方法,因为它们已经被嵌入到 Ruby 中,因此不太可能有错误。
关于Ruby 没有循环所有元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38428965/