JavaScript排序函数。按照第一列排序,然后再按照第二列排序。

109

我有一个对象数组需要排序。每个对象有两个参数:Strength和Name。

objects = []
object[0] = {strength: 3, name: "Leo"}
object[1] = {strength: 3, name: "Mike"}

我希望先按强度排序,然后再按名称字母顺序排序。我正在使用以下代码按第一个参数进行排序。如何按第二个参数排序?

function sortF(ob1,ob2) {
  if (ob1.strength > ob2.strength) {return 1}
  else if (ob1.strength < ob2.strength){return -1}
  return 0;
};

感谢您的帮助。

(我正在使用 Array.sort() 函数,并将 sortF 作为排序比较函数传递给它。)

10个回答

138

将您的排序函数扩展为以下形式:

function sortF(ob1,ob2) {
    if (ob1.strength > ob2.strength) {
        return 1;
    } else if (ob1.strength < ob2.strength) { 
        return -1;
    }

    // Else go to the 2nd item
    if (ob1.name < ob2.name) { 
        return -1;
    } else if (ob1.name > ob2.name) {
        return 1
    } else { // nothing to split them
        return 0;
    }
}

在字符串上进行 <> 的比较是按字母顺序进行的。


95

在按多个键排序时,这个小函数通常很方便:

cmp = function(a, b) {
    if (a > b) return +1;
    if (a < b) return -1;
    return 0;
}

更简洁地说,

cmp = (a, b) => (a > b) - (a < b)

这有效是因为在JavaScript中:

true - true // gives 0
false - false // gives 0
true - false // gives 1
false - true // gives -1

按照以下方式应用:

array.sort(function(a, b) { 
    return cmp(a.strength,b.strength) || cmp(a.name,b.name)
})

Javascript确实缺少Ruby的宇宙飞船运算符,这使得这类比较极其优雅。


25
Perl的“太空船”运算符。 - Richard Simões
1
谢谢。我已经搜索了一段时间这个知识核心了……我只是不知道该如何用语言表达。尤其是太空船运算符。 - Brandon LeBlanc
2
@AndySmith:当然,cmp(a, b) || cmp(c, d) || cmp(e, f)等等。 - georg
2
@AndyB:从那时起它已经被修复了 ;) - georg
当不比较字符串时,可以更简洁地执行 array.sort((a, b) => { return a.x - b.x || a.y - b.y }),甚至可以添加 || a.z - b.z 等等来按第三个条件排序,以此类推进行更多的排序。请注意,在字符串方面,不能使用 - 符号进行比较,而必须使用 <>a.localeCompare(b) 进行比较。 - Magne
显示剩余3条评论

49
你可以使用逻辑或运算符来链接排序顺序。
objects.sort(function (a, b) {
    return a.strength - b.strength || a.name.localeCompare(b.name);
});

太棒了!但是如果强度未定义,我们希望所有具有定义强度的对象先进行处理,这种情况怎么办?是否有与此一样优雅的解决方案? - Nan Li
return ((a.strength || Number.MAX_VALUE) - (b.strength || Number.MAX_VALUE)) || a.name.localeCompare(b.name); - Nan Li
1
是的,您可以为不存在的部分添加默认值,例如 return ((a.strength || Infinity) - (b.strength || Infinity)) || a.name.localeCompare(b.name);。但这取决于 strength 可能的值。 - Nina Scholz
1
像魔法一样运作:return a.z - b.z || a.x - b.x || a.y - b.y - bresleveloper

15

当我正在寻找对这个问题的答案时,我在StackOverflow上找到的答案并不是我所希望的。因此,我创建了一个简单、可重复使用的函数来实现这个目标。它允许您使用标准的 Array.sort,但采用 firstBy().thenBy().thenBy() 的风格。

https://github.com/Teun/thenBy.js

PS. 这是我第二次发布这篇文章。第一次被一位管理员删除,原因是“请不要为自己的工作做推广性质的帖子”。我不确定规则是什么,但我只是想回答这个问题。很抱歉这是我的作品。如有需要,请再次删除,并指出相关规则。


你应该在Readme.md中放一个名字和姓氏的例子,因为每个人都需要它^^,我会尝试使用这个库,看起来不错。 - Christophe Roussy
1
是的,说得好,我想你说得对。虽然展示任意组合的字段很诱人,但自述文件确实应该从最常见的场景开始。感谢您的提示。 - Teun D
3
太好了。我不明白为什么版主会删除这么有用的东西。如果需要购买,那可能会有所不同,虽然我想这取决于我们自己的决定。我们都是成年人,可以做出明智的决定。 - Charles Robertson
我不能代表管理员发言,但除了自我推广之外,我猜他们删除它的原因是:仅链接答案不包含答案。将40行库源代码复制到您的答案中可能也不是理想的选择,但复制几行代码或描述库中实现的算法将使您的答案内容独立存在。 - Carl Walsh

