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?

1
  • Check this. Commented May 29, 2019 at 17:23

5 Answers 5

2

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"))

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

Comments

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')))

Comments

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.

Comments

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>

Comments

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));
    });
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.