views:

203

answers:

4

This is my code:


/**********************************************************
 * remove non-standard characters to give a valid html id *
 **********************************************************/
function htmlid(s) {
 return s.gsub(/[^A-Z^a-z^0-9^\-^_^:^\.]/, ".");
}

Why does jslint throw this error?

Lint at line 5 character 25: Unescaped '^'.
return s.gsub(/[^A-Z^a-z^0-9^\-^_^:^\.]/, ".");
+2  A: 

If what you intend to have is a negated character class, then this is what you want:

return s.gsub(/[^A-Za-z0-9_:.-]/, ".");
J-P
You have to escape the dot: s.gsub(/[^A-Za-z0-9-_:\.]/, ".");
Vincent
@Vincent. No you have not, the dot has no special meaning in a character class. But you have to escape the dash, or put it at the end.
Tomalak
@Tomalak Ouch, didn't know that. +1
Vincent
+3  A: 

Apart from the obvious change to the regex, I recommend the following change to the function itself:

function htmlid(s) {
  // prevents duplicate IDs by remembering all IDs created (+ a counter)
  var self = arguments.callee;
  if (!self.cache) self.cache = {};

  var id = s.replace(/[^A-Za-z0-9_:.-]/, "."); // note the dash is at the end!
  if (id in self.cache) id += self.cache[id]++;
  self.cache[id] = 0;

  return id;
}
Tomalak
+1. I added an answer that gets rid of arguments.callee. If you want to copy it into your answer as an alternative sollution please do so and I'll delete my answer.
David Murdoch
+2  A: 

Don't vote for this...vote for Tomalak's answer if you like this (it is the same as his but without using arguments.callee plus caching the regex itself).

var htmlid = (function(){
    var cache = {},
        reg = /[^A-Za-z0-9_:.-]/;
    return function(s){
        var id = s.replace(reg, ".");
        if (id in cache){ id += cache[id]++;}
        cache[id] = 0;

        return id;
    };
}());
David Murdoch
+1 for optimizing my version with a closure :-) I did not want to make it more complicated than necessary since I was already diverging from the original question, but this is of course more efficient.
Tomalak
You deserve credits for this as well :)
Marcel Korpel
A: 

First of all, thank you for the answers. Your brought a little error to the semantic of the function, as it should return the same id if I query for the same string twice. E.g.:

htmlid("foo bar");  // -> "foo.bar"
htmlid("foo bar");  // -> "foo.bar"
htmlid("foo.bar");  // -> "foo.bar0"
htmlid("foo.bar0"); // -> "foo.bar00"
htmlid("foo.bar");  // -> "foo.bar0"

Howvere, I adopted your functions to:

var htmlid = (function () {
    var cache = {},
        ncache = {},
        reg = /[^A-Za-z0-9_:.-]/;
    return function (s) {
        var id;
        if (s in cache) {
            id = cache[s];
        } else {
            id = s.replace(reg,".");
            if (id in ncache) {
                id += ncache[id]++;
            }
            ncache[id] = 0;
            cache[s] = id;
        }
        return id;
    };
}());
Ben