1

I am trying to return a list of sorted products:

return products.sort(getSortFunc(activeSortId))

Each activeSortId corresponds to a sorting callback function which is retrieved by calling getSortFunc. I have three sorting function: sortByPriceAsc, sortByPriceDesc, sortByDate.

The problem: When the user sortByPriceDesc followed by sortByDate the result is (logically) sorted by descending price still. I would however like it to be sorted by ascending price. I could solve this by chaining the sorting function (return products.sort(a).sort(b).sort(c)), but what if I don't know the order?:

I'm wondering if I can solve it by calling e.g. sortByPriceAsc within the function sortByDate, or something similar? Below is a simplified attempt, but obviously it does not work because calling sortByPriceAsc doesn't modify anything:

sortByPriceAsc: (a, b) => a.price - b.price,
sortByDate: (a, b) => {
  this.sortByPriceAsc()
  return b.date - a.date
}

Glad for any help or general critique.

1
  • I could solve this by chaining the sorting function - in general, no, because sort isn't guaranteed to be stable. Commented Oct 5, 2019 at 12:03

1 Answer 1

0

One option is to have a persistent variable indicating the last asc/dec sort order. Then, in sortByDate, whenever it's called, check that variable to figure out what to return in the case the two dates are the same:

const arr = [
  { date: 1, price: 5 },
  { date: 2, price: 10 },
  { date: 2, price: 20 },
  { date: 3, price: 30 },
  { date: 3, price: 40 },
  { date: 4, price: 50 },
];

let lastWasAsc = false;
const sorts = {
  sortByPriceAsc: (a, b) => a.price - b.price,
  sortByDate: (a, b) => {
    return b.date - a.date || (
      lastWasAsc
      ? a.price - b.price
      : b.price - a.price
    );
  }
};

// When sorting by ascending order, set lastWasAsc to true:
lastWasAsc = true;
arr.sort(sorts.sortByPriceAsc);

// Then, later:
arr.sort(sorts.sortByDate);
console.log(arr);

Compare to, when lastWasAsc is false when sortByDate is called:

const arr = [
  { date: 1, price: 5 },
  { date: 2, price: 10 },
  { date: 2, price: 20 },
  { date: 3, price: 30 },
  { date: 3, price: 40 },
  { date: 4, price: 50 },
];

let lastWasAsc = false;
const sorts = {
  sortByPriceAsc: (a, b) => a.price - b.price,
  sortByDate: (a, b) => {
    return b.date - a.date || (
      lastWasAsc
      ? a.price - b.price
      : b.price - a.price
    );
  }
};

// Then, later:
arr.sort(sorts.sortByDate);
console.log(arr);

Keep in mind that arr.sort(cb1).sort(cb2) will not necessarily take into account anything sorted by cb1 - the sorting algorithm is not necessarily stable, so behavior will differ across implementations (and thus .sort(..).sort( should not be used).

(A stable sorting algorithm is when, when two elements are determined to be next to each other in the result due to having the same "weight", they are in the same order in the output that they were in the input. This is not guaranteed in Javascript. While ES2019 requires Array#sort to be stable, not all implementations comply with that yet.)

Sign up to request clarification or add additional context in comments.

7 Comments

Reading return b.date - a.date || (lastWasAsc ? a.price - b.price : b.price - a.price); out loud make it seem like it either sorts by date or by price, I guess this is not the case? So basically we sort by date, then also change the order of the price if it was ascending last? I'm just trying to understand what the sortByDate do. Thanks so much for your reply!
That conditional operator means: if the date difference comes out to 0, then return the following: if lastWasAsc, return a.price - b.price, else return b.price - a.price. It only sorts by relative price between two items if there is no difference in those two items' dates.
I don't completely understand. If it only does this part: (lastWasAsc ? a.price - b.price : b.price - a.price) if the date difference is 0 then it only does this when the dates are the same? No dates are the same in your example so it would seem as that part of the code would never execute.
There are two pairs of objects with the same date in the example: { date: 2, price: 10 }, { date: 2, price: 20 }, and { date: 3, price: 30 }, { date: 3, price: 40 }. In the first snippet, the objects with the lower price come first, whereas the reverse is true for the second snippet - which was the crux of your question (the snippets look to produce your desired functionality, right?).
Your code seem to do the right thing, but I don't understand it enough to reproduce it. I actually mixed up some function names in the questions. What I want is the result to be sorted in ascending order when the date function is applied (the other way around). I can't seem to adjust your conditional statement to achieve that..
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.