5

更好看的Steve的答案。

objects.sort(function(a,b)
{
  if(a.strength > b.strength) {return  1;}
  if(a.strength < b.strength) {return -1;}
  if(a.name     > b.name    ) {return  1;}
  if(a.name     < b.name    ) {return -1;}
  return 0;
}

1
function sortF(ob1,ob2) {
  if (ob1.strength > ob2.strength) {return 1}
  else if (ob1.strength < ob2.strength) {return -1}
  else if (ob1.name > ob2.name) {return 1}
  return -1;
};

编辑:按强度排序,如果强度相等,则按名称排序。 两个对象中强度和名称都相等的情况不需要单独考虑,因为最终返回-1表示小于或等于关系。排序的结果将是正确的。它可能会使其运行更快或更慢,我不知道。如果您想明确说明,请替换

return -1;

使用

else if (ob1.name < ob2.name) {return -1}
return 0;

1
你漏掉了等于的情况。 - T.J. Crowder

1
在下方找到“sortFn”函数。该函数按无限数量的参数进行排序(例如在c#中:SortBy(...)。ThenBy(...)。ThenByDesc(...))。

function sortFn() {
    var sortByProps = Array.prototype.slice.call(arguments),
        cmpFn = function(left, right, sortOrder) {
            var sortMultiplier = sortOrder === "asc" ? 1 : -1;

            if (left > right) {
                return +1 * sortMultiplier;
            }
            if (left < right) {
                return -1 * sortMultiplier;
            }
            return 0;
        };


    return function(sortLeft, sortRight) {
        // get value from object by complex key
        var getValueByStr = function(obj, path) {
            var i, len;

            //prepare keys
            path = path.replace('[', '.');
            path = path.replace(']', '');
            path = path.split('.');

            len = path.length;

            for (i = 0; i < len; i++) {
                if (!obj || typeof obj !== 'object') {
                    return obj;
                }
                obj = obj[path[i]];
            }

            return obj;
        };

        return sortByProps.map(function(property) {
            return cmpFn(getValueByStr(sortLeft, property.prop), getValueByStr(sortRight, property.prop), property.sortOrder);
        }).reduceRight(function(left, right) {
            return right || left;
        });
    };
}

var arr = [{
    name: 'marry',
    LocalizedData: {
        'en-US': {
            Value: 10000
        }
    }
}, {
    name: 'larry',
    LocalizedData: {
        'en-US': {
            Value: 2
        }
    }
}, {
    name: 'marry',
    LocalizedData: {
        'en-US': {
            Value: 100
        }
    }
}, {
    name: 'larry',
    LocalizedData: {
        'en-US': {
            Value: 1
        }
    }
}];
document.getElementsByTagName('pre')[0].innerText = JSON.stringify(arr)

arr.sort(sortFn({
    prop: "name",
    sortOrder: "asc"
}, {
    prop: "LocalizedData[en-US].Value",
    sortOrder: "desc"
}));

document.getElementsByTagName('pre')[1].innerText = JSON.stringify(arr)
pre {
    font-family: "Courier New" Courier monospace;
    white-space: pre-wrap;
}
Before:
<pre></pre>
Result:
<pre></pre>


你能否制作一个箭头函数版本的这个吗? - Markov

0

使用ES6,你可以做到

array.sort(function(a, b) { 
 return SortFn(a.strength,b.strength) || SortFn(a.name,b.name)
})

 private sortFn(a, b): number {
    return a === b ? 0 : a < b ? -1 : 1;
}

0
这是我使用的函数。它将执行任意数量。
function Sorter(){
  
  var self = this;
  this.sortDefs = [];
  
  for (let i = 0; i < arguments.length; i++) {
  // Runs 5 times, with values of step 0 through 4.
    this.sortDefs.push(arguments[i]);
  }
  
  this.sort = function(a, b){
  
    for (let i = 0; i < self.sortDefs.length; i++) {

        if (a[self.sortDefs[i]] < b[self.sortDefs[i]]) { 
          return -1;
        } else if (a[self.sortDefs[i]] > b[self.sortDefs[i]]) {
          return 1
        }
    }
    
    return 0;
  }
}

data.sort(new Sorter('category','name').sort);

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接