2015-08-27 58 views
3

我有一个名为@results的数组,它只由数组组成。我想通过@results迭代并永久删除任何内部阵列是比给定尺寸小的:每个回路未按预期工作

我的代码:

def check_results limit 
    @results.each_with_index do |result, index| 
     @results.delete_at(index) if result.size < limit 
    end 
    end 

不幸的是,这里的阵列长度小于这个只删除第一项比limit。例如,如果limit = 4@results = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]]然后check_results返回[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]

我不明白为什么会发生这种情况。我使用了错误的循环吗?

+0

你好。我编辑了我的答案,包括_why_您遇到问题。我包含调试语句。请看看它。我也为您的问题提供替代方案/解决方案:-) – onebree

回答

4

你应该这样做,因为delete_at修改数组,如果要删除元素,同时迭代它

@results.reject { |i| i.size < limit } 

上面的代码将排除所有的数组元素,其尺寸更小,你会得到意想不到的行为比limit

+0

我从来没有想过“拒绝”!我喜欢它的用途,所以我会尽快接受你的回答!谢谢! – SoSimple

3

修改@results数组并不是一个好主意,因为这将与外迭代发生冲突。

你应该做的是使用select来构建一个新的数组。

def check_results(limit) 
    @result.select { |result| result.size > limit } 
end 
+0

根据OP的问题描述,它不应​​该是'result.size> = limit'吗? –

+0

是的,这个作品完美。我只是用'=='而不是'>':)谢谢! – SoSimple

2

根据文档,#delete_at返回该索引处的元素。

a = ["ant", "bat", "cat", "dog"] 
a.delete_at(2) #=> "cat" 
a     #=> ["ant", "bat", "dog"] 
a.delete_at(99) #=> nil 

我加了一些调试语句向您展示在每一步发生的,假设极限是4:

@results = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]] 
@results.each_with_index do |r, i| 
    puts "RESULT: #{r.to_s}" 
    puts "INDEX: #{i}" 
    @results.delete_at(i) if r.size < 4 
    puts "ARRAY: #{@results.to_s}" 
end 

RESULT: [1, 1, 1, 1] 
INDEX: 0 
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]] 
RESULT: [1, 1, 1, 1] 
INDEX: 1 
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]] 
RESULT: [1, 1, 1] 
INDEX: 2 
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]] 
# @results == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]] 

正如你可以看到,原本在索引2的元素已被删除。因为在迭代时正在修改@results,所以索引3不再存在,索引2已被分析。这就是为什么你不应该在遍历它时修改一个对象。

理想情况下,您要使用#delete_if。类似于以!结尾的方法,#delete_if将根据块(作为参数)的条件修改数组(不返回结果的副本)。下面是你将如何实现方法:

def check_results(limit) 
    @results.delete_if { |arr| arr.length < limit } 
end 

@results = [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ] 

check_results(2) 
# => @results == [ ['foo', 'bar'], ['bizz', 'bazz'] ] 

如果你不想修改@results,那么我建议类似的方法,#reject。同样,@results将不会被修改,而是返回结果的副本。

def check_results(limit) 
    @results.reject { |arr| arr.length < limit } 
end 

@results = [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ] 

check_results(2) 
# => [ ['foo', 'bar'], ['bizz', 'bazz'] ] 
# => @results == [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ]