JS_Dev
JS_Dev

Reputation: 537

Order array of objects working for number but not string

I've created a util function to sort an array of objects in either ascending or descending order which accepts the property. It appears to work fine for sorting numeric, but not string properties. E.g. in the below, if you pass in "age" as the second argument it order correctly, however if you pass in "job" as the second argument, nothing happens. I was hoping it would order alphabetically by job (Engineer, Marketing, Sales). Any ideas how to fix this / why it is happening?

const arrayOfObjects = [
  { firstName: 'Joe', job: 'Engineer', age: 22 },
  { firstName: 'Sam', job: 'Sales', age: 30 },
  { firstName: 'Claire', job: 'Engineer', age: 40 },
  { firstName: 'John', job: 'Marketing', age: 29 },
  { firstName: 'Susan', job: 'Engineer', age: 21 },
];

const orderByValue = (array, orderByItem, order) => array.sort((a, b) => {
  if (order === 'descending') {
    return b[orderByItem] - a[orderByItem];
  } else {
    return a[orderByItem] - b[orderByItem];
  }
});

// console.log('order by age:', orderByValue(arrayOfObjects, 'age'));
console.log('order by job:', orderByValue(arrayOfObjects, 'job'));

Upvotes: 0

Views: 51

Answers (2)

Shiny
Shiny

Reputation: 5055

You can just use LocaleCompare, and use the Numeric option to handle the numbers as well

LocaleCompare is a String method, so to handle Number types correctly you convert them using .toString(), a[orderByItem] + '', or String(a[orderByItem])

const arrayOfObjects = [
  { firstName: 'Joe', job: 'Engineer', age: 22 },
  { firstName: 'Sam', job: 'Sales', age: 30 },
  { firstName: 'Claire', job: 'Engineer', age: 40 },
  { firstName: 'John', job: 'Marketing', age: 29 },
  { firstName: 'Susan', job: 'Engineer', age: 21 },
];

const orderByProperty = (array, orderByItem, order) => array.sort((a, b) => {
  if (order === 'descending') {
    return b[orderByItem].toString().localeCompare(a[orderByItem].toString(), 'en', {numeric: true});
  } else {
    return a[orderByItem].toString().localeCompare(b[orderByItem].toString(), 'en', {numeric: true});
  }
});

console.log('order by age:', orderByProperty(arrayOfObjects, 'age'));
console.log('order by job:', orderByProperty(arrayOfObjects, 'job'));
.as-console-wrapper {max-height: 100% !important; top: 0;}

Upvotes: 4

Nina Scholz
Nina Scholz

Reputation: 386620

You could return a function as callback and omit for each comparison a check for order.

This approach uses simply greater/smaller operators, which works for strings as well.

const arrayOfObjects = [
  { firstName: 'Joe', job: 'Engineer', age: 22 },
  { firstName: 'Sam', job: 'Sales', age: 30 },
  { firstName: 'Claire', job: 'Engineer', age: 40 },
  { firstName: 'John', job: 'Marketing', age: 29 },
  { firstName: 'Susan', job: 'Engineer', age: 21 },
];

const orderByProperty = (array, orderByItem, order) => array.sort(order === 'descending'
    ? (a, b) => b[orderByItem] > a[orderByItem] || -(b[orderByItem] < a[orderByItem])
    : (a, b) => a[orderByItem] > b[orderByItem] || -(a[orderByItem] < b[orderByItem])
);

console.log('order by age:', orderByProperty(arrayOfObjects, 'age'));
console.log('order by job:', orderByProperty(arrayOfObjects, 'job'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Related Questions