Sorting function not working in IE 11 - javascript

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

Related

custom sorting v-data-table with null values last

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.

Custom sorting array of objects

I have an array of objects like so for example:
[ {
id: '-1'
},
{
id: '10'
},
{
id: '1234'
},
{
id: '1235'
},
{
id: '-1'
} ]
I would like to sort this array so that it is ordered by the ids ascending (smallest to largest), however I would like objects that have the id of '-1' to be sent to the back of the array. So I tried this:
const arr = [ { id: '-1' }, { id: '10'}, { id: '1234' }, { id: '1235' }, { id: '-1' } ]
arr.sort((a, b) => {
if (parseInt(a.id) === -1 || parseInt(b.id) === -1) return 1;
if (parseInt(a.id) - parseInt(b.id)) return -1;
});
console.log(arr);
As you can see from the snippet above it sorts them successfully ascending however it doesn't successfully move ids with '-1' to the back of the array.
Why does this happen? What am I doing wrong?
You could take the function with a check for the items which should be sorted to bottom.
var array = [{ id: '-1' }, { id: '10' }, { id: '1234' }, { id: '1235' }, { id: '-1' }];
array.sort((a, b) => (a.id === '-1') - (b.id === '-1') || a.id - b.id);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
How sorting works
First, let's take a look at what .sort expects you to feed it.
Given paramaters (a, b) -- we should return
< 0 if a < b
> 0 if a > b
How you did the sorting
Let's take a look at your first line
if (parseInt(a.id) === -1 || parseInt(b.id) === -1) return 1;
-1 means "a is smaller". But that might not be true. It might be that a (not b) is -1, and is bigger, yet we're always telling JavaScript to send a to the back when that's what we should be doing to b.
Instead, we want to return -1 when b is -1, but +1 when a is -1. It doesn't really matter what we return if they're both -1.
In JavaScript, any number except for 0 is truthful. Let's take a look at your next line.
if (parseInt(a.id) - parseInt(b.id)) return -1;
If a - b is 0, we don't return anything in this callback. If it isn't 0, we always say that a < b.
Notice that never do we say that b < a -- so if such an event were to occur, we couldn't handle it and would sort it incorrectly.
How to sort correctly
Fetch IDs
const aID = parseInt(a.id)
const bID = parseInt(b.id)
Is a or b -1?
if(aID === -1) return +1;
if(bID === -1) return -1;
Which is bigger, a or b
If you assume that a and b are not -1, then we can simply subtract a - b. Why? If a is bigger than b, then the subtraction will create a positive number to say b < a. If a is less than b, then the subtraction will create a negative number, to say a < b.
return aID - bID
All together
const arr = [ { id: '-1' }, { id: '10'}, { id: '1234' }, { id: '1235' }, { id: '-1' } ]
arr.sort((a, b) => {
const [aID, bID] = [parseInt(a.id), parseInt(b.id)]
if(aID === -1) return +1
if(bID === -1) return -1
return aID - bID
});
console.log(arr);
Golfing
It might be helpful to make things shorter. Another answer, by #Nina Scholz, helpfully showed a much shorter version. I thought it might be useful to explain why this works.
return (a.id === '-1') - (b.id === '-1') || a.id - b.id
What is x || y
x || y means:
if x is truthful, return x.
if x isn't truthful, return y.
What is (aID === -1)
This means true if aID is -1, and false otherwise
What is (aID === -1) - (bID === -1)
How can you subtract true and false? true will be interpreted as 1, and false as 0.
aID = -1, bID = not
This value will be 1 - 0, or +1
aID = not, bID = -1
This value will be 0 - 1, or -1
aID = -1, bID = -1
Remember, it doesn't matter what we return if the two values are the same
aID = not, bID = not
0 - 0. This is 0. This is not a truthful value. So we go into the || bit. Which, in that answer, has the second bit be aID - bID, as described above. It's very clever and very short, though might not be as readable.

JavaScript Object Sort method

In the compareNumeric fn I dont understand this concept with comparison.
To see more detailed: if (5 > 3) return 1; So what is this return 1 and return -1; how does it work, and how does it affect on sort method. Please , help me out!
function compareNumeric(a, b) {
if (a > b) return 1;
if (a < b) return -1;
}
var arr = [ 1, 2, 15, 14, 66, 434, 112, 3 ];
arr.sort(compareNumeric);
alert(arr); // sorted array
MDN's documentation for Array.prototype.sort will help. Basically, sort calls the callback repeatedly, with various combinations of two entries from the array. The callback's return value tells sort whether 1) a should be before of b in the result (by returning a negative value), or 2) a should be after b in the result (by returning a positive value), or 3) a and b are equivalent for sorting purposes so it doesn't matter (by returning 0).
Your example compareNumeric has a bug: It should return 0 if a is neither < nor > b, but instead it doesn't return anything, so calling it results in undefined. Instead:
function compareNumeric(a, b) {
if (a > b) { return 1; }
if (a < b) { return -1; }
return 0;
}
But, it has another problem: It never checks whether a and b are actually numeric. If the author is happy to assume they're numeric, it can be a simpler function:
function compareNumeric(a, b) {
return a - b;
}
If both a and b are numbers, then a - b will be negative if a is less than b and so should be before it in the result, positive if a is greater than b and so should be after it in the result, or 0 if a and b are equal.
Note that the number of times the callback gets called and in what order are not defined by the specification; all it says is that Array#sort will call the callback as necessary and use the resulting information.
Let's watch what arguments Array#sort gives with your example; again note that this will be implementation-dependent:
function compareNumeric(a, b) {
if (a > b) return 1;
if (a < b) return -1;
return 0;
}
var arr = [ 1, 2, 15, 14, 66, 434, 112, 3 ];
arr.sort(function(a, b) {
var result = compareNumeric(a, b);
console.log("a = " + a + ", b = " + b + "; returning " + result);
return result;
});
console.log(arr.join()); // sorted array
.as-console-wrapper {
max-height: 100% !important;
}

