I've been reading similar posts all day but can't figure out how to sort my javascript array by multiple properties.
My array has a 'name' and 'type' property.
To sort by name I now use:
byNameDesc.sort(function (a, b) {
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
return y < x ? -1 : y > x ? 1 : 0;
});
Works great. I want to enhance this function. If 'name' is 'foo' it should always be on top. And I also want to sort by 'type'.
So 'foo' should always be on top, next sort by 'name' and 'type'.
I tried this:
byNameDefault.sort(function (a, b) {
if (a.name == 'foo') {
return -1;
}
var x = a.type.toLowerCase();
var y = b.type.toLowerCase();
return x < y ? -1 : x > y ? 1 : 0;
});
But that didn't work.
And I have no clue how to sort by 'name' AND 'type'.
Any help is much appreciated.
For multiple sort criteria you proceed from the first to the last criterion:
If the two entries for one criterion are not equal, you can return from the sort function with result -1 or 1. Additionally at the last criterion you also can return 0 for two equal inputs.
Here is an example implementation for your case:
byNameDefault.sort(function (a, b) {
// compare names
var na = a.name.toLowerCase();
var nb = b.name.toLowerCase();
if (na !== nb) {
if (na === 'foo')
return -1;
else if (nb === 'foo')
return 1;
else
return na < nb ? -1 : 1;
}
// compare types
return a.type < b.type ? -1 : a.type > b.type ? 1 : 0;
}
Do this in one expression where the different components are combined with ||; only when one part evaluates to 0, then next one comes into play:
byNameDefault.sort(function (a, b) {
return (b === 'foo') - (a == 'foo') ||
a.name.localeCompare(b.name) ||
a.type.localeCommpare(b.type);
}
I'm populating a few drop downs from some JSON, it works as expected at the moment but i'm trying to sort the values in the drop downs. For all my drop downs it works fine, except for one of them which has numbers in, in which case it lists it 1, 10, 12, 2.
Any ideas how i can keep it sorting alphabetically for everything else but get the sort to work with the numeric values too?
Here's my JS (this replicates for each field - probably should try to find a way to make this reuseable):
var populateGenres = _.map(data.genres, function (val) {
return '<option>' + val + '</option>';
}).join();
var target = '#genreField';
$('#genreField').html(populateGenres);
reArrangeSelect(target);
Here's the sort JS:
function reArrangeSelect(target) {
$(target).html($(target + " option").sort(function(a, b) {
return a.text == b.text ? 0 : a.text < b.text ? -1 : 1
}))
}
My HTML is in this format:
<td>
<select id="genreField" class="form-control"></select>
</td>
<td>
<select id="authorField" class="form-control"></select>
</td>
function reArrangeSelect(target) {
$(target).html($(target + " option").sort(function(a, b) {
// u can use that 'getValue' function
// for "a.text == 'some string'" it will return 'some string' (string type),
// for "a.text == '10'" it will return 10 (number type)
var aVal = getValue(a.text);
var bVal = getValue(b.text);
return aVal == bVal ? 0 : aVal < bVal ? -1 : 1;
}));
}
function getValue(val) {
var asNumber = parseFloat(val);
return isNaN(asNumber) ? val : asNumber;
}
You can sort data.genres before running it through the _.map functions:
data.genres.sort(function (a, b) {
a = parseInt(a, 10);
b = parseInt(b, 10);
if(a > b) {
return 1;
} else if(a < b) {
return -1;
} else {
return 0;
}
});
Once you sort the data then run your _.map snippet:
var populateGenres = _.map(data.genres, function (val) {
return '<option>' + val + '</option>';
}).join();
All of your options should now be sorted before you append them to the <select>.
DEMO
I am using a jquery drop down table filter plug in for table filters:
https://github.com/rbayliss/Dropdown-Table-Filter
When I have a column in my table that is numerical however, the numbers are sorted as text, e.g. 1, 10, 100, 2, 20, 200 ...
in the code the sorter looks like:
if(options.sortOpt) {
opts.sort(options.sortOptCallback);
}
I think this is a recursive call to:
sortOptCallback: function(a, b) {
return a.text.toLowerCase() > b.text.toLowerCase();
},
how should I amend this so that it will sort numerical fields correctly? I have tried the following:
sortOptCallback: function (a, b) {
if (isNaN(parseInt(a)) || isNaN(parseInt(b))) {
return a.text.toLowerCase() > b.text.toLowerCase();
} else {
return a > b;
}
},
Thanks,
Your attempt is almost correct. The only problem is that you are still comparing the elements as strings after having determined that they are numeric. Furthermore, sort callbacks expect:
A positive number if a > b
A negative number if a < b
Zero if they are equal.
With this in mind, try this callback:
if( a == b) return 0;
var anum = parseInt(a,10);
var bnum = parseInt(b,10);
if( isNaN(anum) || isNaN(bnum)) {
return a.toLowerCase() > b.toLowerCase() ? 1 : -1;
}
return anum > bnum ? 1 : -1;
EDIT: #PaoloMoretti brought my attention to the fact that all your items are numerical. In that case, you can just do this:
return a-b;
I am using jquery datatable to display data. I display '--' when there is no data. Currently when the table sorts the data all the '--' comes in the beginning and the order looks like below:
--
--
10
20
400
800
But I need to make '--' to be displayed last when sorted in ascending order and should look something like below:
10
20
400
800
--
--
Please let me know how can we get this behavior in jquery datatable?
you can use an exstension
jQuery.extend(jQuery.fn.dataTableExt.oSort, {
"myorder-pre": function (a) {
},
"myorder-asc": function (a, b) {
if(a == '--' && b != '--')
return 1;
else if(b == '--' && a != '--')
return -1;
else if(b == '--'&& a == '--')
return 0;
else
{
a = parseFloat(a);
b = parseFloat(b);
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
}
},
"myorder-desc": function (a, b) {
if(a == '--' && b != '--')
return -1;
else if(b == '--' && a != '--')
return 1;
else if(b == '--'&& a == '--')
return 0;
else
{
a = parseFloat(a);
b = parseFloat(b);
return ((a < b) ? 1 : ((a > b) ? -1 : 0));
}
}
});
myorder-pre is used before all the order call.
myorder-asc when you order asc. Return number negative if a minor b, positive if a major b, 0 if equal.
Desc work adverse
then in the definition of columns of datatable, use
"aoColumnDefs": [{ "sType": 'myorder'}]
You can make use of the following code :
$('#example').dataTable( {
"aaSorting": [[ 4, "desc" ]]
} );
For the reference
What is the best way of sorting if the column values are:
Before SORTING:
CANCELLED,
v06.*,
INDEPENDENT,
v06.00,
v06.00.01,
v06.01,
v06.02,
v06.00.xx,
v06.03,
v06.04,
ON HOLD,
v06.06,
v06.05,
v06.05.01,
v06.04.01,
v06.05.02,
v06.07,
v07.00,
After SORTING:
CANCELLED,
INDEPENDENT,
ON HOLD,
v06.*,
v06.00,
v06.00.01,
v06.00.xx,
v06.01,
v06.02,
v06.03,
v06.04,
v06.04.01,
v06.05,
v06.05.01,
v06.05.02,
v06.06,
v06.07,
v07.00
Thanks in advance,
Joseph
First sort it alphabetically ascending (by the 'default' sort - or sort() without passing a function), then sort it numerically. That said, I'm sure there is a better way:
function sortNumber(a, b) {
return a - b;
}
var arr = [
"v07.00", "CANCELLED", "v06.*", "v06.04.01", "INDEPENDENT", "v06.00", "v06.00.01",
"v06.01", "v06.02", "v06.00.xx", "v06.03", "v06.04", "ON HOLD",
"v06.06", "v06.05", "v06.05.01", "v06.05.02",
"v06.07",
];
alert(arr.sort().sort(sortNumber).join("\n"));
Demo: http://jsfiddle.net/karim79/rY8Du/1/
Assuming your "column values" are in an Array, use Array.sort with a custom compareFunction to define the ordering as you want.
var columnValues = [
"CANCELLED", "v06.*", "INDEPENDENT", "v06.00", "v06.00.01",
"v06.01", "v06.02", "v06.00.xx", "v06.03", "v06.04", "ON HOLD",
"v06.06", "v06.05", "v06.05.01", "v06.04.01", "v06.05.02",
"v06.07", "v07.00" ];
columnValues.sort(function(a, b) {
if (a is less than b by some ordering criterion)
return -1;
if (a is greater than b by the ordering criterion)
return 1;
// a must be equal to b
return 0;
});
Edit here's a long-winded compareFunction that seems to do what you want, at least for the example you give:
function(a, b) {
if (a==b) {
return 0;
}
if (a.length && a[0]=='v' && b.length && b[0]=='v') {
// Both strings are version strings.
// Do special case version matching.
var aParts = a.substring(1).split('.'),
bParts = b.substring(1).split('.'),
l = Math.max(a.length, b.length),
i = 0;
for (;i<l;i++) {
var aPart = aParts[i],
bPart = bParts[i];
if (aPart == '*' && bPart != '*') {
return -1;
}
if (bPart == '*' && aPart != '*') {
return 1;
}
if (aPart == 'xx' && bPart != 'xx') {
return 1;
}
if (bPart == 'xx' && aPart != 'xx') {
return -1;
}
var aNum = parseInt(aPart,10),
bNum = parseInt(bPart,10);
if (aNum < bNum) {
return -1;
}
if (aNum > bNum) {
return 1;
}
// Same so far, try next part
}
// One must be longer than the other.
return (aParts.length < bParts.length) ? -1 : 1
}
// Simple alphabetic comparison
if (a < b)
return -1;
if (a > b)
return 1;
}
Demo: http://jsfiddle.net/daybarr/h6nmg/
Once you get the column into an array you can use any natural sort method,
but you'll need to write a couple extra lines to sort the '*' the way you want.
Here is one sample-
function natSort(as, bs){
var a, b, a1, b1, i= 0, L, rx= /(\d+)|(\D+)/g, rd= /\d/;
if(isFinite(as) && isFinite(bs)) return as - bs;
a= String(as).toLowerCase();
b= String(bs).toLowerCase();
if(a=== b) return 0;
if(!(rd.test(a) && rd.test(b))) return a> b? 1: -1;
a=a.replace('*','0'); // or possibly sort any non alpha nums first:
b=b.replace('*','0'); // replace(/[^\w\.]/g,'0');
a= a.match(rx);
b= b.match(rx);
L= a.length> b.length? b.length: a.length;
while(i < L){
a1= a[i];
b1= b[i++];
if(a1!== b1){
if(isFinite(a1) && isFinite(b1)){
if(a1.charAt(0)=== "0") a1= "." + a1;
if(b1.charAt(0)=== "0") b1= "." + b1;
return a1 - b1;
}
else return a1> b1? 1: -1;
}
}
return a.length - b.length;
}
var v= "v06.05,ON HOLD,v07.00,INDEPENDENT,v06.07,v06.03,v06.*,v06.05.02,v06.05.01,"+
"v06.00.xx,v06.00,CANCELLED,v06.02,v06.04,v06.00.01,v06.06,v06.01,v06.04.01";
v=v.split(/, */);
'before sort:\n'+v.join(',\n')+'\n\nafter sort:\n'+ v.sort(natSort).join(',\n')
/* returned value:
before sort:
v06.05,
ON HOLD,
v07.00,
INDEPENDENT,
v06.07,
v06.03,
v06.*,
v06.05.02,
v06.05.01,
v06.00.xx,
v06.00,
CANCELLED,
v06.02,
v06.04,
v06.00.01,
v06.06,
v06.01,
v06.04.01
after sort:
CANCELLED,
INDEPENDENT,
ON HOLD,
v06.*,
v06.00,
v06.00.01,
v06.00.xx,
v06.01,
v06.02,
v06.03,
v06.04,
v06.04.01,
v06.05,
v06.05.01,
v06.05.02,
v06.06,
v06.07,
v07.00
*/