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; }
Related
Say for example I have a sorting function but I want to reverse it based on another variable.
What I know
if else
ternary ?
I was wondering if there was some kind of cool trick like (true && "return this") or !!(+num % 2) kind of stuff.
Example:
var array = [3,5,2,1,7,8,10];
var reverse = true;
array.sort( function(a,b) {
return a < b // somehow use reverse to sort it reverse order
});
Addendum
While David's answer was the one I was looking for in response to my question. This is what I think would be appropriate if I was looking to conditionally sort: Short circuiting.
var array = [3,5,2,1,7,8,10];
var reverse = true;
function number_sort(a,b) {
return a > b ;
}
(array.sort(number_sort) && reverse && array.reverse());
console.log(array);
reverse = false;
(array.sort(number_sort) && reverse && array.reverse());
console.log(array);
Seeing as you're just returning a boolean from your sort, you could return the boolean value of whether or not your boolean matches your reverse variable:
array.sort( function(a,b) {
return a < b == reverse
});
I would conditionally choose the sort function:
array.sort(reverse
? (a, b) => a - b
: (a, b) => b - a
);
Advantage is that condition is evaluated only once instead of for every item.
Otherwise, quite obvious:
array.sort(...);
if (reverse) {
array.reverse();
}
You could use a variable asc, which could have the value true for ascending sorting or false for descending sorting. Then use the value of asc as factor, if given or use -1 for descending sorting.
The factor -1 reverses the sort order as needed for the sort function.
var array = [3, 5, 2, 1, 7, 8, 10],
asc = false;
array.sort(function(a, b) { return (asc || -1) * (a - b); });
console.log(array);
asc = true;
array.sort(function(a, b) { return (asc || -1) * (a - b); });
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Version with a closure over the sort order
var sortFn = function (asc) {
return function(a, b) {
return (asc || -1) * (a - b);
};
},
array = [3, 5, 2, 1, 7, 8, 10];
array.sort(sortFn(false)); // desc
console.log(array);
array.sort(sortFn(true)); // asc
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can also use a ternary operator:
var array = [3,5,2,1,7,8,10];
var reverse = true;
array.sort( function(a,b) {
return reverse ? (a > b) : (a < b)
});
I have to write a function, which sorts an array containing numbers and strings.
For example:
uSort([3,"2", 4,1,"a","c","b"]) // -> ["a","b","c",1,"2",3, 4].
This is what I've tried so far:
function uSort(arrayOfChars){
var array = [];
for (i = 0; i < arrayOfChars.length; i++) {
if (typeof(arrayOfChars[i]) === '' ){
array.unshift(arrayOfChars[i]); }
else {
array.push(arrayOfChars[i]);
};
};
return array.sort();
};
But the result is wrong:
uSort([3,"2", 4,1,"a","c","b"]) // -> [1,"2",3, 4,"a","b","c"].
I can't figure out what is wrong with my code right now.
One easy way to do that would be to just split the array into two arrays, one containing numbers and strings that are numbers, using isNaN, and one array containing everything else, then sort them and join them back together
function uSort(arrayOfChars){
var numbs = arrayOfChars.filter(function(item) { return isNaN(item) });
var chars = arrayOfChars.filter(function(item) { return !isNaN(item) });
return numbs.sort().concat( chars.sort() );
};
FIDDLE
For better sorting of integers and special characters, you can add callbacks to the sorting
function uSort(arrayOfChars){
var numbs = arrayOfChars.filter(function(item) { return !isNaN(item) });
var chars = arrayOfChars.filter(function(item) { return isNaN(item) });
return chars.sort(function(a, b) {
return a.localeCompare(b);
}).concat( numbs.sort(function(a, b) {
return a == b ? 1 : a - b;
}));
};
FIDDLE
You can use a custom comparator function which checks if the arguments are numeric with isNaN and then uses numerical or lexicographic sort:
[3, "2", 4, 1, "a", "c", "b"].sort(function(a,b) {
if(isNaN(a) || isNaN(b)) {
if(!isNaN(a)) return +1; // Place numbers after strings
if(!isNaN(b)) return -1; // Place strings before numbers
return a < b ? -1 : (a > b ? +1 : 0); // Sort strings lexicographically
}
return a - b; // Sort numbers numerically
}); // ["a", "b", "c", 1, "2", 3, 4]
Write your own custom sort method.
[3,"2", 4,1,"a","c","b"].sort( function (a,b) {
var sa = isNaN(a);
var sb = isNaN(b);
if(sa && sb) { //If both are strings, than compare
return sa>sb;
} else if (!sa && !sb) { //if both are numbers, convert to numbers and compare
return Number(a) - Number(b);
} else { //if we have a number and a string, put the number last.
return sa ? -1 : 1;
}
});
It's normal to have numbers before letters (if this is your problem).
If you want to have letters before you have different ways.
One way is to order elements into two arrays (one for letters and one for numbers), then to merge them in the order you need.
Another way is to move the numbers at the end.
This is an example:
alert((uSort([3,"2", 4,10,"a","c","b"]))) // -> ["a","b","c",1,"2",3, 4].
function uSort(arrayOfChars){
var arrayNum = [];
var arrayLet = [];
for (i = 0; i < arrayOfChars.length; i++) {
if (typeof(arrayOfChars[i]) === '' ){
array.unshift(arrayOfChars[i]); }
else if (typeof(arrayOfChars[i]) === 'number' ){
arrayNum.push(arrayOfChars[i]);
} else {
arrayLet.push(arrayOfChars[i]);
}
};
if (arrayNum.size!=0 && arrayLet.size!=0) {
arrayNum = arrayNum.sort(sortNumber);
arrayNum.push();
return arrayNum.concat(arrayLet.sort());
} else if (arrayNum.size!=0) {
return arrayNum.sort(sortNumber);
} else {
return arrayLet.sort();
}
};
function sortNumber(a,b) {
return a - b;
}
I am sorting my array like this:
array.sort((function(index) {
return function(a, b){
return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 :1));
};
})(0));
As you can see, it is sorted in ascending order.
My question is how do I toggle sorting? For example, if it is already in ascending order then how can I sort it in descending order and vice-versa?
I know to sort in descending I need to modify code like this:
array.sort((function(index) {
return function(a, b) {
return (a[index] === b[index] ? 0 : (a[index] < b[index] ? 1 :-1));
};
})(0));
but I don't know how to toggle.
.reverse() will always reverse the order of an array, so on toggling, you just can call yourSortedArray.reverse()
var myArray = [1, 5, 8, 4, 0, 3, 6];
myArray.sort(); //[0, 1, 3, 4, 5, 6, 8]
myArray.reverse(); //[8, 6, 5, 4, 3, 1, 0]
How about:
var array = [ 2,4,7,12,1,5 ];
array.toggled_sort = function () {
var self=this;
this.asc=!this.asc;
return this.sort(function (l, r) {
return l > r ? (self.asc ? 1 : -1) : l < r ? (self.asc ? -1 : 1) : 0;
});
};
array.toggled_sort(); // ==> [ 1,2,4,5,7,12 ]
array.toggled_sort(); // ==> [ 12,7,5,4,2,1 ]
array.toggled_sort(); // ==> [ 1,2,4,5,7,12 ]
array.toggled_sort(); // ==> [ 12,7,5,4,2,1 ]
// etc.
You were on the right track, you needed a third closure to store the state of toggle.
function fn(reversed){
return function(){
reversed = !reversed;
return function(a,b){
return (a==b ? 0 : a < b? -1 : 1) * (reversed ? -1 : 1);
};
};
};
// usage
var toggleSort = fn();
array.sort(toggleSort())
jsfiddle: http://jsfiddle.net/8JMuj/1/
If you know for certain that array is sorted then you can reverse the order by using a simple loop
var l = array.length;
for(i=0; i< l / 2; i++) {
var t = array[i];
array[i] = array[l - 1 - i];
array[l - 1 - i] = t;
}
More simpler solution is to use reverse function (BTW, check this SO Q&A for different reversing algo and their performance)
If you don't know the initial state of you array then I will advise associating a custom property to an array that will track the sort order. For example,
function sortArray(a, isAscending) {
var currentSort = a["my_sort_order"];
if (typeof currentSort != 'boolean') {
// assume it be unsorted, use sort alogorithm
a.sort(function(a,b) { return isAscending ? a - b : b - a; }); // assuming numerical array, modify as per your needs
} else if (currentSort != isAscending) {
// sorted but in different order, reverse the order
a.reverse(); // or use for loop
}
// set the sort order
a["my_sort_order"] = isAscending ? true : false;
}
//Array sort by its object property can be implemented as this where sortType variable is used as the toggle parameter
//sortElement is the object property can be used as variable if you need to select the property that which is used for sorting
//to sort by date property var elem1Var = new Date(elem1[sortElement]);
//to sort by string property var elem1Var =elem1[sortElement].toUpperCase();
var sortMethod = -1;
if(self.sortType == 'ASC'){
sortMethod = 1;
}
dataArr.sort(function(elem1,elem2){
var elem1Var = elem1[sortElement];
var elem2Var = elem2[sortElement];
if(elem1Var < elem2Var){
return -1*sortMethod;
}
if(elem1Var > elem2Var){
return 1*sortMethod;
}
return 0;
});
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
var arr = [];
arr.push(row1);
arr.push(row2);
...
arr.push(rown);
How to sort by row['key']?
A JavaScript array has a built-in sort() method. In this case, something like the following would work:
arr.sort( function(row1, row2) {
var k1 = row1["key"], k2 = row2["key"];
return (k1 > k2) ? 1 : ( (k2 > k1) ? -1 : 0 );
} );
You call the sort function of an array with your comparator. A JavaScript comparator is just a function that returns -1, 0, or 1 depending on whether a is less than b, a is equal to b, or a is greater than b:
myarray.sort(function(a,b){
if(a < b){
return -1;
} else if(a == b){
return 0;
} else { // a > b
return 1;
}
});
This is just an example, your function can base the comparison on whatever you want, but it needs to return -1,0,1.
Hope this helps.
Here is set of functions if you want to sort asending, descending, or sort on multiple columns in an array.
var cmp = function(x, y){ return x > y? 1 : x < y ? -1 : 0; },
arr = [{a:0,b:0},{a:2,b:1},{a:1,b:2},{a:2, b:2}];
// sort on column a ascending
arr.sort(function(x, y){
return cmp( cmp(x.a, y.a), cmp(y.a, x.a) );
});
// sort on column a descending
arr.sort(function(x, y){
return cmp( -cmp(x.a, y.a), -cmp(y.a, x.a) );
});
// sort on columns a ascending and b descending
arr.sort(function(x, y){
return cmp([cmp(x.a, y.a), -cmp(x.b, y.b)], [cmp(y.a, x.a), -cmp(y.b,x.b)]);
});
To get an ascending sort, use "cmp(...)", and to get a descending sort, use "-cmp(...)"
And to sort on multiple columns, compare two arrays of cmp(...)
Consider the following code:
var arr = new Array();
for(var i = 0; i < 10; ++i) {
var nestedArray = [ "test", Math.random() ];
arr.push(nestedArray);
}
function sortBySecondField(a, b) {
var aRandom = a[1];
var bRandom = b[1];
return ((aRandom < bRandom) ? -1 : ((aRandom > bRandom) ? 1 : 0));
}
arr.sort(sortBySecondField);
alert(arr);
Now just change a sortBySecondField function to compare a['key'] instead of a[1] and do the same for b.