如何根据某些条件对数组进行部分排序?

时间:2014-05-28 08:00:22

标签: javascript arrays sorting

如何基于我选择的某些条件在JavaScript中对数组进行部分排序?

给定数组

var tab = [
    {html: 'This is a test'},
    {html: 'Locked item', locked: true},
    {html: 'Another item'},
    {html: 'Another one'}
    //and 100 more items...
];

第二项被称为"锁定",它应该在数组排序后保持在同一位置(第二位)

var tab = [
    {html: 'Another item'},
    {html: 'Locked item', locked: true},
    {html: 'Another one'},
    {html: 'This is a test'},
    //and 100 more items...
];

这是我目前拥有的完整代码:

var tab = [
    {html: 'This is a test'},
    {html: 'Locked item', locked: true},
    {html: 'Another item'},
    {html: 'Another one'}
    //and more items...
];

tab.sort(function(a,b){
    var aVal = a.html.toLowerCase();
    var bVal = b.html.toLowerCase();
    if (aVal===bVal)
        return 0;
    else
        return aVal < bVal ? -1 : 1;

    //how to handle locked items?
});

//desire output
/*var tab = [
    {html: 'Another item'},
    {html: 'Locked item', locked: true},
    {html: 'Another one'},
    {html: 'This is a test'},
    //and more items...
];*/

console.log(tab);

请参阅fiddle

4 个答案:

答案 0 :(得分:2)

这部分有效:

tab.sort(function(a,b){
    if(a.locked || b.locked)
        return 0;
    if (a.html < b.html)
        return -1;
    if (a.html > b.html)
        return 1;
    return 0;
});

唯一的问题是,由于sort使用的排序算法的工作方式,它无法移动锁定项目后的第1项。但是,它会对您示例中的最后2个项目进行排序。

让它以你想要的方式工作的最好方法是从数组中提取锁定的元素,将它们的索引保存在变量中,对数组进行排序,然后再次重新插入元素。

答案 1 :(得分:2)

这里的问题是sort用于线性排序,没有别的。在其范围之外使用它是不可靠的,因为浏览器可以对它的执行方式进行一些优化。

因此,您基本上必须对数组的一部分进行线性排序。你可以做的最好的事情是拉出锁定的元素,保持它们的索引,执行排序并将锁定的元素放回正确的位置。

var fixed = [], i;
for (i = tab.length; i--;) {
    if (tab[i].locked) {
        fixed.unshift({index: i, item: tab[i]});
        tab.splice(i, 1);
    }
}

tab.sort(...); // Perform your sort

for (i = 0; i < fixed.length; i++)
    tab.splice(fixed[i].index, 0, fixed[i].item);

不要因为我执行了一个向后for并使用不移动而不是推送这一事实而感到困惑:要保持正确的索引。

反向循环是因为当执行splice时它会改变数组,所以应该相应地调整前向循环i以便不跳过项目。但这也会使fixed中记录的索引变得混乱。所以我们做了一个向后循环,并使用unshift来保持fixedindex属性排序。

请记住splice有点慢,因此对于较大的数组,您可能需要使用一些更优化的算法。如果阵列不超过几十个固定元素,我猜是可以的。

答案 2 :(得分:1)

可能可以进行优化,但删除所有锁定的项目并存储其初始位置,然后将它们添加回已排序的数组应该可以正常工作:

var tab = [
    {html: 'This is a test'},
    {html: 'Locked item', locked: true},
    {html: 'Another item'},
    {html: 'Another one'}
    //and more items...
];

var stored = {}, newTab = [];
for(var i = 0, iLimit = tab.length; i < iLimit; i++) {
    if(tab[i].locked) {
        stored[i] = tab[i];
    } else {
        newTab.push(tab[i]);
    }
}

newTab.sort(function(a,b){
    var aVal = a.html.toLowerCase();
    var bVal = b.html.toLowerCase();
    if (aVal===bVal) {
        return 0;
    } else {
        return aVal < bVal ? -1 : 1;
    }
});

for(var indice in stored) {
    newTab.splice(indice, 0, stored[indice]);
}

console.log(newTab);

http://jsfiddle.net/qW7uH/2/

答案 3 :(得分:0)

或者,您可以过滤掉所有锁定的项目,将它们放在一个单独的数组中,如下所示:

[
    { obj: ..., index: ...}, 
    { ... }
] 

这会保留每个对象的索引。然后你可以在排序后把它们放进去。看起来有点麻烦。

请查看Array.prototype.filter进行过滤,但请注意,如果没有填充,它在IE8中不起作用。