我试图7从数组中删除所有s:
a = [1,2,3,4,5,7,7,7,7,7,7,7,7,7,9,10,11,12,13]
Run Code Online (Sandbox Code Playgroud)
我做了:
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]
Run Code Online (Sandbox Code Playgroud)
这怎么发生的?
只有大约一半(5/9)的项目消失的事实是一个死的赠品,问题是在迭代集合时删除.
迭代将处理索引1,2,3,4等.如果在处理索引2时将其删除,则会将所有后面的索引向下移动一个.
因此,当您在下一次迭代中转到索引3时,您将跳过原始索引3,因为它将向下移动到索引2.
换句话说,让我们从一个更简单的例子开始,要删除两个连续项:
index | 0 | 1 | 2 | 3 |
value | 1 | 7 | 7 | 9 |
Run Code Online (Sandbox Code Playgroud)
你检查第一个索引,值是1这样你什么都不做.然后检查第二个索引,值是7这样你删除它,给出:
index | 0 | 1 | 2 |
value | 1 | 7 | 9 |
Run Code Online (Sandbox Code Playgroud)
然后检查第三个索引,值是9这样你什么都不做.你也到了最后,所以它停止了.
所以你可以看到你实际上跳过了你想要删除的第二个项目,因为你在迭代时改变了一些东西.这不是特定于Ruby的问题,许多语言都有同样的问题.
通常,每个完整的相邻项目对将仅删除该对中的第一个,而一个项目自身(不跟随另一个相同的值)将被正常删除.这就是为什么只有5/9你的7s被删除,四个对中的每个对和最后一个对.
删除单个给定值的所有项的正确方法(在Ruby中)是使用数组删除方法:
a.delete(7)
Run Code Online (Sandbox Code Playgroud)
您还可以对更复杂的条件使用条件删除,例如删除大于以下的所有内容7:
a.delete_if {|val| val > 7}
Run Code Online (Sandbox Code Playgroud)
而且,如果你真的想自己做(作为一种教育练习),你只需要意识到问题是因为你以前进的方式处理数组 - 当你这样做时,更改超出你删除的地方可能会导致的问题.
如果您要找到一些以反向方式处理数组的方法,则不会发生此问题.幸运的是,Ruby有这样一个野兽:
a.to_enum.with_index.reverse_each do |item, index|
Run Code Online (Sandbox Code Playgroud)
该行将以删除不会影响未来操作的方式处理数组.请注意,如果您正在处理的数据结构不是简单的索引数组,则在迭代时删除仍然是个问题.
我还保证delete,并delete_if有去,因为他们正在烤成的Ruby已经以正确的方式,因此令人难以置信不太可能有错误.