Reputation: 3804
How can I partially sort an array in JavaScript, based on some conditions that I choose?
Given an array
var tab = [
{html: 'This is a test'},
{html: 'Locked item', locked: true},
{html: 'Another item'},
{html: 'Another one'}
//and 100 more items...
];
The 2nd items is called "locked", it should stays at the same place (2nd place) after the array is sorted
var tab = [
{html: 'Another item'},
{html: 'Locked item', locked: true},
{html: 'Another one'},
{html: 'This is a test'},
//and 100 more items...
];
This is the complete code I currently have:
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);
See fiddle
Upvotes: 2
Views: 2011
Reputation: 22617
The problem here is that sort
is used for linear sorting, nothing else. Using it outside its scope is not reliable, also because browsers can perform some optimizations on the way it's executed.
So, you essentially have to linear sort just a part of your array. The best thing you can do is pulling out the locked elements, keep their indexes, perform the sort and putting the locked elements back in in the right positions.
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);
Don't be puzzled by the fact that I performed a backward for
and used unshift istead of push: it's to keep the correct indexes.
The reversed loop is because when splice
is executed it alters the array, so with a forward loop i
should be adjusted accordingly in order to not skip items. But that would also mess the index that's recorded in fixed
. So we do a backward loop instead and use unshift
to keep fixed
sorted by the index
property.
Keep in mind that splice
is kind of slow, so for larger arrays you may want to use some more optimized algorithms. If the array has no more than some dozens fixed elements, it's ok I guess.
Upvotes: 3
Reputation: 521
Probably could be optimised, but removing all locked items and storing their initial positions, then adding them back into the sorted array should work:
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);
Upvotes: 1
Reputation: 3370
Alternatively, you could filter out all the locked items, an place them in a separate array like this:
[
{ obj: ..., index: ...},
{ ... }
]
This keeps the index for each object. Then you could put them in after you have sorted. Seems like a little hassle though.
Look at Array.prototype.filter for filtering, but beware that it doesn't work in IE8 without a polyfill.
Upvotes: 0
Reputation: 72857
This partially works:
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;
});
The only problem is that it can't move the 1st item after the locked item, due to the way the sorting algorithm used by sort
works. It does, however, sort the last 2+ items in your example.
Your best bet to get it to work the way you want, is to pull the locked elements from the array, save their index in a variable, sort the array, and then re-insert the elements again.
Upvotes: 2