Search code examples
javascriptsortingecmascript-6lodash

Sort function to achieve custom difference along with multiple columns


Trying to achieve custom sort as below:

// 1. sort by 'value' column, here 'a' (first object) and b (second object) has difference equals to 4.5 or less then 4.5 then,
// consider equal values (even though it's 12.12 and 13.22) 
// then 2. sort by 'altValue' column (as equal value found on 'value' column) and then,
// 3. sort by 'name' column (as equal value found on 'altValue').
const validSortEqualityDiffence = 4.5;
const inputArray = [
  { id: 1, name: 'ABC INC.', value: 12.12, altValue: 22.22 },
  { id: 2, name: 'DBC INC.', value: 13.22, altValue: 23.32 },
  { id: 3, name: 'DBC INC.', value: 15.22, altValue: 25.42 },
  { id: 4, name: 'CBC INC.', value: 12.22, altValue: 22.32 },
  { id: 5, name: 'CBC INC.', value: 16.22, altValue: 26.32 },
  { id: 6, name: 'QBC INC.', value: 13.52, altValue: 23.72 },
  { id: 7, name: 'QBC INC.', value: 11.02, altValue: 21.92 },
];

/* below code error out as unable to find 'b' value here */

_.orderBy(
  inputArray,
  (a, b) => {
    console.log('a,b', a, b); // b is undfined

    if (Math.abs(a.value - b.value) <= validSortEqualityDiffence) {
      return 0;
    }
    if (a.value < b.value) {
      return -1;
    }
    return 1;
  },
  (a, b) => {
    console.log('a,b', a, b); // b is undfined

    if (Math.abs(a.altValue - b.altValue) <= validSortEqualityDiffence) {
      return 0;
    }
    if (a.altValue < b.altValue) {
      return -1;
    }
    return 1;
  },
  'name'
  );

Here, unable to get 'b' object. Is there any alternate way using Array.sort or other lodash method that can help to achieve, sort with custom difference and consider multiple order by columns.

Thank You


Solution

  • Lodash _.orderBy() and Underscore.js _.sortBy() don't take comparison functions, they take an iteratee argument, which extracts the key from each element. b is undefined because only one element is passed to the function at a time.

    Use the built-in sort() method, and put the secondary ordering in place of the first return 0; statement, and the tertiary ordering in place of the second return 0;.

    const validSortEqualityDiffence = 4.5;
    
    inputArray.sort(
      (a, b) => {
        if (Math.abs(a.value - b.value) <= validSortEqualityDiffence) {
          if (Math.abs(a.altValue - b.altValue) <= validSortEqualityDiffence) {
            return a.name.localeCompare(b.name);
          }
          if (a.altValue < b.altValue) {
            return -1;
          }
          return 1;
        }
        if (a.value < b.value) {
          return -1;
        }
        return 1;
      });
    
    console.log(inputArray);
    <script>
    const inputArray = [
      { id: 1, name: 'ABC INC.', value: 12.12, altValue: 22.22 },
      { id: 2, name: 'DBC INC.', value: 13.22, altValue: 23.32 },
      { id: 3, name: 'DBC INC.', value: 15.22, altValue: 25.42 },
      { id: 4, name: 'CBC INC.', value: 12.22, altValue: 22.32 },
      { id: 5, name: 'CBC INC.', value: 16.22, altValue: 26.32 },
      { id: 6, name: 'QBC INC.', value: 13.52, altValue: 23.72 },
      { id: 7, name: 'QBC INC.', value: 11.02, altValue: 21.92 },
    ];
    </script>