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)
});
Related
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;
}
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; }
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 like this:
arr = []
arr[0] = "ab"
arr[1] = "abcdefgh"
arr[2] = "abcd"
After sorting, the output array should be:
arr[0] = "abcdefgh"
arr[1] = "abcd"
arr[2] = "ab"
I want in the descending order of the length of each element.
You can use Array.sort method to sort the array. A sorting function that considers the length of string as the sorting criteria can be used as follows:
arr.sort(function(a, b){
// ASC -> a.length - b.length
// DESC -> b.length - a.length
return b.length - a.length;
});
Note: sorting ["a", "b", "c"] by length of string is not guaranteed to return ["a", "b", "c"]. According to the specs:
The sort is not necessarily stable (that is, elements that compare
equal do not necessarily remain in their original order).
If the objective is to sort by length then by dictionary order you must specify additional criteria:
["c", "a", "b"].sort(function(a, b) {
return a.length - b.length || // sort by length, if equal then
a.localeCompare(b); // sort by dictionary order
});
We can use Array.sort method to sort this array.
ES5 solution
var array = ["ab", "abcdefgh", "abcd"];
array.sort(function(a, b){return b.length - a.length});
console.log(JSON.stringify(array, null, '\t'));
For ascending sort order: a.length - b.length
For descending sort order: b.length - a.length
ES6 solution
Attention: not all browsers can understand ES6 code!
In ES6 we can use an arrow function expressions.
let array = ["ab", "abcdefgh", "abcd"];
array.sort((a, b) => b.length - a.length);
console.log(JSON.stringify(array, null, '\t'));
With modern JavaScript you can do like this:
Descending order
const arr = [
"ab",
"abcdefgh",
"abcd",
"abcdefghijklm"
];
arr.sort((a, b) => b.length - a.length);
console.log(JSON.stringify(arr, null, 2));
Ascending Order - Just switch the a with b
const arr = [
"ab",
"abcdefgh",
"abcd",
"abcdefghijklm"
];
arr.sort((a, b) => a.length - b.length);
console.log(JSON.stringify(arr, null, 2));
Here is the sort, depending on the length of a string with javascript using Bubble sort as you asked:
var arr = ['1234', '12', '12345', '1'];
bubbleSort(arr );
function bubbleSort(a) {
var swapped;
do {
swapped = false;
for (var i = 0; i < a.length - 1; i++) {
if (a[i].length < a[i + 1].length) {
var temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
swapped = true;
}
}
} while (swapped);
}
console.log(arr );
#created a sorting function to sort by length of elements of list
def sort_len(a):
num = len(a)
d = {}
i = 0
while i<num:
d[i] = len(a[i])
i += 1
b = list(d.values())
b.sort()
c = []
for i in b:
for j in range(num):
if j in list(d.keys()):
if d[j] == i:
c.append(a[j])
d.pop(j)
return c
If you want to preserve the order of the element with the same length as the original array, use bubble sort.
Input = ["ab","cdc","abcd","de"];
Output = ["ab","cd","cdc","abcd"]
Function:
function bubbleSort(strArray){
const arrayLength = Object.keys(strArray).length;
var swapp;
var newLen = arrayLength-1;
var sortedStrArrByLenght=strArray;
do {
swapp = false;
for (var i=0; i < newLen; i++)
{
if (sortedStrArrByLenght[i].length > sortedStrArrByLenght[i+1].length)
{
var temp = sortedStrArrByLenght[i];
sortedStrArrByLenght[i] = sortedStrArrByLenght[i+1];
sortedStrArrByLenght[i+1] = temp;
swapp = true;
}
}
newLen--;
} while (swap);
return sortedStrArrByLenght;
}
let arr = [5,2,100,1,20,3];
arr.sort((a,b)=>{
return a-b
})
console.log(arr) //[1, 2, 3, 5, 20, 100]
on the return value, the sort method will perform the functionality of swapping of an elements
return < 0 { i.e -ve number then a comes before b}
return > 0 { i.e +ve number then b comes before a}
return == 0 { order of a and b remains same }
Based on Salman's answer, I've written a small function to encapsulate it:
function sortArrayByLength(arr, ascYN) {
arr.sort(function (a, b) { // sort array by length of text
if (ascYN) return a.length - b.length; // ASC -> a - b
else return b.length - a.length; // DESC -> b - a
});
}
then just call it with
sortArrayByLength( myArray, true );
Note that unfortunately, functions can/should not be added to the Array prototype, as explained on this page.
Also, it modified the array passed as a parameter and doesn't return anything. This would force the duplication of the array and wouldn't be great for large arrays. If someone has a better idea, please do comment!
I adapted #shareef's answer to make it concise. I use,
.sort(function(arg1, arg2) { return arg1.length - arg2.length })
This code should do the trick:
var array = ["ab", "abcdefgh", "abcd"];
array.sort(function(a, b){return b.length - a.length});
console.log(JSON.stringify(array, null, '\t'));
let array = [`ab`, `abcdefgh`, `abcd`];
let newArray = array.sort((a,b) => {
return b.length - a.length
})
console.log(newArray);
Please the following code
<script>
arr = []
arr[0] = "ab"
arr[1] = "abcdefgh"
arr[2] = "sdfds"
arr.sort(function(a,b){
return a.length<b.length
})
document.write(arr)
</script>
The anonymous function that you pass to sort tells it how to sort the given array.hope this helps.I know this is confusing but you can tell the sort function how to sort the elements of the array by passing it a function as a parameter telling it what to do
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.