2015-08-27 54 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'] ]