Sorting an array of objects on both ascending and descending order on same click

Viewed 3846

I have an array of objects:

var arr = [{
  title: "finance",
  book: "book1"
},
{
  title: "nature",
  book: "book2"
},
{
  title: "programming",
  book: "book3"
}]

HTML:

<th><a href="#" ng-click="controller.sortTitle()">TITLE</a></th>

Right now I have implemented a basic sorting logic below

JAVASCRIPT(AngularJS controller):

self.sortTitle = function () {
    self.arr= self.arr.sort((a, b) => (a.title > b.title) ? 1 : -1);
}

This JavaScript function is currently sorting it in ascending order only based on title. How do I write a logic that sorts it on either order, i.e. click1 should sort it in ascending and click2 should reverse and set it in descending? Basically reversing the sort everytime it is clicked.

6 Answers

You can create a property on self like isAscending and default to true or false (whatever you want initially) based on which sort order will be executed and flip isAscending every time the sort fn runs.

self.isAscending = true;

self.sortTitle = function () {
 if(self.isAscending){ 
   self.arr= self.arr.sort((a, b) => (a.title > b.title) ? 1 : -1);
 }else{
   self.arr= self.arr.sort((a, b) => (a.title > b.title) ? -1 : 1);
 }
 self.isAscending = !self.isAscending;
}

It probably would make sense to have a reusable function you can call to do the sorting for you which also accepts a direction parameter:

var arr = [{ title: "finance", book: "book1" }, { title: "nature", book: "book2" }, { title: "programming", book: "book3" }]

let sortBy = (arr, p, o=1) => [...arr].sort((a,b) => a[p].localeCompare(b[p]) * o)

console.log(sortBy(arr, 'title'))
console.log(sortBy(arr, 'title', -1))
console.log(sortBy(arr, 'book'))
console.log(sortBy(arr, 'book', -1))

You can replace the last param to deal with asc/desc if you need to.

Also since you are comparing strings the recommended way is to use String.localeCompare simply due to the amount of options it supports which almost always come in handy (sorting book10 vs book1 for example)

You just need to maintain sort type somewhere so that you can toggle it.

There's lots of variants, but something simple like this should work:

const ascendingComparator = (a, b) => (a.title > b.title) ? 1 : -1;
const descendingComparator = (a, b) => (a.title > b.title) ? -1 : 1;

self.sortTitle = function (type) {
  if (type === "ascending") {
    self.arr = self.arr.sort(ascendingComparator);
    type = "descending";
  } else {
    self.arr = self.arr.sort(descendingComparator);
    type = "ascending";
  }
}

Add a variable to your function and change the conditional

self.sortTitle = function (sortOrder) {
    if (sortOrder === 'asc') {
        self.arr= self.arr.sort((a, b) => (a.title > b.title) ? 1 : -1);
    } else { //desc
        self.arr= self.arr.sort((a, b) => (a.title < b.title) ? 1 : -1);
    }
}

Create a boolean variable that is set to false. In your sortTitle() function create a condition that will detect whether it's true or false and then run the opposite when it's true.

You can declare an auxiliary variable to help you decide which is the next direction in which you need to sort your array. Somehting like this:

self.sortTitle = function () {
    sortArrayAsc = !sortArrayAsc;
    self.arr= self.arr.sort((a, b) => sort(a,b, sortArrayAsc));
}

// ToDo: You can writte this nicer
self.sort = function (a, b, ascending) {
    if(ascending) {
        return a.title > b.title ? 1 : -1;
    } else {
        return a.title > b.title ? -1 : 1;
    }
}

Related