12

Possible Duplicate:
Javascript: The prettiest way to compare one value against multiple values
Javascript If statement used to check file extensions not working

In JS I'm trying to check whether an extension ends in "png" "jpg" or "gif". I'm aware this can be done with a switch statement, but I'm wondering if there's a simpler way to throw it all in the if conditional. Like:

    if (aExtensions[i].toLowerCase() == ('jpg' || 'png' || 'gif')) {}

What's the best way to achieve this?

Community
  • 1
  • 1
JVG
  • 20,198
  • 47
  • 132
  • 210

4 Answers4

13

You could use an array like this:

var extensions = ["jpg", "png", "gif"];

if (extensions.indexOf(aExtensions[i].toLowerCase()) > -1) {
    // match
}

In this case, you store the "valid" extensions. Then, you can use the indexOf method of Array to find if any of the items in the array match the specific extension you're looking at - checking for a value that is 0 or greater.

indexOf isn't supported on older browsers, so you'd need to include a polyfill to back it up. There are several solutions for that.

Of course, if you wanted to only use if statements, you could use this format:

var ext = aExtensions[i].toLowerCase();
if (ext == "jpg" || ext == "png" || ext == "gif") {
    // match
}

Another possibility I can think of is a switch statement, like:

var ext = aExtensions[i].toLowerCase();

switch (ext) {
    case "jpg":
    case "png":
    case "gif":
        // match
        break;
    case default:
        // No match
}

I might as well include it (other answers had it first, definitely), but one more I can think of is using a regex, like:

if (/jpg|png|gif/i.test(aExtensions[i])) {
    // match
}

But note that you will never be able to individually get the extensions back, and that's why I would prefer one of the first two options I gave.

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
Ian
  • 50,146
  • 13
  • 101
  • 111
3

What about a regex?

if ( /jpg|png|gif/i.test( aExtensions[i] ) ) {
  ...
}
elclanrs
  • 92,861
  • 21
  • 134
  • 171
2

Yet another way to do this is by using an object literal like this,

if ({'jpg':1,'png':1,'gif':1}[aExtensions[i].toLowerCase()]) {
    // ...
}

This way you're looking to see if the anonymous object {'jpg':1,'png':1,'gif':1} has a property which will be true by the if. 1 meets this, undefined doesn't.


If you're using an object, you could also make use of the in operator (as pointed out by Ian) which gives true if the object has such a key irrelevant of it's value.

if (aExtensions[i].toLowerCase() in {'jpg':0,'png':1,'gif':2}) {
    // ...
}

Note this time how 'jpg' still passes even though 'jpg':0.

