Javascript sort custom comparator function - sorting a sorted array

前端 未结 5 652
你的背包
你的背包 2021-02-03 22:23

I have an array of objects of the following form:

arr[0] = { \'item1\' : 1234, \'item2\' : \'a string\' };

I sort it first by \'item1\'

相关标签:
5条回答
  • 2021-02-03 22:56

    Or as simple oneliner for first and second priority sort, you can expand it further as you wish, just replace the 0 with another comparison chain. Switch < and > or -1 and 1 for the reversed order.

    someArray.sort(function(a,b) {
      return a.item1 > b.item1 ? 1 : a.item1 < b.item1 ? -1 : a.item2 > b.item2 ? 1 : a.item2 < b.item2 ? -1 : 0;
    });
    
    0 讨论(0)
  • 2021-02-03 23:02

    You can have four different comparison functions - one sorting by item1, one by item2, one by item1 then item2 and one by item2 then item1.

    E.g.:

    arr.sort(function(a,b){
      if(a.item1 == b.item1){
        return a.item2 > b.item2 ? 1 : a.item2 < b.item2 ? -1 : 0;
      }
    
      return a.item1 > b.item1 ? 1 : -1;
    });
    
    0 讨论(0)
  • 2021-02-03 23:09

    I hit the same question lately. Came with a similar solution than langpavel, but I prefer to split the thing in two. First a chained comparator helper that will allows multiple sort rule, each applied in order as a tie-breaker in case of equality:

        type Comparator<T> = (a: T, b: T) => number; // -1 | 0 | 1
    
        /**
         * Allow to chain multiple comparators, each one called to break equality from the previous one.
         */
        function chainedComparator<T>(...comparators: Comparator<T>[]): Comparator<T> {
            return (a: T, b: T) => {
                let order = 0;
                let i = 0;
        
                while (!order && comparators[i]) {
                    order = comparators[i++](a, b);
                }
        
                return order;
            };
        }
    

    I like it, because it takes and return sort comparator. So if you have a collection of other comparators, they are easy to use.

    Then you can simplify a bit your life with an additional helper. This one return a sort comparator based on the result of the passed lambda over each items.

        type Comparable = string | number;
    
        /**
         * Returns a comparator which use an evaluationFunc on each item for comparison
         */
        function lambdaComparator<T>(evaluationFunc: ((item: T) => Comparable), reversed = false): Comparator<T> {
            return (a: T, b: T) => {
                const valA = evaluationFunc(a);
                const valB = evaluationFunc(b);
                let order = 0;
        
                if (valA < valB) {
                    order = -1;
                } else if (valA > valB) {
                    order = 1;
                }
                return reversed ? -order : order;
            };
        }
    

    reversed here is not required to answer the question, but will allow to reverse the order easily.

    To answer the question specifically, using our two comparators:

        arr.sort(chainedComparator(
            lambdaComparator(a => a.item1),
            lambdaComparator(a => a.item2.toLowerCase()) // "banana" before "Melon"
        ));
    

    Because the original question was in pure JavaScript, precision: If you're not accustomed to TypeScript, you can get normal JavaScript just by removing the typing <T>, : T, : ((item: T) => Comparable) everywhere and the two type lines out.

    0 讨论(0)
  • 2021-02-03 23:11

    I'm using this helper in TypeScript:

    // Source
    type ComparatorSelector<T> = (value: T, other: T) => number | string | null;
    
    export function createComparator<T>(...selectors: ComparatorSelector<T>[]) {
      return (a: T, b: T) => {
        for (const selector of selectors) {
          const valA = selector(a, b);
          if (valA === null) continue;
          const valB = selector(b, a);
          if (valB === null || valA == valB) continue;
          if (valA > valB) return 1;
          if (valA < valB) return -1;
        }
        return 0;
      };
    }
    

    // Usage:
    const candidates: any[] = [];
    // ...
    candidates.sort(createComparator(
      (x) => x.ambiguous,
      (_, y) => y.refCount, // DESC
      (x) => x.name.length,
      (x) => x.name,
    ));
    
    0 讨论(0)
  • 2021-02-03 23:12

    You can just import type-comparator with npm and then use queue to do the chaining:

    const comparator = queue([
        map(x => x.item1, asc),
        map(x => x.item2, asc)
    ]);
    arr.sort(comparator);
    
    0 讨论(0)
提交回复
热议问题