Rspec - Check if an array have same elements than other, regardless of the order
Solution 1
Here was my wrong matcher (thanks @steenslag):
RSpec::Matchers.define :be_same_array_as do |expected_array|
match do |actual_array|
(actual_array | expected_array) - (actual_array & expected_array) == []
end
end
Other solutions:
use the builtin matcher, best solution
use
Set
:
Something like:
require 'set'
RSpec::Matchers.define :be_same_array_as do |expected_array|
match do |actual_array|
Set.new(actual_array) == Set.new(expected_array)
end
end
Solution 2
There is a match_array
matcher in RSpec which does this:
http://rubydoc.info/github/rspec/rspec-expectations/RSpec/Matchers:match_array
Solution 3
You can use the =~
operator:
[:b, :a, :c].should =~ [:a, :b, :c] # pass
From the docs:
Passes if actual contains all of the expected regardless of order. This works for collections. Pass in multiple args and it will only pass if all args are found in collection.
For RSpec's expect syntax there's match_array
:
expect([:b, :a, :c]).to match_array([:a, :b, :c]) # pass
or contain_exactly
if you're passing single elements:
expect([:b, :a, :c]).to contain_exactly(:a, :b, :c) # pass
Solution 4
I think all answers seems to be pretty old.
Latest matcher is contain_exactly
.
You can simply do -
expect([:b, :a, :c]).to contain_exactly(:a, :b, :c)
Please not that in contain_exactly
we don't pass a whole array, instead pass separate
elements.
Ref - Rspec Guide
pierallard
Updated on July 11, 2022Comments
-
pierallard almost 2 years
I'm not sure if its a Rspec question, but I only encountred this problem on Rspec tests.
I want to check if an array is equal to another array, regardless of the elements order :
[:b, :a, :c] =?= [:a, :b, :c]
My current version :
my_array.length.should == 3 my_array.should include(:a) my_array.should include(:b) my_array.should include(:c)
Is there any method on Rspec, ruby or Rails for do something like this :
my_array.should have_same_elements_than([:a, :b, :c])
Regards
-
pierallard about 11 yearsWhat an elegant solution ! Thanks ! I knew
&
, but you learned me the operator|
. -
apneadiving about 11 yearsdamn, didn't know that, +1
-
steenslag about 11 yearsI didn't downvote, but this fails when there are duplicates: [3,2,1,1] and [1,2,3] returns true.
-
apneadiving about 11 years@steenslag:
([3,2,1,1] | [1,2,3] - [3,2,1,1] & [1,2,3]) => [3, 2, 1]
and([1,2 ,3] | [3, 2, 1, 1] - [1, 2, 3] & [3, 2, 1, 1])=> [1, 2, 3]
this doesn't return true since it's different from[]
-
pierallard about 11 yearsBest solution for many people, but I can't install RSpec > 2.11.
-
apneadiving about 11 years
spec/support/custom_matchers.rb
is where I store this -
steenslag about 11 years@apneadiving In your post you are doing
([3,2,1,1] | [1,2,3]) - ([3,2,1,1] & [1,2,3])
which is different from your comment (result: []) -
steenslag about 11 years@apneadiving [3,2,1,1] and [1,2,3,2] still failing. I guess this is why Array does not have a
^
method like Set . -
apneadiving about 11 years@steenslag: you've made my day :)
-
Nick Messick about 11 yearsThere is no need to go through all this. Just use the builtin operator
=~
like in Stefan's answer. -
Nick Messick about 11 yearsThe answer includes two custom matchers, which is extremely bad practice when a builtin operator is available.
-
apneadiving about 11 years@messick: yes I didn't delete my first anser ubt I wrote
use the builtin matcher, best solution
what else do you need? -
Ulysse BN about 6 years
contain_exactly
does the trick as well, for a splatted array (see stackoverflow.com/a/48206163/6320039) -
Ulysse BN about 6 yearsDownvote because: The answer is very NOT straightforward. Users tend to read quickly, and won't see your best solution part. Plus It is unclear.