I have a v-data-table in vueJS that contains some numeric columns and some string columns.
In each column, some of the values are null.
I am trying to create a custom sorting function that will place null values last.
This is what I tried so far:
<v-data-table
:headers="[
{ text: 'Name', value: 'name' },
{ text: 'Date of Birth', value: 'dateofbirth_fmt' },
{ text: 'Team', value: 'team_name' },
{
text: 'dp1 (string)',
value: 'dp1',
},
{
text: 'dp2 (Numeric),
value: 'dp2',
}
]"
:items="filteredPlayersData"
item-key="_id"
class="elevation-1"
:custom-sort="customSort"
/>
and this function
customSort(items, index, isDesc) {
items.sort((a, b) => {
if (!isDesc[0]) {
return (a[index] != null ? a[index] : Infinity) >
(b[index] != null ? b[index] : Infinity)
? 1
: -1;
} else {
return (b[index] != null ? b[index] : -Infinity) >
(a[index] != null ? a[index] : -Infinity)
? 1
: -1;
}
});
return items;
}
It is working for this numeric column (dp1), but not for the string one (dp2).
Any ideas how to get this work?
Your sorting algorithm is not working correctly for strings.
Imagine that your first string is null, and the second one is 'Jelly bean'.
Instead of null value you are trying to compate Infinity with 'Jelly bean'.
This comparison will be false in both cases:
let a = Infinity;
let b = 'Jelly bean';
console.log(a > b);
console.log(a < b);
It'd be better to use another sorting algorithm.
For example, I've adapted an algorithm from this post:
customSort(items, index, isDesc) {
items.sort((a, b) => {
if (a[index] === b[index]) { // equal items sort equally
return 0;
} else if (a[index] === null) { // nulls sort after anything else
return 1;
} else if (b[index] === null) {
return -1;
} else if (!isDesc[0]) { // otherwise, if we're ascending, lowest sorts first
return a[index] < b[index] ? -1 : 1;
} else { // if descending, highest sorts first
return a[index] < b[index] ? 1 : -1;
}
});
return items;
}
You may test this at CodePen. Works fine for both strings and numbers.
I think I finally found the source of an irritating problem. I am sorting an array of objects by the price of a product and sometimes the product price is not available denoted an "N/A". I am expecting all the values of "N/A" to be put last in the total sort order...but its not turning out that way. It seems to work for a bit, all prices listed first and then prices and N/A's are interspersed towards the bottom of the sort. How/What can I do to solve this issue?
var arr = [
{id:0,vendor:'ACompany',price:'10.82'},
{id:1,vendor:'ZCompany',price:'10.00'},
{id:2,vendor:'LCompany',price:'9.82'},
{id:3,vendor:'DCompany',price:'N/A'},
{id:4,vendor:'WCompany',price:'11.82'},
{id:5,vendor:'RCompany',price:'N/A'},
{id:6,vendor:'HCompany',price:'10.83'},
{id:7,vendor:'MCompany',price:'10.72'},
{id:8,vendor:'XCompany',price:'9.92'},
{id:9,vendor:'ICompany',price:'N/A'},
{id:10,vendor:'GCompany',price:'10.82'},
] ;
function sortArr(key) {
arr.sort(function(a,b) {
var x = a[key]; var y = b[key] ;
if (key == "vendor") {
return ((x<y) ? -1 : ((x > y) ? 1: 0)) ;
} else {
return parseFloat(x) - parseFloat(y) ;
}
})
}
sortArr('price') ; // when sorting by 'vendor', it works fine.
for (x=0;x<arr.length;x++) {
console.log(arr[x].vendor+ ": " +arr[x].price) ;
}
The Sort output is:
LCompany: 9.82
XCompany: 9.92
ZCompany: 10.00
MCompany: 10.72
ACompany: 10.82
GCompany: 10.82
DCompany: N/A
ICompany: N/A
HCompany: 10.83
WCompany: 11.82
RCompany: N/A
And I know I can't try to parse the prices as strings, the 'N/A's will be last, but $10 values will be listed before $9 values.
You can first sort it by not NaN values and then by price values.
var arr = [{id:0,vendor:'ACompany',price:'10.82'},{id:1,vendor:'ZCompany',price:'10.00'},{id:2,vendor:'LCompany',price:'9.82'},{id:3,vendor:'DCompany',price:'N/A'},{id:4,vendor:'WCompany',price:'11.82'},{id:5,vendor:'RCompany',price:'N/A'},{id:6,vendor:'HCompany',price:'10.83'},{id:7,vendor:'MCompany',price:'10.72'},{id:8,vendor:'XCompany',price:'9.92'},{id:9,vendor:'ICompany',price:'N/A'},{id:10,vendor:'GCompany',price:'10.82'}];
arr.sort((a, b) => {
let aN = +a.price, bN = +b.price;
return !isNaN(bN) - !isNaN(aN) || aN - bN
})
console.log(arr)
Your solution is close, but needs to handle non-numeric input as a special exception. You can detect if the string is non-numeric as follows: isNaN(parseFloat("N/A")) == true, isNaN(parseFloat("3.0")) == false.
The problem is because NaN has no ordering. NaN < NaN == false, NaN > NaN == false, and NaN == NaN == false.
So before you decide to sort numerically with parseFloat(x) - parseFloat(y) in your code, check for that condition and use a different ordering of your choosing.
var arr = [
{id:0,vendor:'ACompany',price:'10.82'},
{id:1,vendor:'ZCompany',price:'10.00'},
{id:2,vendor:'LCompany',price:'9.82'},
{id:3,vendor:'DCompany',price:'N/A'},
{id:4,vendor:'WCompany',price:'11.82'},
{id:5,vendor:'RCompany',price:'N/A'},
{id:6,vendor:'HCompany',price:'10.83'},
{id:7,vendor:'MCompany',price:'10.72'},
{id:8,vendor:'XCompany',price:'9.92'},
{id:9,vendor:'ICompany',price:'N/A'},
{id:10,vendor:'GCompany',price:'10.82'},
] ;
function sortArr(key) {
arr.sort(function(a,b) {
var x = a[key]; var y = b[key] ;
if (key == "vendor") {
return ((x<y) ? -1 : ((x > y) ? 1: 0)) ;
} else {
return isNaN(+x) ? 1 : isNaN(+y) ? -1 : parseFloat(x) - parseFloat(y) ;
}
})
}
sortArr('price') ; // when sorting by 'vendor', it works fine.
for (x=0;x<arr.length;x++) {
console.log(arr[x].vendor+ ": " +arr[x].price) ;
}
This solution is slightly more robust than just handling 'N/A', since it will also put any non-numeric input at the bottom, though not in a specified order.
Try this custom sorter instead.
In short, numeric prices will be sorted at the top of the array and all N/A price types will be sorted down to the bottom. I'd recommend reading about custom sorters here...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
var arr = [
{id:0,vendor:'ACompany',price:'10.82'},
{id:1,vendor:'ZCompany',price:'10.00'},
{id:2,vendor:'LCompany',price:'9.82'},
{id:3,vendor:'DCompany',price:'N/A'},
{id:4,vendor:'WCompany',price:'11.82'},
{id:5,vendor:'RCompany',price:'N/A'},
{id:6,vendor:'HCompany',price:'10.83'},
{id:7,vendor:'MCompany',price:'10.72'},
{id:8,vendor:'XCompany',price:'9.92'},
{id:9,vendor:'ICompany',price:'N/A'},
{id:10,vendor:'GCompany',price:'10.82'}
];
arr.sort(function(a, b) {
if (a.price == b.price) return 0;
if (a.price == "N/A") return 1;
if (b.price == "N/A") return -1;
return a.price - b.price;
});
console.log(arr);
You could check if the property has a string and get the delta of the comparison fo a chained retuen value.
var array = [{ id: 0, vendor: 'ACompany', price: '10.82' }, { id: 1, vendor: 'ZCompany', price: '10.00' }, { id: 2, vendor: 'LCompany', price: '9.82' }, { id: 3, vendor: 'DCompany', price: 'N/A' }, { id: 4, vendor: 'WCompany', price: '11.82' }, { id: 5, vendor: 'RCompany', price: 'N/A' }, { id: 6, vendor: 'HCompany', price: '10.83' }, { id: 7, vendor: 'MCompany', price: '10.72' }, { id: 8, vendor: 'XCompany', price: '9.92' }, { id: 9, vendor: 'ICompany', price: 'N/A' }, { id: 10, vendor: 'GCompany', price: '10.82' }];
array.sort(function (a, b) {
return (a.price === 'N/A') - (b.price === 'N/A') || a.price - b.price;
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Do you really need that N/A's in your output? Filter them out _.filter(arr, ({price}) => price !== 'N/A') and then pass the result to a sort function. A sort function shouldn't know about N/A I guess, it should just do the sorting.
You should check isNaN(parseFloat(x)) and isNaN(parseFloat(y)) before returning the result : if one is NaN then put it last
I wanted to sort the JSON object specific key values of first three characters.
Below is the sample JSON format
var obj = [{
id :12,
trackingNo: 'APQW123'},
{
id :13,
trackingNo: '123ABXU'},
{
id :12,
trackingNo: '98012JIH'},
{
id :12,
trackingNo: 'JHG567'}
];
In above object, based on first three chars of trackingNo need to sort the object. trackingNo is combination of alphanumeric.
I tried the below method. I thought it is numeric sort.It not correct one for this alphanumeric.
function sortObject(obj) {
var sortedData = obj.sort(function (a, b) {
return a.trackingNo > b.trackingNo;
});
return sortedData;
}
Could anyone please provide the logic to implement the alphanumeric sort based on first three characters in jasscript.
You could slice the first characters for sorting.
For checking if the value is not a number, a check for isNaN is included. Numbers are sorted first and then the other characters.
function sortTrackingNo(array, order) {
return array.sort(order === 'DESC'
? function (b, a) {
a = a.trackingNo.slice(0, 3);
b = b.trackingNo.slice(0, 3);
return isNaN(b) - isNaN(a) || a > b || -(a < b);
}
: function (a, b) {
a = a.trackingNo.slice(0, 3);
b = b.trackingNo.slice(0, 3);
return isNaN(a) - isNaN(b) || a > b || -(a < b);
});
}
var array = [{ id: 12, trackingNo: 'APQW123' }, { id: 13, trackingNo: '123ABXU' }, { id: 12, trackingNo: '98012JIH' }, { id: 12, trackingNo: 'JHG567' }];
sortTrackingNo(array);
console.log(array);
sortTrackingNo(array, 'DESC');
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have a very simple sorting function that sorts objects by index:
panoramas.sort((a, b) => {
if (a.index > b.index) return 1
})
Input:
[
{ index: 0 },
{ index: 2 },
{ index: 1 }
]
Output:
[
{ index: 1 },
{ index: 2 },
{ index: 3 }
]
The function works in Chrome and Firefox, but not in IE (the array isn't being sorted at all.)
Is there something wrong with my function?
The sorting function should return -1, 0 or 1 for the ordering.
// Your function tests for 'a.index > b.index'
// but it's missing the other cases, returning false (or 0)
panoramas.sort((a, b) => {
if (a.index > b.index) return 1;
if (a.index < b.index) return -1;
return 0;
})
from Sorting in JavaScript: Shouldn't returning a boolean be enough for a comparison function?
> 0 when a is considered larger than b and should be sorted after it
== 0 when a is considered equal to b and it doesn't matter which comes first
< 0 when a is considered smaller than b and should be sorted before it
for numbers, you can use a more concise approach:
panoramas.sort((a, b) => {
return a.index - b.index;
// but make sure only numbers are passed (to avoid NaN)
})
for IE11, that as noted by #teemu doesn't support arrow functions, you'll have to use a function expression:
http://caniuse.com/#feat=arrow-functions
panoramas.sort(function(a, b) {
return a.index - b.index;
});
I have four variables of type integer -
var tip_1, tip_2, tip_3, tip_4;
The value of these variables are getting fill by some other logic which is always between 1 to 10. I need to maintain a hash with variable name as "key" and value as "value" by following these rules -
The variable with MAX value should be the first element in the hash and so on. e.g.
if tip_1 = 4, tip_2 = 1, tip_3 = 2, tip_4 = 10 then hash should be something like,
Hash = {tip_4, 10} {tip_1, 4} {tip_3, 2} {tip_1, 1}
In case of tie following order should be considered -
tip_1 > tip_2 > tip_3 > tip_4;
You can always build your custom objects to retain all information instead of encoding them in indices. Makes sorting easier too.
function Tip(type, value) {
this.type = type;
this.value = value;
}
var tips = [];
tips.push(new Tip(3, 4));
tips.push(new Tip(2, 4));
tips.push(new Tip(1, 3));
tips.push(new Tip(4, 10));
tips.sort(function(a, b) {
// sort first by value, and if equal, then by type (index)
return (b.value - a.value) || (a.type - b.type);
});
console.log(tips); // 4=>10, 2=>4, 3=>4, 1=>3
Updated the example.
It's a lot easier to do if you make a real array out of your tips:
var tip_1 = 4, tip_2 = 1, tip_3 = 2, tip_4 = 10;
// create an array so we can sort it
var tips = [
{ key: 'tip_1', value: tip_1 },
{ key: 'tip_2', value: tip_2 },
{ key: 'tip_3', value: tip_3 },
{ key: 'tip_4', value: tip_4 },
];
tips.sort(function (a, b) {
// sort value descending
if (a.value < b.value) return 1;
if (a.value > b.value) return -1;
// if the values are equal, sort by key.
return a.key > b.key ? 1 : b.key < a.key ? -1 : 0;
});
// tips is now:
// [{key:"tip_4", value:10}, {key:"tip_1", value:4}, {key:"tip_2", value:2}, {key:"tip_3", value:2}]
// loop over the tips
for (var tip, i=0; tip = tips[i]; i++) {
console.log(tip.key+": "+tip.value);
}
// Outputs:
// tip_4: 10
// tip_1: 4
// tip_3: 2
// tip_2: 1