96

If you read the comments at the jQuery inArray page here, there's an interesting declaration:

!!~jQuery.inArray(elm, arr) 

Now, I believe a double-exclamation point will convert the result to type boolean, with the value of true. What I don't understand is what is the use of the tilde (~) operator in all of this?

var arr = ["one", "two", "three"];
if (jQuery.inArray("one", arr) > -1) { alert("Found"); }

Refactoring the if statement:

if (!!~jQuery.inArray("one", arr)) { alert("Found"); }

Breakdown:

jQuery.inArray("one", arr)     // 0
~jQuery.inArray("one", arr)    // -1 (why?)
!~jQuery.inArray("one", arr)   // false
!!~jQuery.inArray("one", arr)  // true

I also noticed that if I put the tilde in front, the result is -2.

~!!~jQuery.inArray("one", arr) // -2

I don't understand the purpose of the tilde here. Can someone please explain it or point me towards a resource?

Salman A
  • 229,425
  • 77
  • 398
  • 489
user717236
  • 4,799
  • 19
  • 61
  • 98
  • 51
    Whoever would write code like that needs to step away from the keyboard. – Kirk Woll Feb 16 '12 at 18:11
  • 12
    @KirkWoll: Why? `~jQuery.inArray()` is actually very useful - possibly even a very good reason why the search functions return `-1` for failure (the only value whose two's complement is falsy). Once you've seen and understood the trick, I feel it is even more readable than `!= -1`. – Amadan Feb 16 '12 at 18:20
  • 9
    @Amadan -- no. Just no. Seriously, I can't believe you're defending `!!~` for *anything*. – Kirk Woll Feb 16 '12 at 18:23
  • 3
    @KirkWoll: *shrug*. In fact I do think `!!~` is an overkill, since JS deals with truthy/falsy values well enough. But a simple `~` as an alternative to `!= -1` for `indexOf` and `inArray` tests, particularly in conditionals, I'd have absolutely no problems with that. – Amadan Feb 16 '12 at 18:29
  • 24
    Problem is, it's just that: A "trick". The main difference between `if (x != -1)` and `if (~x)` to me, is that the former actually expresses what you intend to do. The latter expresses you wanting to do something else entirely ("please convert my 64-bit Number to a 32-bit integer, and check if bitwise NOT of that integer is truthy"), where you just happen to get the desired result in this one case. – JimmiTh Feb 16 '12 at 18:50
  • 1
    @TheKaneda: Depends if you think of the `-1` that the search functions return as a coincidence (which makes `~` an ugly exploit), or as a design decision (which makes `~` supremely suited for that particular test). I don't care that `x` is `-1` - it is as arbitrary to me as your "please...". What I care about is whether something is found or not; and given that the search functions have a consistent API returning `-1` for "not found", `~` is a rather clean way to test for it. When you recognise the `$.indexOf(...) != -1` as an idiom and not as a test for equality to `-1`, `~` is as meaningful. – Amadan Feb 16 '12 at 19:29
  • 2
    Check out http://stackoverflow.com/questions/784929/what-is-the-not-not-operator-in-javascript – Vincent Briglia May 14 '12 at 11:22
  • 2
    @20100 - Read the question... the OP has linked to that same question, and stated that he has read it. – James Allardice May 14 '12 at 11:23
  • 10
    `>= 0` probably wasn't *[leet](http://www.urbandictionary.com/define.php?term=leet)* enough, so the more cryptic `!!~` was used. – Yoshi May 14 '12 at 11:23
  • 2
    The regular person way is [faster for me](http://jsperf.com/cryptic-versus-non-cryptic). – Ry- May 14 '12 at 14:24
  • [JavaScript hipsters...](https://github.com/twitter/bootstrap/issues/3057) – Alex Turpin May 14 '12 at 15:07
  • 2
    @Yoshi, technically speaking, `foo >= 0` and `!!~foo` return differing results. `foo != -1` and `!!~foo` would be equivalent. – zzzzBov May 14 '12 at 16:21
  • @minitech test 2 should be !== -1, not > -1 – octal9 May 14 '12 at 16:31
  • 1
    @octal9: No it shouldn't. `indexOf` will never return `-2` or lower. – Ry- May 14 '12 at 17:49
  • 2
    In case you're thinking that `!!~` is (way) faster than `!= -1`: http://jsperf.com/faster-way-to-check-expr-not-equal-to-minus-one – Salman A May 16 '12 at 12:50
  • possible duplicate of [what does `~` mean in javascript](http://stackoverflow.com/questions/8191531/what-does-mean-in-javascript) – kabuko Sep 11 '12 at 00:15
  • 1
    @TheKaneda Yep, but the trick fixes another "trick". [`inArray` unf doesn't express what it intends to do either](http://forum.jquery.com/topic/inarray). `~` is the way to make `inArray` "return" the boolean its name implies it would in the fewest keystrokes. It's a sort of two wrongs make a right situation, though I'd agree that's not really a justification. [This guy's `isInArray` is a solution](http://blog.jerodsanto.net/2011/01/jquery-is-in-array-boolean-sugar/), as `inArray` can't be changed without breaking existing code. Using ~ *with inArray* makes sense tho in gen a bad idea, perhaps – ruffin Dec 27 '12 at 17:25
  • see also [What does a tilde do when it precedes an expression?](http://stackoverflow.com/q/12299665/1048572) and [What does `!!~` mean in javascript?](http://stackoverflow.com/q/10582286/1048572) – Bergi Jun 02 '15 at 20:44

13 Answers13

122

There's a specfic reason you'll sometimes see ~ applied in front of $.inArray.

Basically,

~$.inArray("foo", bar)

is a shorter way to do

$.inArray("foo", bar) !== -1

$.inArray returns the index of the item in the array if the first argument is found, and it returns -1 if its not found. This means that if you're looking for a boolean of "is this value in the array?", you can't do a boolean comparison, since -1 is a truthy value, and when $.inArray returns 0 (a falsy value), it means its actually found in the first element of the array.

Applying the ~ bitwise operator causes -1 to become 0, and causes 0 to become `-1. Thus, not finding the value in the array and applying the bitwise NOT results in a falsy value (0), and all other values will return non-0 numbers, and will represent a truthy result.

if (~$.inArray("foo", ["foo",2,3])) {
    // Will run
}

And it'll work as intended.

Whymarrh
  • 11,635
  • 13
  • 55
  • 96
Yahel
  • 35,856
  • 22
  • 98
  • 150
105

!!~expr evaluates to false when expr is -1 otherwise true.
It is same as expr != -1, only broken*


It works because JavaScript bitwise operations convert the operands to 32-bit signed integers in two's complement format. Thus !!~-1 is evaluated as follows:

   -1 = 1111 1111 1111 1111 1111 1111 1111 1111b // two's complement representation of -1
  ~-1 = 0000 0000 0000 0000 0000 0000 0000 0000b // ~ is bitwise not (invert all bits)
   !0 = true                                     // ! is logical not (true for falsy)
!true = false                                    // duh

A value other than -1 will have at least one bit set to zero; inverting it will create a truthy value; applying ! operator twice to a truthy value returns boolean true.

When used with .indexOf() and we only want to check if result is -1 or not:

!!~"abc".indexOf("d") // indexOf() returns -1, the expression evaluates to false
!!~"abc".indexOf("a") // indexOf() returns  0, the expression evaluates to true
!!~"abc".indexOf("b") // indexOf() returns  1, the expression evaluates to true

* !!~8589934591 evaluates to false so this abomination cannot be reliably used to test for -1.

Salman A
  • 229,425
  • 77
  • 398
  • 489
  • 1
    In a stable library, I see no issue with using `~foo.indexOf(bar)`, it's not significant savings on characters or performance, but it is a relatively common shorthand in the same way that `foo = foo || {}` is. – zzzzBov May 14 '12 at 13:18
  • 6
    It is is not an issue... not at least until someone else is asked to continue with your code. – Salman A May 14 '12 at 15:32
  • 9
    @zzzzBov to [expand on Salman's](http://stackoverflow.com/questions/10582286/what-does-mean-in-javascript#comment13710140_10582345) comment: [always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.](http://www.codinghorror.com/blog/2008/06/coding-for-violent-psychopaths.html) – ahsteele May 14 '12 at 16:08
  • 1
    @ahsteele, I'm well aware of that rule, however bitwise operators are a part of every programming language that I can think of. I try to program in a way that is readable to someone *who can read code*. I don't stop using features of a language simply because someone else doesn't understand it, otherwise I [wouldn't even be able to use `!!`](http://stackoverflow.com/questions/784929/what-is-the-not-not-operator-in-javascript). – zzzzBov May 14 '12 at 16:18
  • Strictly speaking, `>= 0` doesn't have the same behavior as `!!~`. `!== -1` is closer. – Peter Olson Aug 22 '12 at 23:27
  • Looks really handy if you read from Access databases, as `false` is encoded as -1 and `true` 0 IIRC. – CodeManX Nov 25 '14 at 21:34
  • Great explanation, and I have to agree with this answer: if you're writing code that you plan to share please don't do this. Your coworkers/contributors will thank you. – Tristan Shelton Jan 05 '18 at 15:47
57

The tilde operator isn't actually part of jQuery at all - it's a bitwise NOT operator in JavaScript itself.

See The Great Mystery of the Tilde(~).

You are getting strange numbers in your experiments because you are performing a bitwise logical operation on an integer (which, for all I know, may be stored as two's complement or something like that...)

Two's complement explains how to represent a number in binary. I think I was right.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
p.g.l.hall
  • 1,941
  • 2
  • 15
  • 25
33

~foo.indexOf(bar) is a common shorthand to represent foo.contains(bar) because the contains function doesn't exist.

Typically the cast to boolean is unnecessary due to JavaScript's concept of "falsy" values. In this case it's used to force the output of the function to be true or false.

zzzzBov
  • 157,699
  • 47
  • 307
  • 349
18

jQuery.inArray() returns -1 for "not found", whose complement (~) is 0. Thus, ~jQuery.inArray() returns a falsy value (0) for "not found", and a truthy value (a negative integer) for "found". !! will then formalise the falsy/truthy into real boolean false/true. So, !!~jQuery.inArray() will give true for "found" and false for "not found".

Amadan
  • 169,219
  • 18
  • 195
  • 256
13

The ~ for all 4 bytes int is equal to this formula -(N+1)

SO

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 
Mina Gabriel
  • 17,138
  • 23
  • 89
  • 118
10

The ~ operator is the bitwise complement operator. The integer result from inArray() is either -1, when the element is not found, or some non-negative integer. The bitwise complement of -1 (represented in binary as all 1 bits) is zero. The bitwise-complement of any non-negative integer is always non-zero.

Thus, !!~i will be true when integer "i" is a non-negative integer, and false when "i" is exactly -1.

Note that ~ always coerces its operand to integer; that is, it forces non-integer floating point values to integer, as well as non-numeric values.

Pointy
  • 371,531
  • 55
  • 528
  • 584
10

Tilde is bitwise NOT - it inverts each bit of the value. As a general rule of thumb, if you use ~ on a number, its sign will be inverted, then 1 will be subtracted.

Thus, when you do ~0, you get -1 (0 inverted is -0, subtract 1 is -1).

It's essentially an elaborate, super-micro-optimised way of getting a value that's always Boolean.

Joe
  • 15,062
  • 4
  • 38
  • 77
8

You're right: This code will return false when the indexOf call returns -1; otherwise true.

As you say, it would be much more sensible to use something like

return this.modifiedPaths.indexOf(path) !== -1;
LukeH
  • 242,140
  • 52
  • 350
  • 400
  • 1
    But that's 3 more bytes to send to the client! edit: (just joking by the way, posted my comment and realized it wasn't obvious (which is both sad and silly)) – Wesley Murch May 14 '12 at 11:26
  • @Wesley: That's true, but it only has to be sent to each client *once*, assuming the client will cache the `.js`. Having said that, they could use `>=0` rather than `!==-1` - no extra bytes to send and still more readable than the bit-twiddling version. – LukeH May 14 '12 at 11:30
  • 2
    Who's trolling who here? ;) I guess I took it for granted that writing readable code is better than cryptic pre-optimized code that generates these kind of questions. Just minify later and write readable, understandable code now. – Wesley Murch May 14 '12 at 11:31
  • 2
    Personally I'd say that `> -1` is even more readable, but that's probably very subjective. – Yoshi May 14 '12 at 11:36
6

The ~ operator is the bitwise NOT operator. What this means is that it takes a number in binary form and turns all zeroes into ones and ones into zeroes.

For instance, the number 0 in binary is 0000000, while -1 is 11111111. Likewise, 1 is 00000001 in binary, while -2 is 11111110.

Frxstrem
  • 30,162
  • 8
  • 66
  • 99
3

My guess is that it is there because it's a few characters shorter (which library authors are always after). It also uses operations that only take a few machine cycles when compiled into the native code (as opposed to the comparison to a number.)

I agree with another answer that it's an overkill but perhaps might make sense in a tight loop (requires performance gain estimation, though, otherwise may turn out to be premature optimization.)

Alexander Pavlov
  • 29,538
  • 4
  • 63
  • 88
2

I assume, since it is a bitwise operation, it is the fastest (computationally cheap) way to check whether path appears in modifiedPaths.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
panos2point0
  • 160
  • 1
  • 8
1

As (~(-1)) === 0, so:

!!(~(-1)) === Boolean(~(-1)) === Boolean(0) === false
Engineer
  • 43,887
  • 11
  • 83
  • 90
  • 1
    This maybe accurate, but is it a useful explanation for the questioner? Not at all. If I didn't understand it to start with, a terse answer like this wouldn't help. – Spudley May 14 '12 at 17:37
  • I think this answer makes sense. If you have a mathematic brain, you can clearly see which parts are changing at each step. Is it the _best_ answer to this question? No. But it is useful, I think so! +1 – Taylor Lopez Oct 06 '15 at 15:07