Rails 里获取数组中除指定元素外的其他元素,且不修改原数组
当需要获取数组中除了某些值以外的其他元素时,我们可以使用数组的 without
方法,这个方法是 ActiveSupport 给 Array
加上的。
使用
获取数组中除 0
以外的元素,且不改变原有数组:
arr = [1, 2, 3, 4, 0]
arr.without(0) # => [1, 2, 3, 4]
arr # => [1, 2, 3, 4, 0]
实现
通过查看源码我们可以看出,without
是 excluding
方法的别名,所以二者可以互换。而 excluding
通过 Array#-
这个方法实现排除元素且不修改原数组的效果。
# activesupport-7.1.2/lib/active_support/core_ext/array/access.rb
class Array
# ...
# Returns a copy of the Array excluding the specified elements.
#
# ["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
# [ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
#
# Note: This is an optimization of Enumerable#excluding that uses Array#-
# instead of Array#reject for performance reasons.
def excluding(*elements)
self - elements.flatten(1)
end
alias :without :excluding
# ...
end
这里有个比较巧妙的地方,即使用 *elements
接受数组参数,并 flatten
了一层传入的参数 。好处是在对一维数组进行处理时,可以接受两种方式的传参:
-
单个数组里边包含多个需要排除的元素;
-
将需要排除的元素以单个参数的形式传入
即:
[1, 2, 3, 4].without(3, 4) # => [1, 2]
[1, 2, 3, 4].without([3, 4]) # => [1, 2]
缺点是在二维及二维以上的数组时,只能接受单个数组包含多个元素的传参方式:
# unexpected behavior
[[1, 2], [3, 4], [5, 6]].without([1, 2], [3, 4]) # => [[1, 2], [3, 4], [5, 6]]
# correct
[[1, 2], [3, 4], [5, 6]].without([[1, 2], [3, 4]]) # => [[5, 6]]
扩展
Hash 中也定义了 without
方法,不过传入的参数是需要排除的键:
h = {name: "ian", email: "xxx@gmail.com", password: "xxxx"}
h.without(:password) # => {name: "ian", email: "xxx@gmail.com"}
h # => {name: "ian", email: "xxx@gmail.com", password: "xxxx"}
原本 Hash 和 Array 都是使用 ActiveSupport 在 Enumerable
模块扩展中的定义:
module Enumerable
# ...
# Returns a copy of the enumerable excluding the specified elements.
#
# ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
# # => ["David", "Rafael"]
#
# ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
# # => ["David", "Rafael"]
#
# {foo: 1, bar: 2, baz: 3}.excluding :bar
# # => {foo: 1, baz: 3}
def excluding(*elements)
elements.flatten!(1)
reject { |element| elements.include?(element) }
end
alias :without :excluding
# ...
end
后来发现由于使用 Array#reject
+ include?
判断的性能不如直接使用 Array#-
, 所以又为 Array
单独写了一个实现,覆盖了 Enumerable
中的实现
评论