简体   繁体   English

Javascript 排序自定义比较器函数 - 排序已排序的数组

[英]Javascript sort custom comparator function - sorting a sorted array

I have an array of objects of the following form:我有以下形式的对象数组:

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

I sort it first by 'item1' which is straightforward.我首先按'item1'对它进行排序,这很简单。 Now i want to sort arr (which is sorted by 'item1' ) again but this time by 'item2' but only for the elements where 'item1' is the same.现在我想再次对arr (按'item1'排序)进行排序,但这次按'item2'但仅针对'item1'相同的元素。 The final array would look like:最终的数组看起来像:

arr = [
  { 'item1' : 1234, 'item2' : 'apple' },
  { 'item1' : 1234, 'item2' : 'banana' },
  { 'item1' : 1234, 'item2' : 'custard' },
  { 'item1' : 2156, 'item2' : 'melon' },
  { 'item1' : 4345, 'item2' : 'asparagus' } 
];

I tried to write a sorting function for the second case like so:我尝试为第二种情况编写一个排序函数,如下所示:

arr.sort(function(a,b){
  if(a.item1 === b.item1){
    return a.item2 > b.item2 ? 1 : a.item2 < b.item2 : -1 : 0;
  }
});

I could combine the two sorts in one function to get the final sorted array but there will be cases where I'll have to sort by just 'item1' or just 'item2' .我可以在一个函数中组合这两种排序以获得最终的排序数组,但在某些情况下,我必须仅按'item1'或仅按'item2'排序。

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.您可以使用四种不同的比较功能 - 一种按 item1 排序,一种按 item2 排序,一种按 item1 然后 item2 和一种按 item2 然后 item1。

Eg:例如:

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

I hit the same question lately.我最近遇到了同样的问题。 Came with a similar solution than langpavel, but I prefer to split the thing in two.提供了与 langpavel 类似的解决方案,但我更喜欢将其一分为二。 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.这个基于每个项目上传递的 lambda 的结果返回一个排序比较器。

    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.这里不需要reversed来回答问题,但可以轻松反转顺序。

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.因为最初的问题是在纯 JavaScript 中,精度:如果你不习惯 TypeScript,你可以通过在任何地方删除<T> , : T , : ((item: T) => Comparable)和两种type行。

I'm using this helper in TypeScript:我在 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,
));

You can just import type-comparator with npm and then use queue to do the chaining:您可以使用 npm 导入类型比较器,然后使用queue进行链接:

const comparator = queue([
    map(x => x.item1, asc),
    map(x => x.item2, asc)
]);
arr.sort(comparator);

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.或者作为第一和第二优先级排序的简单单行,您可以根据需要进一步扩展它,只需将 0 替换为另一个比较链。 Switch < and > or -1 and 1 for the reversed order.切换<>-11以实现相反的顺序。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM