views:

237

answers:

5

Hey there geniuses of SO!

This is for an autocomplete plugin that needs to accept data as an array of arrays and convert it using a format string (or regex). The format string could be any format.

var dataArray = [ ["data1-1", "data1-2", "data1-3"], ["data2-1", "data2-2", "data2-3"],... ];
var format = "<li>{0} <br /> -- <small> {1}, {2}</small></li>";
// alternate formats could be: 
//  "<li>{0}</li>"
//  "<a href="{0}" title="{2}">{1} ({2})</a>"
// etc...

function fillAutocomplete(datum,format){
    // do some magic here...
    // return "<li>data1-1 <br /> -- <small> data1-2, data1-3</small></li>";
}

The following idea works..but i'd like to see if anything would be faster...

var datum = data[0],
    html="<li>\{0\} <br /> -- <small> \{1\}, \{2\}</small></li>";
for(var i=0,l=datum.length;i<l;++i){
    var reg = new RegExp("\\{"+i+"\\}");
    html=html.replace(reg,datum[i]);
}

I'm open to new ideas on how to approach this problem.

+1  A: 

While less elegant, this will be significantly faster:

html = "<li>" + datum[0] 
        + " <br /> -- <small> " 
        + datum[1] + ", " + datum[2] 
        + "</small></li>";

Your original approach creates a new regular expression for each iteration of the for loop which can be expensive. You could look into creating these expressions once and caching them perhaps but even then the overhead of executing the regular expression and replacing the format string will still be greater than a simple string concatenation.

Unfortunately elegance is often the first victim of optimization.

Andrew Hare
That was how it was originally. I'll update my question to make it more clear. I need the format string to be dynamic for each dataset.
David Murdoch
+8  A: 

Check out John Resig's "Search and Don't Replace" to see that you can pass a callback function to myString.replace(..).

var datum = data[0];
var html="<li>{0}<br /> -- <small>{1}, {2}</small></li>";
var pattern = /\{(\d+)\}/g;

html = html.replace(pattern,function(match, key, value){
    return datum[key];
});
JasonWyatt
Ah, i remember reading that a while back. I'll try it out later. Thanks alot!
David Murdoch
+1. Thanks for this, I had no idea you could pass a function to analyze the current match during the replacement loop.
Matt
works wonders! thanks! I'm wondering if john resig's templating will be faster. hm. i'll get back to you all on that one tomorrow.
David Murdoch
update: micro templating isn't faster until you are doing thousands of iterations.
David Murdoch
A: 

You can pass a function to replace as the second argument. Try this:

function fillAutocomplete(datum,format){
  return format.replace(/{([0-9]+)}/g, function(match) {
    return datum[match[1]];
  });
}
Mark Stewart
A: 

Depending on the number of replacements you need to make this could be faster

var datum = data[0],
    html="<li>{0} <br /> -- <small> {1}, {2}</small></li>";
for(var i=0,l=datum.length;i<l;++i){
    html=html.split("{" + i + "}").join(datum[i]);
}

there are some corner cases for when the {n} appears as the very first or last part of the string.

barkmadley
A: 

This should be the fastest if you still want to use HTML strings:

html = [
  "<li>", datum[0],
  "<br /> -- <small>",
  datum[1], ", ", datum[2],
  "</small></li>"
].join("");

Of course, it would be best if actually used the DOM.

html = document.createElement("li");
html.appendChild(document.createTextNode(datum[0]));
html.appendChild(document.createElement("br"));
html.appendChild(document.createTextNode(" -- "));
html.appendChild(document.createElement("small"))
  .appendChild(document.createTextNode(datum[1] + ", " + datum[2]));
Eli Grey
It might be philosophically "better" to use the DOM, but it will be pragmatically slower, way slower...
JasonWyatt
Jason, it's slower to use the string as it has to be converted to the DOM anyways.
Eli Grey