.sort() method not working

function isSorted(set) {
if(set == set.sort()) {
return true;
} else {
return false;
}
}
This function is supposed to check if an array is sorted properly, but no matter what, it still returns true.
From MDN:
The sort() method sorts the elements of an array in place and returns the array.
After set.sort() is invoked, set itself has been sorted, so set will always equal set.sort().
Reference:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
As pointed out in the comments, == compares by reference and hence still refers to the same array. Thereby the condition is always true
Use the below method to ensure if the array is sorted or not. Found the method form here
/*
* check the array is sorted
* return: if positive (ascending) -> 1
* if negative (descending) -> -1
* not sorted -> 0
*/
Array.prototype.isSorted = function() {
return (function(direction) {
return this.reduce(function(prev, next, i, arr) {
if (direction === undefined)
return (direction = prev <= next ? 1 : -1) || true;
else
return (direction + 1 ?
(arr[i-1] <= next) :
(arr[i-1] > next));
}) ? Number(direction) : false;
}).call(this);
}
var arr = [3,2,1,0];
arr.isSorted();
Or you can create a similar function like this using the above code
/*
* check the array is sorted
* return: if positive (ascending) -> 1
* if negative (descending) -> -1
* not sorted -> 0
*/
function isSorted(myArray) {
return (function(direction) {
return myArray.reduce(function(prev, next, i, arr) {
if (direction === undefined)
return (direction = prev <= next ? 1 : -1) || true;
else
return (direction + 1 ?
(arr[i-1] <= next) :
(arr[i-1] > next));
}) ? Number(direction) : false;
}).call(myArray);
}
var arr = [3,2,1,0];
isSorted(arr);
I suggest to use another way to check, if an array is sorted. You could use a compareFunction as described by Array#sort and use it as check for test as sorted with Array#every
For example, if you have numbers and a compareFunction like
var greaterOrEqual = function (a, b ) { return a - b; }
you could use the function to sort an array with number like
array.sort(greaterOrEqual);
and you could use the following pattern for checking every element
var isSorted = function (compareFunction) {
return function (a, i, aa) {
return !(compareFunction(aa[i - 1], a) > 0);
};
};
and then use it with an array, like
var isSorted = array.every(isSorted(greaterOrEqual));
A working example
var greaterOrEqual = function (a, b ) { return a - b; },
isSorted = function (compareFunction) {
return function (a, i, aa) {
return !(compareFunction(aa[i - 1], a) > 0);
};
};
console.log([42].every(isSorted(greaterOrEqual))); // true
console.log([42, 42].every(isSorted(greaterOrEqual))); // true
console.log([1, 2, 3, 4, 5].every(isSorted(greaterOrEqual))); // true
console.log([1, 2, 5, 4, 3].every(isSorted(greaterOrEqual))); // false
.as-console-wrapper { max-height: 100% !important; top: 0; }

Javascript sort function. Sort by First then by Second

