Here's a little trick I picked up while browsing through some plugins I had to edit or extend again
var arr = [ 1, 2, 3, 'foo' ];
// old way
if (arr.indexOf('foo') > -1) {
console.log('"foo" is in "arr"!');
}
// new way
if (~arr.indexOf('foo')) {
console.log('"foo" is in "arr"!');
}
I wondered - how does that work? It's because of this little operator I never used or seen yet:
The Bitwise NOT-Operator ~
It flips all bits of a number, I'm not sure how it exactly works but it seems that -1
is the only case of ending up with a falsy number, or rather a falsy expression: 0
So that means, we can take advantage of it and use it in indexOf (haven't seen any other uses yet though).
// you can also negate the statement, no separate parantheses needed
if (!~dailyRoutine.indexOf('☕️')) {
console.log('Not a life worth living');
}
What do you think about this? I think it sure looks neat and saves up some characters in the code, but it could confuse people who read it afterwards (or look it up like me lol)
Top comments (17)
Unless one will reuse the index in other places, I like to recommend includes instead of
indexOf
. I think it's more readable.if (arr.includes('foo')) { ... }
Lack of IE support is a huge bummer since I still have to support IE11 for all my work projects, but that's definitely more readable
Indeed, Brian. I have to support IE11 too. I believe the polyfill solution that others have mentioned here would help.
Cheers,
It's a neat trick, but 'saving a few characters' is a notoriously bad reason to do something a certain way (unless saving a few characters is the prime motivator in your specific use-case). That's because there is a big difference between 'concise' and 'dense'. The former is somewhat preferable, all things being equal. The latter is to be avoided at all cost (again, unless your specific use-case demands it).
Beyond that, to a degree this is similar to using the following approach to declare a default value:
That serves as a clean way to do that if it's a convention that your team has decided upon.
I mean, if you strike it as bad, it's your convention or set of rules. I can see where you're coming from though and probably many people think so too. Both the bitwise NOT operator for indexOf and your code snippet are based on relying on the truthfulness of an expression.
But I think it's one of JavaScript's most interesting feats that it can work like that. That's the reason I wrote this, because I think it's interesting that this works at all.
It's indeed interesting, no doubt about that :).
It reminds me a bit of the following line of JavaScript, that was making the round a couple of years ago, and which manages to showcase a lot of neat JS with very little code: (source)
Back to the original topic, I wrote my response from the perspective of 'should this be used?', which isn't so much a matter of interestingness as it is one of maintainability. That's not the same as saying it's 'bad', but in any setting where you're not the only contributing developer, code generally has to do better than simply not being bad.
(of course I'm ignoring the already mentioned
String.prototype.includes()
in all this)If someone's interested in why it actually works ( as Patrick should have been 😉) it's because numbers in javascript are represented as 32 bits in two's complement format.
That means that negative numbers have all bits inverted and then get added 1:
1 = 0000 0000 0000 0000 0000 0000 0000 0001
-1 = 1111 1111 1111 1111 1111 1111 1111 1111
10 = 0000 0000 0000 0000 0000 0000 0000 1010
-10 = 1111 1111 1111 1111 1111 1111 1111 0110
So if you apply the bitwise NOT only -1 can become 0 and thus be falsy
facepalm, why would you use something you don't know how it works?
As a professional you must guarantee that your code works in all conditions, and you cannot do that if you don't understand how it works.
This snippet I would consider a bad code, most devs will have a hard time understanding it, and that extra time it takes to read&understand will be added for each developer * each time it reads that code. So basically losing resources (time = money).
You do not use an explicit boolean expression, you let the language to cast (all non-zero elements to
true
), again I think our code must be explicitif (something != 0)
would be better thanif (something)
,for us humans.indexOf
is not efficient, it will do a linear search, that means, in worst case, it will:If you want to do that often you can use other data structures that are more efficient for lookups or if the array is sorted a more optimal search algorithm (ex: binary search).
Please keep the "neat" tricks for hackatons, learning projects and meetups, not for production code. We write code for humans, our peers, and bit operators are for machines, we think in decimal.
As for how it works, it has something to do with how the runtimes store the negative numbers
Thanks for your opinion and the URL, I'm not trying to convince anyone to use this piece of advice/tip or how you wanna call it - everyone has to decide on his/her own if they want to experiment with it or even use it. I'll use it because my projects mostly consist of legacy code from 2002 that is much worse than a little operator combined with a non-efficient, but established method :c)
Do not get me wrong, I commented because I felt that junior devs (that are usually attracted by this kind of posts) should know the other side of the story. You presented the story only for one side so I felt compelled to add the side effects.
Also experimenting is good, I recommend to you and readers:
indexOf(1), indexOf("1")
As for legacy code, too bad they don't evolve over time, as our skills and knowledge growth, our code should too.
As other said,
Array.includes
was added to ECMAScript just becauseindexOf > -1
was very popular. but, they are not fully interchangeable because they do not use the same comparison algorithm: results of indexOf and includes may be different.That is a great little trick.
It is actually mentioned in the MDN documentation for Bitwise NOT
developer.mozilla.org/en-US/docs/W...
I agree that
includes
is more readable, but it still must be polyfilled in various situations.I dig it. But I would say there's a reason that you see (function () {.... }()) in people's written code, and !function() { ... } in minified code; minifiers seem to want to do what you have above without care of performance or readability. Perf is minor for most of these if not iterated over, so that's no big deal. Readability across the team is.
Even using something as seemingly basic (and fundamental interview question on truthy/falseyness) as using !!var, I had several people at my new office not understand what it was used for, that it was just a shortcut, and insisted it was causing problems that it wasn't.
I do enjoy these bit operator shortcuts, though... even if I don't fully understand what they are doing
I'd much rather my code be readable and explicit. Converting -1 to falsy and all other indexes to true seems like more work - just check the index that you're looking for, or use another method like Set.has or Array.includes if that's what you're trying to check. My opinion :)
~~
can be used instead ofMath.floor
and is actually (I believe) faster~~
truncates, which rounds towards 0.Math.floor
rounds towards -∞.Yup, I should have added that small caveat
Oh nice! I didn't know that, cool :o