2017-05-20 57 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] 
相关问题