I have an array of objects to sort. Each object has two parameters: Strength and Name
objects = []
object[0] = {strength: 3, name: "Leo"}
object[1] = {strength: 3, name: "Mike"}
I want to sort first by Strength and then by name alphabetically. I am using the following code to sort by the first parameter. How do I sort then by the second?
function sortF(ob1,ob2) {
if (ob1.strength > ob2.strength) {return 1}
else if (ob1.strength < ob2.strength){return -1}
return 0;
};
Thanks for your help.
(I am using Array.sort() with the aforementioned sortF as the sort comparison function passed into it.)
Expand your sort function to be like this;
function sortF(ob1,ob2) {
if (ob1.strength > ob2.strength) {
return 1;
} else if (ob1.strength < ob2.strength) {
return -1;
}
// Else go to the 2nd item
if (ob1.name < ob2.name) {
return -1;
} else if (ob1.name > ob2.name) {
return 1
} else { // nothing to split them
return 0;
}
}
A < and > comparison on strings is an alphabetic comparison.
This little function is often handy when sorting by multiple keys:
cmp = function(a, b) {
if (a > b) return +1;
if (a < b) return -1;
return 0;
}
or, more concisely,
cmp = (a, b) => (a > b) - (a < b)
Which works because in javascript:
true - true // gives 0
false - false // gives 0
true - false // gives 1
false - true // gives -1
Apply it like this:
array.sort(function(a, b) {
return cmp(a.strength,b.strength) || cmp(a.name,b.name)
})
Javascript is really missing Ruby's spaceship operator, which makes such comparisons extremely elegant.
You could chain the sort order with logical OR.
objects.sort(function (a, b) {
return a.strength - b.strength || a.name.localeCompare(b.name);
});
When I was looking for an answer to this very question, the answers I found on StackOverflow weren't really what I hoped for. So I created a simple, reusable function that does exactly this. It allows you to use the standard Array.sort, but with firstBy().thenBy().thenBy() style.
https://github.com/Teun/thenBy.js
PS. This is the second time I post this. The first time was removed by a moderator saying "Please don't make promotional posts for your own work". I'm not sure what the rules are here, but I was trying to answer this question. I'm very sorry that it is my own work. Feel free to remove again, but please point me to the rule involved then.
steve's answer, but prettier.
objects.sort(function(a,b)
{
if(a.strength > b.strength) {return 1;}
if(a.strength < b.strength) {return -1;}
if(a.name > b.name ) {return 1;}
if(a.name < b.name ) {return -1;}
return 0;
}
function sortF(ob1,ob2) {
if (ob1.strength > ob2.strength) {return 1}
else if (ob1.strength < ob2.strength) {return -1}
else if (ob1.name > ob2.name) {return 1}
return -1;
};
EDIT: Sort by strength, then if strength is equal, sort by name.
The case where strength and name are equal in both objects doesn't need to be accounted for seperately, since the final return of -1 indicates a less-than-or-equal-to relationship. The outcome of the sort will be correct. It might make it run faster or slower, I don't know. If you want to be explicit, just replace
return -1;
with
else if (ob1.name < ob2.name) {return -1}
return 0;
Find 'sortFn' function below. This function sorts by unlimited number of parameters(such as in c#: SortBy(...).ThenBy(...).ThenByDesc(...)).
function sortFn() {
var sortByProps = Array.prototype.slice.call(arguments),
cmpFn = function(left, right, sortOrder) {
var sortMultiplier = sortOrder === "asc" ? 1 : -1;
if (left > right) {
return +1 * sortMultiplier;
}
if (left < right) {
return -1 * sortMultiplier;
}
return 0;
};
return function(sortLeft, sortRight) {
// get value from object by complex key
var getValueByStr = function(obj, path) {
var i, len;
//prepare keys
path = path.replace('[', '.');
path = path.replace(']', '');
path = path.split('.');
len = path.length;
for (i = 0; i < len; i++) {
if (!obj || typeof obj !== 'object') {
return obj;
}
obj = obj[path[i]];
}
return obj;
};
return sortByProps.map(function(property) {
return cmpFn(getValueByStr(sortLeft, property.prop), getValueByStr(sortRight, property.prop), property.sortOrder);
}).reduceRight(function(left, right) {
return right || left;
});
};
}
var arr = [{
name: 'marry',
LocalizedData: {
'en-US': {
Value: 10000
}
}
}, {
name: 'larry',
LocalizedData: {
'en-US': {
Value: 2
}
}
}, {
name: 'marry',
LocalizedData: {
'en-US': {
Value: 100
}
}
}, {
name: 'larry',
LocalizedData: {
'en-US': {
Value: 1
}
}
}];
document.getElementsByTagName('pre')[0].innerText = JSON.stringify(arr)
arr.sort(sortFn({
prop: "name",
sortOrder: "asc"
}, {
prop: "LocalizedData[en-US].Value",
sortOrder: "desc"
}));
document.getElementsByTagName('pre')[1].innerText = JSON.stringify(arr)
pre {
font-family: "Courier New" Courier monospace;
white-space: pre-wrap;
}
Before:
<pre></pre>
Result:
<pre></pre>
With ES6 you can do
array.sort(function(a, b) {
return SortFn(a.strength,b.strength) || SortFn(a.name,b.name)
})
private sortFn(a, b): number {
return a === b ? 0 : a < b ? -1 : 1;
}
Here is the function I use. It will do an arbitrary number.
function Sorter(){
var self = this;
this.sortDefs = [];
for (let i = 0; i < arguments.length; i++) {
// Runs 5 times, with values of step 0 through 4.
this.sortDefs.push(arguments[i]);
}
this.sort = function(a, b){
for (let i = 0; i < self.sortDefs.length; i++) {
if (a[self.sortDefs[i]] < b[self.sortDefs[i]]) {
return -1;
} else if (a[self.sortDefs[i]] > b[self.sortDefs[i]]) {
return 1
}
}
return 0;
}
}
data.sort(new Sorter('category','name').sort);
In 2018 you can use just sort() ES6 function, that do exactly, what you want.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Categories