0

I need to order an array of objects by the property price.

Right now I have the following, which works as intended:

function orderByPriceASC(a,b) {
    return(
      a.price < b.price ? -1
      : a.price > b.price ? 1
        : 0
    );
  }

  function orderByPriceDESC(a,b) {
    return(
      a.price < b.price ? 1
      : a.price > b.price ? -1
        : 0
    );
  }

  function sortByPrice(order) {
    setProductList((prevState) => {
      const aux = Array.from(prevState);
      order === 'asc' ? aux.sort(orderByPriceASC) : aux.sort(orderByPriceDESC);
      return aux;
    });
  }

But can is there a way that I can structure this so I can get a single compare function that works for both ASC and DESC order?

Something like:

function orderByPrice(a,b,order) {
    return(
      a.price < b.price ?
        order === 'asc' ? -1 : 1
      : a.price > b.price ? 1
        order === 'asc' ? 1 : -1
        : 0
    );
  }

The problem is that I would have to send that extra parameter down to the Array.sort method which I don't think it's possible. Maybe with some wrapper function.

How can this be implemented?

Panther
  • 8,938
  • 3
  • 23
  • 34
cbdeveloper
  • 27,898
  • 37
  • 155
  • 336
  • Check [this](https://stackoverflow.com/questions/8537602/any-way-to-extend-javascripts-array-sort-method-to-accept-another-parameter). – Deepak Biswal May 29 '19 at 17:23

5 Answers5

3

You can use a multiplier and set it to 1 or -1 based on the order variable. Then multiply it to the existing expression inside compareFunction (Here I'm assuming price is of type number)

let arr = [{ price: 2 },{ price: 1 },{ price: 3 }]

function sort(array, order) {
  const multiplier = order === "asc" ? 1 : -1;
  return array.sort((a, b) => multiplier * (a.price - b.price))
}

console.log(sort(arr, "asc"))
console.log(sort(arr, "desc"))
adiga
  • 34,372
  • 9
  • 61
  • 83
1

You can define the function and pass it to sort by binding it and sending a custom param like

let arr = [{price: 1},{price: 11},{price: 7},{price: 1},{price: 9},{price: 5},]
function orderByPrice(order, a,b) {

        if(a.price < b.price) {
            if(order === 'asc') {
                return -1;
            }
            return 1;
        } else if(a.price > b.price){
            if(order === 'asc') {
                return 1;
            }
            return -1
        }
        return 0;
      }
    
console.log(arr.sort(orderByPrice.bind(this, 'asc')))
console.log(arr.sort(orderByPrice.bind(this, 'desc')))
Jagrati
  • 11,474
  • 9
  • 35
  • 56
1

You could just do this:

function flip(f) {
    return function (a, b) {
        return f(b, a);
    };
}

function orderByPriceASC(a,b) {
    return (
        a.price < b.price ? -1 :
        a.price > b.price ? 1 :
        0
    );
}

function sortByPrice(order) {
    setProductList((prevState) => {
        const aux = Array.from(prevState);
        aux.sort(order === 'asc' ? orderByPriceASC : flip(orderByPriceASC));
        return aux;
    });
}

This way you don't need a separate orderByPriceDESC function and you don't have to add any extra parameters either.

melpomene
  • 84,125
  • 8
  • 85
  • 148
0

You have to have the function return a function that accepts the direction as an argument, which is then passed into the inner function. I'm not a fan of flags in dynamically typed languages, but it would be something like this:

function orderByPrice(direction) { // either 'ASC' or 'DESC
  return function (a, b) {
    if (direction === 'DESC') {
      const temp = a;
      a = b
      b = temp;
    }
    return (
      a.price < b.price ? -1
        : a.price > b.price ? 1
          : 0
    );
  }
}

Then implementation would be

  function sortByPrice(direction) {
    setProductList((prevState) => {
      return Array.from(prevState).sort(orderByPrice(direction));
    });
  }
Andrew
  • 7,201
  • 5
  • 25
  • 34
0

You can do something like this with Array.sort and Array.slice:

let arr = [{ price: 2 },{ price: 1 },{ price: 3 }]

let sortBy = (arr, prop, dir=null) => 
  arr.slice().sort((a,b) => (a[prop] - b[prop]) * (dir ? -1 : 1))

console.log(sortBy(arr, 'price'))
console.log(sortBy(arr, 'price', 'desc')) // <-- any truthy value for `desc`

You have to use Array.slice to clone the array since Array.sort does mutate the array and you want a pure function. From the documentation:

The sorted array. Note that the array is sorted in place, and no copy is made.

In addition if you are using lodash there is a _.orderBy function for that already which also allows sorting on multiple props etc:

let arr = [{ price: 2 },{ price: 1 },{ price: 3 }]

console.log(_.orderBy(arr, 'price'))
console.log(_.orderBy(arr, 'price', 'desc'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Akrion
  • 18,117
  • 1
  • 34
  • 54