3

I am writing a Safari extension, and would like to detect what page (url) I am currently on, and do things accordingly.

Right now my code looks something like:

if (linkHas("facebook.com")) {...}
else if (linkHas("google.com")) {...}
else if (linkHas("yahoo.com")) {...}

where linkHas() is a function that basically returns 1 if it has the parameter, and 0 otherwise.

Is it possible to write this one single switch statement? I've read somewhere that switch statements are implemented as hash tables if there are more than 5 options. For a small script like mine, the speed probably doesn't really matter much here, I'm really just trying it as an exercise.

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Ignoranian
  • 69
  • 5
  • Is it `linkHas("example.com")` verbatim or more like `var url = getCurrentUrl();if(linkHas(url)){...}`? – deamentiaemundi Nov 23 '15 at 23:48
  • The definition of linkHas is: `function linkHas(url) { return window.location.href.indexOf(url) == -1 ? false : true }` or 0 and 1 depending on how you want. – Ignoranian Nov 24 '15 at 00:32
  • Not the definition of the function, how you call it. Do you get the string `"facebook.com"` from somewhere else as in a variable or does your code look like what you have posted wher every call of `linkHas()` gets the URL typed in directly or do you read the URLs from a list in the program? – deamentiaemundi Nov 24 '15 at 00:42
  • URLs are all typed in by myself. – Ignoranian Nov 24 '15 at 00:53
  • Than abandon all hope. You may give Merott's example a try, it's ok for a small list. For longer lists (a couple of hundreds or even thousands) you may overthink the very basics of your method. – deamentiaemundi Nov 24 '15 at 00:57
  • Lol. Are you sure you aren't misunderstanding anything? I don't understand why you say I have to abandon hope. – Ignoranian Nov 24 '15 at 01:11

3 Answers3

6

The simple, and straight answer is no.

The switch expression has to be/return a value, that is evaluated against the value of each case. As JCOC611 has actually demonstrated, it can be done, but I beg you, don't :)

There is probably definitely a better way of implementing what you're after, but that wasn't your question, so I'll leave it at that.

EDIT

In fact, let me show you a better way.

function doFacebookStuff() {
  console.log('Hello Facebook!');
}

function doGoogleStuff() {
  console.log('Hello Google!');
}

function doYahooStuff() {
  console.log('Hello Yahoo!');
}

var domainActions = {
  'facebook.com': doFacebookStuff,
  'google.com': doGoogleStuff,
  'yahoo.com': doYahooStuff
}

var matched = Object.keys(domainActions).filter(function(domain) {
  if (linkHas(domain)) {
    domainActions[domain]();
    return true;
  }
});

console.log('matched domains:', matched);

You can also replace .filter() with .some(), to have it stop at the first match it finds.

Merott
  • 7,189
  • 6
  • 40
  • 52
  • 2
    Actually, the simple and straight answer is **yes**, and the longer answer is _don't do it_. JCOC611's answer shows it is possible, but there really isn't any reason to do it that way. – Mathias-S Nov 23 '15 at 23:40
  • OK, I've updated my answer. JCOC611's answer is quite an achievement actually. I'd have never came up with that! :) – Merott Nov 23 '15 at 23:48
  • That switch statement JCOC611 wrote was amazing, although it looked a bit weird hahah. What is domains.filter? Is that a function available within extensions? Cause it seems undefined on console. – Ignoranian Nov 24 '15 at 00:22
  • @Ignoranian - sorry, `domains` was a typo. It should have been `domainActions` - fixed now. [`#filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) is a native array function in JavaScript, since ES5. – Merott Nov 24 '15 at 00:31
  • It's telling me .filter is undefined, probably because domainActions is an object instead of an array. Doesn't work on any of the major browsers I'm using. A for-in loop works though. – Ignoranian Nov 24 '15 at 01:04
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/96040/discussion-between-merott-and-ignoranian). – Merott Nov 24 '15 at 14:49
5

Edit: Disclaimer: the following is something you should not do, but is there for reference purposes only. Don't let people tell you you can't, but listen when they say you shouldn't.

The very naive way:

switch(true){
    case linkHas("facebook.com"):
        // ...
        break;
    case linkHas("google.com"):
        // ...
        break;
    case linkHas("yahoo.com"):
        // ...
        break;
}

This has absolutely no performance benefits though. In fact, it might be slower if all the functions are evaluated before the equality tests are performed.

The long way would be to refactor linkHas to be getLinkDomain and check on whatever that returns.

In fact, lets do that. Using the convenient function extractDomain, written by lewdev, you can:

var domain = extractDomain(link);
switch(domain){
    case "facebook.com":
        // ...
        break;
    //...
}
Community
  • 1
  • 1
JCOC611
  • 19,111
  • 14
  • 69
  • 90
  • 2
    Well, you *could* do that, but c'mon, that's going to teach some nasty habits to JavaScript newbies. – Merott Nov 23 '15 at 23:44
  • 1
    @Merott made an edit. Hopefully it quenches your angst. – JCOC611 Nov 23 '15 at 23:47
  • Thank you! It's a very clever trick what you did there, but it definitely made me cringe for a moment lol – Merott Nov 23 '15 at 23:49
  • Many thanks!! That is one weird switch statement though hahah. I think I'll go with Merott's solution instead though, because on some sites I check for specific pages and not just the domain. – Ignoranian Nov 24 '15 at 00:19
0
  1. Create an "enumeration" of domains to avoid magic strings.
  2. Create a function "getDomain" that returns one of said enumeration.
  3. Create your own hash table called "actions" using an object literal, keyed on said enumeration values. Hash table values can be functions containing the logic to run for that key.

Then write:

actions[getDomain()]();
Ben Aston
  • 53,718
  • 65
  • 205
  • 331