Javascript - Sort Array of objects by 2 Properties

this is a simple way to sort array of objects by multiple values

arr.sort((a,b) => a.firstVal.localeCompare(b.firstVal) || a.secondVal.localeCompare(b.secondVal))

so in your case it's gonna be

array.sort((a,b) => a.resHP.localeCompare(b.resHP) || b.resFlow.localeCompare(a.resFlow)

The requirement is to sort ascending by resHP property and then for any ties, sort descending by resFlow property.

Now, Javascript's Array.prototype.sort function allows us to sort an array. It accepts one parameter, which is a "compareFunction". The compareFunction accepts two parameters, which are elements of the array to be compared, and it must return their relative position in the expected resultant array.

function compareFunction (itemA, itemB) {...}

compareFunction(a, b) returns a numeric value greater than zero if b comes before a in the result.

compareFunction(a, b) returns a numeric value less than zero if b comes after a in the result.

compareFunction(a, b) returns zero if a and b are considered equivalent in terms of their relative order while sorting the array. For example, while sorting [5, 3, 5] both 5's are considered equivalent, hence the array should return 0 when compareFunction(5,5) is called.

For sorting strings, providing a compareFunction is not necessary. As per the specification, when compareFunction is not provided, each value in the array is converted to string, and then sorted. Here's an example:

console.log('["z", "ct", "a", "aa", "ab", "bc"].sort(): ', ["z", "ct", "a", "aa", "ab", "bc"].sort());

Common Mistakes:

Comparing numbers without compareFunction

Although compareFunction is optional by specification, if it is not provided while sorting numbers or other objects, it may result into incorrect results. Consider the example below:

console.log("[1, 2, 5, 3, 0, 20].sort(): ", [1, 2, 5, 3, 0, 20].sort());

Here 20 appears before 3 because each number is converted to string before comparison, and since "20" comes before "3" in dictionary (if there was such a dictionary) it appears above "3" in the result.

Returning boolean value from compareFunction instead of numeric value

This is my mistake in the original answer.

When you return a boolean value instead of a numeric one out of the compareFunction, the boolean value is cast into a number. Meaning true will become 1 and false will become 0, but you are not returning -1.

var array = [
  {
    compareThis: 1,
    originalIndex: 1
  },
  {
    compareThis: 2,
    originalIndex: 2
  },
  {
    compareThis: -1,
    originalIndex: 3
  },
  {
    compareThis: -2,
    originalIndex: 4
  }
];

console.log(array.sort((a, b) => a.compareThis > b.compareThis));

Here the result is the unsorted, same array because when 2 and -1 are compared, the comparison 2 > -1 returns false, which is converted to 0. But when compareFunction returns 0 it indicates that the order of the two elements shall remain unchanged. Hence the array doesn't get sorted.

The correct way to sort this array would be:

var array = [
  {
    compareThis: 1,
    originalIndex: 1
  },
  {
    compareThis: 2,
    originalIndex: 2
  },
  {
    compareThis: -1,
    originalIndex: 3
  },
  {
    compareThis: -2,
    originalIndex: 4
  }
];

console.log(array.sort((a, b) => a.compareThis - b.compareThis));

So with that, the correct way to solve the problem presented in the question would be:

var array = [{ resVal: "25FA15", resFlow: 49, resName: "Rendimiento Tri-Seal Completo", resPhoto: "Tri-Sealseries.png", resHP: 1.5 },
 { resVal: "25FA2", resFlow: 52, resName: "Rendimiento Tri-Seal Completo", resPhoto: "Tri-Sealseries.png", resHP: 2 },
{ resVal: "45FA2", resFlow: 53, resName: "Rendimiento Hi-Cap Completo", resPhoto: "HighCapseries.png", resHP: 2 },
{ resVal: "35FA2", resFlow: 59, resName: "Rendimiento Hi-Cap Completo", resPhoto: "HighCapseries.png", resHP: 2 }];


// The requirement is to sort ascending by resHP property and then for any ties, sort descending by resFlow property.
/* array.sort(function(a, b) {
  if (a.resHP > b.resHP) {
    return 1;
  } else if (a.resHP === b.resHP) {
    if (a.resFlow > b.resFlow) {
      return -1;
    } else if (a.resFlow === b.resFlow) {
      return 0;
    } else {
      return 1;
    }
  } else {
    return -1;
  }
}); */

// Simplified to the following as we are working with numbers.
array.sort(function sortByResHP(a, b) {
  return (a.resHP - b.resHP) !== 0 ? (a.resHP - b.resHP) : (b.resFlow - a.resFlow);
});

console.log("Result: ", array);

Of course this can be further shortened as shown in the other answer. I am leaving the longer version of the compareFunction to make it easier to read.


Original (incorrect) answer

You could use Array#sort for this:

array = [{ resVal: "25FA15", resFlow: 49, resName: "Rendimiento Tri-Seal Completo", resPhoto: "Tri-Sealseries.png", resHP: 1.5 },
 { resVal: "25FA2", resFlow: 52, resName: "Rendimiento Tri-Seal Completo", resPhoto: "Tri-Sealseries.png", resHP: 2 },
{ resVal: "45FA2", resFlow: 53, resName: "Rendimiento Hi-Cap Completo", resPhoto: "HighCapseries.png", resHP: 2 },
{ resVal: "35FA2", resFlow: 59, resName: "Rendimiento Hi-Cap Completo", resPhoto: "HighCapseries.png", resHP: 2 }]


array.sort(function(d1,d2) {
  if(d1.resHP == d2.resHP) {
    return d1.resFlow < d2.resFlow;
  } else {
    return d1.resHP > d2.resHP;
  }
});

console.log(array);

You could use a chained approach for a specified order of keys and their sort order.

The array is sorted by the properties

  • resHP, ascending and
  • resFlow, descending.

It works with calculating the delta and this reflects the relation of the two objects. If the value is zero, then the two values are equal and the next delta is calculated and returned.

var array = [{ resVal: "25FA15", resFlow: 49, resName: "Rendimiento Tri-Seal Completo", resPhoto: "Tri-Sealseries.png", resHP: 1.5 }, { resVal: "25FA2", resFlow: 52, resName: "Rendimiento Tri-Seal Completo", resPhoto: "Tri-Sealseries.png", resHP: 2 }, { resVal: "45FA2", resFlow: 53, resName: "Rendimiento Hi-Cap Completo", resPhoto: "HighCapseries.png", resHP: 2 }, { resVal: "35FA2", resFlow: 59, resName: "Rendimiento Hi-Cap Completo", resPhoto: "HighCapseries.png", resHP: 2 }];

array.sort(function (a, b) {
    return a.resHP - b.resHP || b.resFlow - a.resFlow;
});

console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }