60

I want to implement a kind of jQuery live search. But before sending the input to the server I'd like to remove all items in my array which have 3 or less characters (because in the german language, those words usually can be ignored in terms of searching) So ["this", "is", "a", "test"] becomes ["this", "test"]

$(document).ready(function() {
var timer, searchInput;
$('#searchFAQ').keyup(function() {
    clearTimeout(timer);
    timer = setTimeout(function() {
        searchInput = $('#searchFAQ').val().match(/\w+/g);
        if(searchInput) {
            for (var elem in searchInput) {
                if (searchInput[elem].length < 4) {
                    //remove those entries
                    searchInput.splice(elem, 1);
                }
            }
            $('#output').text(searchInput);
            //ajax call here
        }
    }, 500);
});
});

Now my problem is that not all items get removed in my for loop. If I for example typ "this is a test" "is" gets removed, "a" stays. JSFIDDLE

I think the problem is the for loop because the indexes of the array change if I remove an item with splice, so it goes on with the "wrong" index.

Perhaps anybody could help me out?

scottt
  • 7,008
  • 27
  • 37
SirDerpington
  • 11,260
  • 4
  • 49
  • 55
  • I can't see why this shouldn't be done server-side. Remember, it's always possible JavaScript will be turned off by the user. – Blazemonger Apr 25 '13 at 14:24
  • @Blazemonger You're right, but both ways should be possible. Server and client side. – SirDerpington Apr 25 '13 at 14:31
  • What's the benefit of doing it client-side, though? You're only stripping out about a dozen characters before you upload the search string, and you're going to repeat the check server-side anyway. This is worthwhile only if you want to return errors to the user before submitting. – Blazemonger Apr 25 '13 at 14:42
  • Well, I also thought about that but let's say it is more like an exercise for me. In fact it's smarter to just let the server do the work but I started this and wanted a working solution ;) – SirDerpington Apr 25 '13 at 15:12

5 Answers5

154

Solution 1

You can loop backwards, with something like the following:

var searchInput, i;

searchInput = ["this", "is", "a", "test"];
i = searchInput.length;
while (i--) {
    if (searchInput[i].length < 4) {
        searchInput.splice(i, 1);
    }
}

DEMO: http://jsfiddle.net/KXMeR/

This is because iterating incrementally through the array, when you splice it, the array is modified in place, so the items are "shifted" and you end up skipping the iteration of some. Looping backwards (with a while or even a for loop) fixes this because you're not looping in the direction you're splicing.


Solution 2

At the same time, it's usually faster to generate a new array instead of modifying one in place. Here's an example:

var searchInput, newSearchInput, i, j, cur;

searchInput = ["this", "is", "a", "test"];
newSearchInput = [];
for (i = 0, j = searchInput.length; i < j; i++) {
    cur = searchInput[i];
    if (cur.length > 3) {
        newSearchInput.push(cur);
    }
}

where newSearchInput will only contain valid length items, and you still have the original items in searchInput.

DEMO: http://jsfiddle.net/RYAx2/


Solution 3

In addition to the second solution above, a similar, newer Array.prototype method is available to handle that better: filter. Here's an example:

var searchInput, newSearchInput;

searchInput = ["this", "is", "a", "test"];
newSearchInput = searchInput.filter(function (value, index, array) {
    return (value.length > 3);
});

DEMO: http://jsfiddle.net/qky7D/


References:

Ian
  • 50,146
  • 13
  • 101
  • 111
  • @SirDerpington haha well no problem, and thanks :) Yeah, it all depends on your implementation. I'd prefer the first example unless you need the original array to reference, even if `splice` is a little slower (which you wouldn't see the difference in speed unless this code ran several times quickly on a huge array). – Ian Apr 25 '13 at 17:26
  • I'd say that you saved my day, but I have already wasted my day figuring out why my code wasn't working :D – DifferentPseudonym Feb 21 '16 at 20:34
  • This helped a lot! Could decrementing the index while splicing be solution #4? – Muhammad Ali Dec 03 '19 at 12:47
  • Great, solution! One thing in the first solution, `i` should be pre decremented? I think, that's how it worked for me. – Muhammad Dec 13 '19 at 12:40
  • @Muhammad, I thought the same, but it's perfect: the first cycle sets i to searchInput.length -1 – Daniel Faure Apr 20 '21 at 00:00
10
var myArr = [0,1,2,3,4,5,6];

Problem statement:

myArr.splice(2,1);

  \\ [0, 1, 3, 4, 5, 6];

now 3 moves at second position and length is reduced by 1 which creates problem.

Solution: A simple solution would be iterating in reverse direction when splicing.

var i = myArr.length;
while (i--) {
    // do your stuff
}
Arun Pratap Singh
  • 3,428
  • 30
  • 23
6

If you have the lodash library installed, they have a sweet gem you might want to consider.

The function is _.forEachRight (iterates over elements of a collection from right to left)

Here is an example.

var searchInput = ["this", "is", "a", "test"];

_.forEachRight(searchInput, function(value, key) {

    if (value.length < 4) {
        searchInput.splice(key, 1);
    }
});
Clinton Dobbs
  • 561
  • 5
  • 6
1

You can also use $.grep function to filter an array:

var timer, searchInput;
$('#searchFAQ').keyup(function () {
    clearTimeout(timer);
    timer = setTimeout(function () {
        searchInput = $('#searchFAQ').val().split(/\s+/g); // match is okay too
        searchInput = $.grep(searchInput, function(el) {
            return el.length >= 4;
        });
        console.log(searchInput);
    }, 500);
});

http://jsfiddle.net/dfsq/4Wdp9/

dfsq
  • 191,768
  • 25
  • 236
  • 258
1

Another approach would be to decrement the index whenever you slice from the array (x--), so that when the array gets shifted down the next element doesn't get skipped.

var searchInput;

searchInput = ["this", "is", "a", "test"];
for (var x = 0; x < searchInput.length; x++) {
    if (searchInput[x].length < 4) {
        searchInput.splice(x--, 1);
    }
}

console.log(searchInput);

DEMO: https://jsfiddle.net/alinaeem229/n2kq3690/1/

Muhammad Ali
  • 3,478
  • 5
  • 19
  • 30