在for循环中使用splice从数组中删除项目

60

我想实现一种类似于jQuery的实时搜索。 但在将输入发送到服务器之前,我希望删除数组中所有长度为3个字符或更少的项(因为在德语中,这些单词通常可以在搜索方面忽略)。 所以 ["this", "is", "a", "test"] 变成了 ["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);
});
});

现在我的问题是不是所有的项都被移除了,例如我输入 "this is a test","is" 被移除,但 "a" 没有。

JSFIDDLE

我认为问题出在 for 循环中,因为如果我用 splice 移除一个项,数组的索引会改变,所以它继续使用“错误”的索引。

也许有人可以帮帮我?


我不明白为什么这不能在服务器端完成。请记住,用户可能会关闭JavaScript。 - Blazemonger
@Blazemonger 你说得对,但两种方式都应该是可行的。服务器端和客户端都可以。 - SirDerpington
然而,客户端处理的好处是什么呢?在上传搜索字符串之前,您只会剥离大约十几个字符,并且您将在服务器端重复检查。这只有在您想在提交之前向用户返回错误时才有价值。 - Blazemonger
好的,我也考虑过这个问题,但是可以说这对我来说更像是一种练习。事实上,让服务器处理工作更加聪明,但我已经开始了这个项目,想要一个可行的解决方案 ;) - SirDerpington
5个回答

154

解决方案1

您可以向后循环,可以使用以下代码:

var searchInput, i;

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

演示:http://jsfiddle.net/KXMeR/

这是因为在数组中进行递增迭代时,当您对其进行切片(splice)操作时,数组会被原地修改,因此某些项会被“移位”,您最终会跳过一些迭代。反向循环(使用whilefor循环)可以解决这个问题,因为您不会在进行切片的方向上进行循环。


解决方案2

同时,通常更快的方法是生成一个新的数组而不是直接修改原数组。以下是一个示例:

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);
    }
}

newSearchInput只包含有效长度的项,而您仍然可以在searchInput中保留原始项。

演示:http://jsfiddle.net/RYAx2/


解决方案三

除了上面的第二个解决方案外,还有一个类似的、更新的Array.prototype方法可用于更好地处理这个问题:filter。以下是一个示例:

var searchInput, newSearchInput;

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

演示: http://jsfiddle.net/qky7D/


参考资料:


1
谢谢,这就解决了。有时候你会变得很蠢,大脑会阻止所有聪明的思考方式 :D 我会尽快接受这个答案 ;) - SirDerpington
2
“沿着你拼接的方向循环” 是一个惊人的概念。 - jperelli
我想说你救了我的一天,但是我已经浪费了一整天的时间来找出为什么我的代码不起作用 :D - DifferentPseudonym
这非常有帮助!将索引递减而进行切片是否可以成为解决方案#4? - Muhammad Ali
太好了,解决方案!在第一个解决方案中,i 应该是预先递减的吗?我认为这就是它对我起作用的方式。 - Muhammad
显示剩余3条评论

10
var myArr = [0,1,2,3,4,5,6];

问题陈述:

myArr.splice(2,1);

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

现在第二个位置有三个移动,而长度减少了1,这会导致问题。

解决方案:一个简单的解决方案是在切片时反向迭代。

var i = myArr.length;
while (i--) {
    // do your stuff
}

6
如果您已经安装了lodash库,他们有一个不错的宝石您可能需要考虑。
该函数是_.forEachRight(从右到左迭代集合中的元素)。
这里是一个例子。
var searchInput = ["this", "is", "a", "test"];

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

    if (value.length < 4) {
        searchInput.splice(key, 1);
    }
});

1
非常好,真的很有帮助。谢谢。 - Amit Kumar

1

您也可以使用$.grep函数来过滤数组:

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/


1
另一种方法是在从数组中切片时递减索引(x--),这样当数组向下移动时,下一个元素不会被跳过。
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);

演示: https://jsfiddle.net/alinaeem229/n2kq3690/1/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接