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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7157590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 01:05:29  来源:igfitidea点击:

Javascript sort custom comparator function - sorting a sorted array

javascriptarrayssorting

提问by fenderplayer

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. 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. The final array would look like:

我先按'item1'哪个简单排序。现在我想再次排序arr(按 排序'item1'),但这次按'item2'但仅针对'item1'相同的元素。最终的数组看起来像:

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'.

回答by Petar Ivanov

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。

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

回答by fhewitt

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:

我最近遇到了同样的问题。提供了与 langpavel 类似的解决方案,但我更喜欢将其一分为二。首先是一个链式比较器助手,它将允许多个排序规则,每个排序规则在相等的情况下作为决胜局应用:

    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 (scoreA < scoreB) {
                order = -1;
            } else if (scoreA > scoreB) {
                order = 1;
            }
            return reversed ? -order : order;
        };
    }

reversedhere 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 typelines out.

因为最初的问题是在纯 JavaScript 中,精度:如果你不习惯 TypeScript,你可以通过删除键入<T>, : T,: ((item: T) => Comparable)处处和两type行出来的方式得到普通的 JavaScript 。

回答by langpavel

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

回答by Jonas Ostergaard

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 -1and 1for the reversed order.

或者作为第一和第二优先级排序的简单单行,您可以根据需要进一步扩展它,只需将 0 替换为另一个比较链。切换<>-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;
});