Community
  • 1
  • 1
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • Can you explain what the `{'jpg':1,'png':1,'gif':1}` does in this instance? Never seen it before in an if statement. – JVG Dec 19 '12 at 01:30
  • @Jascination does my edit explain it adequately? – Paul S. Dec 19 '12 at 01:31
  • You could also use `if (aExtensions[i].toLowerCase() in {'jpg':"",'png':"",'gif':""})` in that case – Ian Dec 19 '12 at 01:34
  • @Ian Yep, but I'm not sure if `in` works like a loop over the keys (which would be similar to using `indexOf`) or a hashmap. – Paul S. Dec 19 '12 at 01:38
  • @PaulS. I guess it's similar, but it's still very different from `indexOf`. `indexOf` is a value lookup, while `in` is a key lookup. It will have to do the same thing as your original, because we are each providing it a key, and expecting the value back (or in the case of `in` - `true` or `false`). And I did say "you could **also** use" is all – Ian Dec 19 '12 at 01:51
  • @Ian, as I say, I'm not sure how `x in obj` works internally compared to `obj[x]`. My doubt that the same method is used for both comes from the fact you can do `for (x in obj)` which suggests to me that `in` involves some kind of iterator. I'm not saying you can't do it this way too though. In fact, I'll edit it into the answer. – Paul S. Dec 19 '12 at 01:59
  • 1
    @PaulS: The [`in`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/in) [*operator*](http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.7) is entirely separate from and completely unrelated to the [`for-in`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/for...in) [*statement*](http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4). The `in` operator simply returns whether or not the named property exists on the object or in the object's prototype chain. – josh3736 Dec 19 '12 at 02:05
  • @josh3736 thanks for clearing that up for me! – Paul S. Dec 19 '12 at 02:06
  • 1
    Also, +1 as **this is the most performant way to solve this problem**. Iterating over an array is slow; object key lookups are *very* fast. – josh3736 Dec 19 '12 at 02:07
  • @josh3736 Yeah, I had a feeling since `in` is used in two different scenarios, they are two different things – Ian Dec 19 '12 at 02:09
  • @josh3736 And I wouldn't say iterating over an array is slow - you never actually iterate over an array, you just "count" from a certain integer to the array's length. In a normal `for` loop, accessing the array's item at that index is the consuming part. Using `indexOf` has implications for larger arrays though because it has to go through every key (index) and **then** compare the values. At the same time, both `indexOf` and `in` stop as soon as they find a match at least. Using `in` will only check the key, but `if (x in y)` has to use some internal "loop" we don't see – Ian Dec 19 '12 at 02:17
  • @Ian: There's the rub: `in` *doesn't* use a simple internal loop. JavaScript object property lookup was historically implemented as a relatively fast lookup in a dictionary-like data structure, and in modern engines it is [compiled into a hidden class allowing lookup to be a simple pointer operation](https://developers.google.com/v8/design?hl=fr#prop_access), meaning that lookup is near O(1). [Compare](http://jsperf.com/array-vs-key-lookup) this to array iteration (comparing the value of each array item), which is a O(n) operation. – josh3736 Dec 19 '12 at 02:26
  • @Ian the last two comments between you and josh3736 were what I wasn't sure of until his comment above linking to the spec. – Paul S. Dec 19 '12 at 02:31
  • @josh3736 (I meant to write this a long time ago) Sorry, I didn't mean to seem like I wasn't in agreement with you. My main point is that `indexOf` is fundamentally different than `key in obj` because `indexOf` isn't a key lookup...it **has** to go through every item (key), get its value, then compare to that value. All `key in obj` has to do is go through every item (key) and compare to that item. Even as a fast lookup, it has to "go through" every item, as does an array in terms of `indexOf`. Both an array and object are stored the same - key/value, we just don't "see" the keys for arrays. – Ian Jan 18 '13 at 17:18
  • @Ian "`key in obj` has to do is go through every item (key)", the _"has"_ and _"every"_ here is what the discussion is about. It depends on the implementation and modern implementations actually compile keys as a hidden class, then the `in` operator basically asks _"is there a pointer mapping for this key?"_ or _"is hash(key) defined?"_ without having to _"go though"_ each key. – Paul S. Jan 18 '13 at 17:56
  • @PaulS. Right, I understand. But the point is that to answer "is there a pointer mapping for this key?", something has to analyze whatever implementation of a lookup table there is to determine that...which means "go through" each item in its "lookup table" to see if that key exists. With `indexOf`, it goes an extra step and has to compare the value based on the pointer. – Ian Jan 18 '13 at 18:02
  • @Ian: Your understanding of how a key lookup is actually implemented in incorrect. Yes, `indexOf` (and the `for-in` *statement* `for (var key in obj) { ... }`) must iterate over every key in the object. However, the `in` *operator* (as used in `if (key in obj) { ... }`) **does not simply iterate over keys** as `indexOf` must. Lookups are implemented using a very fast mechanism like a [hash lookup](http://en.wikipedia.org/wiki/Hash_table). – josh3736 Jan 18 '13 at 20:22
1

You could use a variable and triple comparison, but that's ugly.

Or you could just use an array and check if the string is contained in it.

if (~ ["jpg", "png", "gif"].indexOf(aExtensions[i].toLowerCase()) …

Yet, for the complicated != -1 comparison and the lack of native indexOf in older IEs, people tend to use regexes:

if (/jpg|png|gif/i.test(aExtensions[i])) …
Bergi
  • 630,263
  • 148
  • 957
  • 1,375