2017-05-20 56 views
0

我想做一個方法來過濾包含正整數和字符串的數組,只留下整數,但奇怪的是我的代碼不工作。過濾包含整數和字符串的數組

def filter_list(l) 
l.map { |items| items.is_a?(Integer) ? items : l.delete(items) } 
end 

filter_list([1,2,'a','b']) 

從理論上講,我的代碼工作,因爲如果我更換itemsl.delete(items)文字爲「真」和「假」整型和字符串被替換正確返回

[true, true, false, false] 

但是,保持items : l.delete(items)它回報我

[1, 2, "a"] or sometimes [1, 2, "b"] 

所以只有一個被刪除。我怎樣才能改變我的方法只返回整數?

+0

爲了在迭代它的同時安全地修改一個集合,你必須知道並理解確切的機制。爲了簡化這一點,Ruby提供了其中 - [Array#delete_if'](http://ruby-doc.org/core-2.4.1/Array.html#method-i-delete_if)和['Array#keep_if '](http://ruby-doc.org/core-2.4.1/Array.html#method-i-keep_if),所以你不必打擾。 – Stefan

回答

2

您的代碼不起作用,因爲您在迭代同一個數組時通過l.delete刪除陣列中的元素。

想象一下,你走過一個有4個元素的數組,同時有人改變了數組中元素的數量及其位置。

我認爲你正在尋找Array#select

array = [1, 2, 'a', 'b'] 
array.select { |ele| ele.is_a?(Integer) } 
#=> [1, 2] 
+0

好的,謝謝,這可能是另一種方式,但爲什麼我的不工作? –

0

.delete(items)回報items其映射到陣列。

在控制檯

arr = [1,2,'a','b'] 
arr.delete('b') 
# => 'b' 
arr 
# => [1, 2, 'a'] 

因此,刪除的作品太多,但它返回刪除的元素,因此不能被映射,因爲.map創造任何值塊11返回的數組。

如果你想使用.delete然後,

def filter_list(l) 
    l.dup.each{ |e| l.delete(e) unless e.is_a?(Integer) } 
    l 
end 

這是低效的,請嘗試

l.select { |items| items.is_a?(Integer) } 
+0

你是什麼意思:.delete(items)返回映射到數組中的項目?爲什麼.delete無法正常工作?謝謝 –

+0

看到更新後的答案,'.map'確實是創建一個'block'返回的數組。 –

+0

喔!也許我已經明白了。如果錯誤,糾正我:閱讀Ruby Doc它說.delete返回它刪除的項目,同時在我的代碼中,.map!正在映射數組以保持塊返回的內容。對?這就是爲什麼'a'儘管被刪除保存在數組中。對?。以及爲什麼b不被返回?也許是因爲是最後一項,地圖不再「映射」了?真是一團糟:P。如果我現在所說的是正確的,我可以使用它。準確地選擇 –

1

你的問題是,你正在改變陣列您是map平,如果你的產品不一個整數。

當您的map首次訪問元素'a'時,它調用l.delete('a')(返回'a')。這意味着'l'不再是您的初始數組,即您的map結束。您的數組l現在不包含[1, 2, 'a', 'b'],現在包含[1, 2, 'b']

我不知道確切的實施map但我想,現在的陣列改變,包含的只是三個而不是四個要素,map不看你的數組中的最後'b'。 我們假設你有一個數組a = [1, 2, 'a', 'b']

您第一次致電filter_list(a)時,它會返回[1, 2, 'a']。如果您現在打印a,您將獲得您更改的陣列的內容:[1, 2, 'b']。爲什麼這與你的函數調用的結果不同?因爲map會創建一個包含map返回值的新數組(並且delete返回刪除的值,第一次調用的結果將爲'a')。但由於'a'已從我們的陣列a中刪除,我們陣列的結果爲[1, 2, 'b']

下次撥打電話filter_list(a)map s超過[1, 2, 'b']。這導致第二次致電delete,其返回'b'。因此map返回一個包含[1, 2, 'b']的新數組。在第二次調用filter_list後,您再次更改了輸入數組,因此我們的數組a現在僅包含[1, 2]

如果您再次致電filter_list(a),您將得到[1, 2],因此a將不會再次更改,因爲數組中沒有字符串需要刪除。

您應該時刻注意改變您的輸入數據,因爲它可能有(並且在這種情況下)具有意想不到的副作用。

一個更好的選擇,以你的實現是使用Ruby的select方法(即過濾法)(https://ruby-doc.org/core-2.4.0/Array.html#method-i-select):

a = [1, 2, 'a', 'b'] 

a.select { |element| element.is_a?(Integer) } 
1

試試這個:

a = [1, 2, 3.4, 'a', 'b', [:c], { d: 1 }] 

a.grep Integer #=> [1, 2] 
a.grep Float #=> [3.4] 
a.grep Numeric #=> [1, 2, 3.4] 
a.grep String #=> ['a', 'b'] 
a.grep Array #=> [[:c]] 
a.grep Hash  #=> [{ :d => 1 }] 

Enumerable#grep

0

grep如果您想要某個特定類的實例,則是正確的答案。

如果你想通過班族元素,你可以寫:

strings, integers = [1,2,'a','b'].group_by(&:class).values_at(String, Fixnum) 
strings 
# => ["a", "b"] 
integers 
# => [1, 2] 
0

如果你仍然想使用你寫的代碼,你可以做這樣的事情:

def filter_list(l) 
    i = l.length 
    while i > 0 
    l.map { |items| items.is_a?(Integer) ? items : l.delete(items) } 
    i -= 1 
    end 
    l 
end 

filter_list([1, 2, 'a', 'b', 3, 'duh', 'isitover?']) # will return [1, 2, 3] 
相關問題