我将Rails 1.2.3与Ruby 1.8.6结合使用,并且需要确定两个数组是否具有相同的元素,而不管它们的顺序是否相同。保证其中一个数组不包含重复项(另一个可能,在这种情况下答案为否)。
我的第一个念头是
require 'set'
a.to_set == b.to_set
但我想知道是否有更有效或惯用的方式来做到这一点。
我将Rails 1.2.3与Ruby 1.8.6结合使用,并且需要确定两个数组是否具有相同的元素,而不管它们的顺序是否相同。保证其中一个数组不包含重复项(另一个可能,在这种情况下答案为否)。
我的第一个念头是
require 'set'
a.to_set == b.to_set
但我想知道是否有更有效或惯用的方式来做到这一点。
[1,2]
和[2,1,1]
有相同的元素)?
Answers:
对于两个数组A和B:如果满足以下条件,则A和B的内容相同:
(A-B).blank? and (B-A).blank?
或者您可以只检查:
((A-B) + (B-A)).blank?
同样如@ cort3z所建议的,此解决方案als0适用于多态数组,即
A = [1 , "string", [1,2,3]]
B = [[1,2,3] , "string", 1]
(A-B).blank? and (B-A).blank? => true
# while A.uniq.sort == B.uniq.sort will throw error `ArgumentError: comparison of Fixnum with String failed`
::::::::::::编辑::::::::::::::
正如评论中所建议的那样,上述解决方案对于重复项将失败。他在检查之前使用.uniq运算符的正确答案,也掩盖了重复项。)但是,即使您对重复项感兴趣,只需添加计数检查即可解决该问题(根据问题,只有一个数组可以包含重复项)。因此,最终的解决方案将是:
A.size == B.size and ((A-B) + (B-A)).blank?
a.to_set == b.to_set
并且接受的答案正在被使用,a.uniq.sort == b.uniq.sort
并且给出的结果与((A-B) + (B-A)).blank?
A = [1] 完全相同并且B = [1,1]同意吗?由于他只是想对原始解决方案进行改进,因此我的原始解决方案仍然有效:)。同意?
A = [123, "test", [], some_object, nil]
和B = A#because I am lazy
,然后A.uniq.sort
将引发错误(字符串和数组比较失败)。
A = [1, 1, 2]
以及B = [1, 2, 2]
速度比较
require 'benchmark/ips'
require 'set'
a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 4, 5, 6]
Benchmark.ips do |x|
x.report('sort') { a.sort == b.sort }
x.report('sort!') { a.sort! == b.sort! }
x.report('to_set') { a.to_set == b.to_set }
x.report('minus') { ((a - b) + (b - a)).empty? }
end
Warming up --------------------------------------
sort 88.338k i/100ms
sort! 118.207k i/100ms
to_set 19.339k i/100ms
minus 67.971k i/100ms
Calculating -------------------------------------
sort 1.062M (± 0.9%) i/s - 5.389M in 5.075109s
sort! 1.542M (± 1.2%) i/s - 7.802M in 5.061364s
to_set 200.302k (± 2.1%) i/s - 1.006M in 5.022793s
minus 783.106k (± 1.5%) i/s - 3.942M in 5.035311s
sort
速度
to_set
使用足够大的数组开始表现
Ruby 2.6以上
Ruby difference
在2.6中引入。
这提供了一个非常快速,易读的解决方案,如下所示:
a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3, 4, 5, 6]
a.difference(b).any?
# => false
a.difference(b.reverse).any?
# => false
a = [1, 2, 3, 4, 5, 6]
b = [1, 2, 3]
a.difference(b).any?
# => true
运行基准测试:
a = Array.new(1000) { rand(100) }
b = Array.new(1000) { rand(100) }
Benchmark.ips do |x|
x.report('sort') { a.sort == b.sort }
x.report('sort!') { a.sort! == b.sort! }
x.report('to_set') { a.to_set == b.to_set }
x.report('minus') { ((a - b) + (b - a)).empty? }
x.report('difference') { a.difference(b).any? }
end
sort 13.908k (± 2.6%) i/s - 69.513k in 5.001443s
sort! 14.656k (± 3.0%) i/s - 73.736k in 5.035744s
to_set 5.125k (± 2.9%) i/s - 26.023k in 5.082083s
minus 16.398k (± 2.2%) i/s - 83.181k in 5.074938s
difference 27.839k (± 5.2%) i/s - 141.048k in 5.080706s
希望能对某人有所帮助!
如果您期望[:a, :b] != [:a, :a, :b]
to_set
不起作用。您可以改用频率:
class Array
def frequency
p = Hash.new(0)
each{ |v| p[v] += 1 }
p
end
end
[:a, :b].frequency == [:a, :a, :b].frequency #=> false
[:a, :b].frequency == [:b, :a].frequency #=> true
a.sort == b.sort
在乎频率呢?
["", :b].frequency == [:b, ""].frequency #=> true
a.group_by{|i| i} == b.group_by{|i| i}
如果您知道数组的长度相等,并且两个数组都不包含重复项,那么也可以这样做:
( array1 & array2 ) == array1
说明:&
在这种情况下,运算符将返回a1的副本,但不包含a2中未找到的任何项目,这与原始a1相同,前提是两个数组的内容相同且没有重复。
分析:考虑到顺序不变,我猜想这是作为两次迭代实现的,因此始终如一O(n*n)
,特别是对于大型数组,a1.sort == a2.sort
这比在最坏情况下应该执行的效果更糟O(n*logn)
。
a1 = [1,2,3], a2 = [2, 1, 3]
a1 && a2
回报[2,1,3]
给我的不等于a1
a1==a2
什么时候才有效吗?如果array1
将等式右边的替换为array2
,则可能会起作用,但是我怀疑由返回的元素的顺序&
是否得到保证。
&
是数组的交集运算符,&&
逻辑与-它们是非常不同的!
结合起来&
,size
可能也会很快。
require 'benchmark/ips'
require 'set'
Benchmark.ips do |x|
x.report('sort') { a.sort == b.sort }
x.report('sort!') { a.sort! == b.sort! }
x.report('to_set') { a.to_set == b.to_set }
x.report('minus') { ((a - b) + (b - a)).empty? }
x.report('&.size') { a.size == b.size && (a & b).size == a.size }
end
Calculating -------------------------------------
sort 896.094k (±11.4%) i/s - 4.458M in 5.056163s
sort! 1.237M (± 4.5%) i/s - 6.261M in 5.071796s
to_set 224.564k (± 6.3%) i/s - 1.132M in 5.064753s
minus 2.230M (± 7.0%) i/s - 11.171M in 5.038655s
&.size 2.829M (± 5.4%) i/s - 14.125M in 5.010414s