ChatGPT解决这个技术问题 Extra ChatGPT

Rspec: "array.should == another_array" but without concern for order

I often want to compare arrays and make sure that they contain the same elements, in any order. Is there a concise way to do this in RSpec?

Here are methods that aren't acceptable:

#to_set

For example:

expect(array.to_set).to eq another_array.to_set

or

array.to_set.should == another_array.to_set

This fails when the arrays contain duplicate items.

#sort

For example:

expect(array.sort).to eq another_array.sort

or

array.sort.should == another_array.sort

This fails when the arrays elements don't implement #<=>

Not to smartass, but comparing to_set and size actually doesn't do what you want. E.g. [a, b, b] would match [a, a, b]. Cheers!
For those who stumbled here wondering the opposite: order should be the same. Use the eq matcher, e.g. expect([1, 2]).to_not eq([2, 1])

x
x1a4

Try array.should =~ another_array

The best documentation on this I can find is the code itself, which is here.


This doesn't take the order in account, so this is not an acceptable answer, is it? Quote from the docs: Passes if actual contains all of the expected regardless of order..
Title of this post: "Rspec: “array.should == another_array” but without concern for order"
This is now officially documented under operator matchers
If you're using the new "expect" syntax found in rspec 3.0, see the answer from @JoshKovach.
Rspec 3.0 syntax is expect([1, 2, 3]).to match_array([2, 1, 3]) see: stackoverflow.com/a/19436763/33226
V
Valentin Nemcev

Since RSpec 2.11 you can also use match_array.

array.should match_array(another_array)

Which could be more readable in some cases.

[1, 2, 3].should =~ [2, 3, 1]
# vs
[1, 2, 3].should match_array([2, 3, 1])

yup, just upgraded to rails 4 and =~ stopped working where match_array works fine, thanks!
I dunno if that is more readable. Now it reads like it should be an exact match, but it isn't. The previous squiggle was vague enough to mean nothing for an array, so I didn't have the preconception. Maybe it's just me.
For "exact", you always have eq(), so I guess match_array() is vague enough for me.
This doesn't work for me on ordered lists. It thinks lists, which have the same items in a different order, are the same. :-(
FWIW match_array is an alias for contain_exactly (documentation)
J
Josh Kovach

I've found =~ to be unpredictable and it has failed for no apparent reason. Past 2.14, you should probably use

expect([1, 2, 3]).to match_array([2, 3, 1])

Also valid past 3.0: expect([1, 2, 3]).to contain_exactly(2, 3, 1). relishapp.com/rspec/rspec-expectations/v/3-0/docs/…
n
nicholaides

Use match_array, which takes another array as an argument, or contain_exactly, which takes each element as a separate argument, and is sometimes useful for readability. (docs)

Examples:

expect([1, 2, 3]).to match_array [3, 2, 1]

or

expect([1, 2, 3]).to contain_exactly 3, 2, 1

n
notapatch

For RSpec 3 use contain_exactly:

See https://relishapp.com/rspec/rspec-expectations/v/3-2/docs/built-in-matchers/contain-exactly-matcher for details, but here's an extract:

The contain_exactly matcher provides a way to test arrays against each other in a way that disregards differences in the ordering between the actual and expected array. For example: expect([1, 2, 3]).to contain_exactly(2, 3, 1) # pass expect([:a, :c, :b]).to contain_exactly(:a, :c ) # fail

As others have pointed out, if you want to assert the opposite, that the arrays should match both contents and order, then use eq, ie.:

    expect([1, 2, 3]).to    eq([1, 2, 3]) # pass
    expect([1, 2, 3]).to    eq([2, 3, 1]) # fail

B
Blair Anderson

not documented very well but i added links anyways:

Rspec3 docs

expect(actual).to eq(expected)

Rspec2 docs

expect([1, 2, 3]).to match_array([2, 3, 1])


Both expect(actual).to eq(expected) and expect(actual).to match_array(expected) work in rspec3, but they are doing different things. #match_array ignores the ordering, while #eq does not.
This does not answer the question, since the OP specifically asked to disregard the order.
Yes! This worked for me. A comparison that fails if the order of elements is not the same. Thank you! I refer to the .to eq method, not the match_array .