-
-
Notifications
You must be signed in to change notification settings - Fork 769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Array matchers #1208
Array matchers #1208
Conversation
1 similar comment
This is great. Just want to have it browsed by someone else than me :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent work. Thank you!
I have a few small comments and questions.
it("matches arrays with the exact same elements", function () { | ||
var deepEquals = sinonMatch.array.deepEquals([1, 2, 3]); | ||
assert(deepEquals.test([1, 2, 3])); | ||
assert(!deepEquals.test([1, 2])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The negation here is easy to overlook. Could you change these to assert.isFalse
throughout?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course, that would be much more clear and explicit indeed.
Thanks for the feedback!
return match(function (actual) { | ||
// Comparing lengths is the fastest way to spot a difference before iterating through every item | ||
var sameLength = actual.length === expectation.length; | ||
return sameLength && Array.prototype.every.call(actual, function (element, index) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason you didn't use actual.every(....)
? Same question for all the array methods here (indexOf
, toString
, etc').
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometimes people end up creating iterables without those methods, so if we just used actual.every
or other methods directly on actual
they might fail due to the lack of them being implemented. However they would still work when called this way. An example for this would be arguments
. In order to make it an array and call these methods on it we need to use Array.prototype.slice.call
before, since it doesn't have the other methods implemented.
Also, I think this approach is more functional and explicit, but this second reason is just a matter of personal opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, I was just thinking if we should act upon those iterables as if they were arrays, after all this matcher has the word array
in it.
Please let me know what you think, I can change this without problems if you want me to 😄
Also, thank you both for the feedback, it's always good to have such a careful review. I usually learn a lot with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, you could still cache the references to the functions to make it a bit less verbose.
const proto = Array.prototype;
const every = proto.every;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Fatso you're right!
I've just seen you doing that on the behavior
module for push
and it looks much better.
I think I'll start doing this too the next time I have to call Array
methods like this. Thanks for the tip, I'm gonna update this PR as soon as I get home 😄
0bd2c71
to
363e3e4
Compare
Hi friends! Thanks for the review, your considerations were great, specially the idea of assigning Let me know if I missed something and I'll happily fix it, thanks again for reviewing 😄 |
@lucasfcosta Thank you for the contribution. |
Purpose (TL;DR)
This adds the following array matchers as indicated by @mroderick at #1113:
The
equals
matcher has also been renamed todeepEquals
, because as @fearphage suggested thedeepEquals
name is more explicit since we're not doing astrict
comparison between array elements.Solution
I added a new matcher for each one of the suggested items and tests for each one of them too.
Here go a few considerations about the implementation:
length
check to avoid iterating through array items in case they don't have the same length. This makes it faster to spot differences in most cases and also allows me to iterate through every item inactual
later to finish the checkactual
array. I've added a comment to make that clear but let me know if you think that comment is unnecessaryindexOf
call to find items into theactual
array. I thought about mapping theactual
array's items using an object (O(n)
) in order to traverse it only once since we would then be able to find each element by anO(1)
factor. Since we would also have to traverse the wholeexpectation
array I believe that would beO(n * m)
wheren
isactual
's length andm
isexpectation
's length. Let me know if I said anything wrong or if I'm overthinking. I avoided that solution because I was not sure it would be too much over engineering.message
for each one of the new matchers I've just used<nameOfMatcher>([x,y,z])
, please let me know if you don't like that output format.Array.prototype
's methods by usingcall
to avoid incompatibilities with fake arrays or overwritten methodsHow to verify
npm install
npm run test
will run the tests I added totest/match-test.js
.Please let me know if I missed anything or if you disagree with any of these changes and I'll be more than happy to fix this PR.
Thanks everyone for reading this. You rock, thanks for maintaining this awesome project ❤️