2
\$\begingroup\$

What would be the fastest way of skipping certain values in a for loop? This is what I intend to do in its most basic form, with about 20 more keys to omit.

for (var key in arr ) {
    if ( key != 'ContentType' && key != 'FileRef' ) {
        arrayPushUnknown( columns, formatTitle( key ), true, key )
    }   
}   
\$\endgroup\$
4
  • 2
    \$\begingroup\$ I'm a big fan of 'continue'. if( wanting_to_skip_condition ) continue; then the rest of the code you want run during a good loop. \$\endgroup\$
    – rlemon
    Commented Oct 9, 2013 at 17:51
  • \$\begingroup\$ could you elaborate a little more on what you are doing and what you want? what you are doing currently? \$\endgroup\$
    – Malachi
    Commented Oct 9, 2013 at 18:56
  • \$\begingroup\$ Sure, the keys in the object are fields in a SharePoint document library. There are a lot of default fields that I do not need, so I wish to skip the keys I do not need and add the rest to a fresh object. The function "arrayPushUknown" just adds the keys I need to the new object. \$\endgroup\$
    – Skarven
    Commented Oct 9, 2013 at 19:02
  • 1
    \$\begingroup\$ @Skarven: Make an array containing all the keys you want to skip and then if (keysToBeOmitted.Contains(key)) continue. If you are concerned about performance then make sure your keysToBeOmitted array is sorted and do a binary search on it (localCompare() should help with that) \$\endgroup\$
    – ChrisWue
    Commented Oct 9, 2013 at 19:42

4 Answers 4

2
\$\begingroup\$

One of the simplest and probably fastest way to do it would be to store the keys to ignore in a fast lookup structure, such as using an object as a map.

First you need to build the map, which takes O(n) time based on the number of keys to ignore, but allows you to ignore keys with a condition that will take O(1) time to compute.

var keysToIgnore = ['ContentType', 'FileRef'].reduce(function (res, key) {
    res[key] = true;
    return res;
}, {});

Then within the loop, if the key should be ignored, just continue.

if (keysToIgnore[key]) continue;

The algorithm will probably not be an order of magnitude faster (slower? could be!) than a bunch of ifs or a switch statement, but it will definitely be more readable. It also has the advantage of being dynamic, since you can add or remove keys from keysToIgnore.

Note: If you have to perform the iteration process many times, it could be faster to simply filter out the keys using Array.prototype.filter and the algorithm described above.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I like how this way (continue + reversed OP's condition) removes needless nesting. \$\endgroup\$ Commented Oct 10, 2013 at 3:13
  • \$\begingroup\$ Thanks, I am trying this out along with other solutions - but yes, my goal is performance, as I will be iterating through thousands of elements (documents in the Document Library). \$\endgroup\$
    – Skarven
    Commented Oct 10, 2013 at 14:03
  • \$\begingroup\$ @Skarven Have you tested mine? I modified your test case -> jsperf.com/codereview-32486/4 Also, make sure to test in multiple browsers. \$\endgroup\$
    – plalx
    Commented Oct 10, 2013 at 17:11
3
\$\begingroup\$
  • Tests across various browser has shown a reverse while loop is fastest.
  • Accessing array.length is slower than storing it in a variable to iterate on.
  • You can just "push" to an array by storing the value in the last element which is conveniently at .length
  • A switch may not be much faster in this case (though it can be with other data) than a bunch of &&s and ||s, but its much easier to read and the fallthrough characteristic allows you to write like code in groupings. In this case we only have 2 groups: do nothing (break;) and the default "push"

For example:

var i=arr.length;
var newarr=[];
while (i--){
  switch (arr[i].key){
    case 'ContentType' :
    case 'FileRef' :
    case (other) :
    case (cases) :
    case (here) :
    break;
    default: newarr[newarr.length]=arr[i];
  }
}

On a few newer browsers (Chrome for one), push is faster than direct assignment. Note that this will be reversed from the original array, but if you need to keep them in the same order using for(var i=0,len=arr.length;i<len;i++), I caution you not to be tempted to use newarr[i]=arr[i] or you will get empty slots where your keys match your blacklist.

\$\endgroup\$
1
  • \$\begingroup\$ @200_success No, the break is for the switch, not the while. \$\endgroup\$ Commented Oct 10, 2013 at 3:40
2
\$\begingroup\$

There are several possible approaches:

1) The straightforward:

function contains(array,element){
    return array.indexOf(element)!=-1;
}

function filter(unfilteredArray){
    var filteredArray=[];
    for(var i=0; i<unfilteredArray.length; i+=1){
        if(contains(omitted,unfilteredArray[i])) continue;
        filteredArray.push(unfilteredArray[i]);
    }
    return filteredArray;
}

Looping over your array and sorting out, which ones you don't like. You could play with this Fiddle

2) The "functional" approach

function omitting_1_to_10(element){
    return [1,2,3,4,5,6,8,9,10].indexOf(element)==-1;
}

console.log([1,2,3,4,11].filter(omitting_1_to_10));

Define your omitting filter and apply it to your array. For Array.filter read the according MDN-article.

3) Doing it with a RegEx:

function filter(unfilteredArray){
    var filteredArray=[];
    for(var i in unfilteredArray){   
    if(/\b1\b|\b2\b|\b3\b|\b4\b|\b5\b|\b6\b|\b7\b|\b8\b|\b9\b|\b10\b/.test(unfilteredArray[i])) continue;
        filteredArray.push(unfilteredArray[i]);
    }
    return filteredArray;
}
console.log(filter([1,2,3,4,11]));

Here is the Fiddle

\$\endgroup\$
2
\$\begingroup\$

A switch block works about 4% faster on a pre-release Safari 6.1, but is 11% slower on Firefox 17. Regardless of performance, I like the switch block better stylistically, especially if there are more exceptional keys.

http://jsperf.com/codereview-32486

for (var key in arr) {
    switch (key) {
      case 'ContentType':
      case 'FileRef':
        break;
      default:
        arrayPushUnknown(columns, formatTitle(key), true, key);
    }
}
\$\endgroup\$
6
  • \$\begingroup\$ Thanks, I extended your test with some more exceptions, and the performance increase on Safari grew to 23% faster. As the code will iterate through thousands of items and iPhone performance is central, this is very helpful. jsperf.com/codereview-32486/3 \$\endgroup\$
    – Skarven
    Commented Oct 10, 2013 at 14:16
  • 1
    \$\begingroup\$ @200_success stackoverflow.com/questions/18076238/… . Then a for-in loop as an optimization? oreilly.com/server-administration/excerpts/even-faster-websites/… \$\endgroup\$ Commented Oct 10, 2013 at 17:01
  • \$\begingroup\$ @Skarven Have you tested mine? I modified your test case -> jsperf.com/codereview-32486/4 Also, make sure to test in multiple browsers. \$\endgroup\$
    – plalx
    Commented Oct 10, 2013 at 17:09
  • \$\begingroup\$ Corrected continue to break. Thanks, @technosaurus. \$\endgroup\$ Commented Oct 10, 2013 at 22:54
  • \$\begingroup\$ @plalx - Thanks for setting up the test, and the results are interesting, your object map approach outperforms the switch approach by a massive 42% in IE 9 and by 12% in FireFox 24. In Safari 5.1.7 (PC), however - the switch is 12% faster than the object map, and in Chrome, the object map is 24% slower than both the switch and the "if foo not bar" approach. \$\endgroup\$
    – Skarven
    Commented Oct 11, 2013 at 4:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.