【问题标题】:Sort an array by the contents of another in JavaScript在 JavaScript 中按另一个数组的内容对数组进行排序
【发布时间】:2015-06-24 14:27:17
【问题描述】:

假设我有一个 Person 对象数组:

var people = [{name: "Joe Schmo", age: 36}, {name: "JANE DOE", age: 40}];

我有一个函数可以不区分大小写地对字符串数组进行排序:

function caseInsensitiveSort(arr) { ... }

是否有任何直接的方法可以将我现有的排序功能与Array.prototype.map 结合使用name 键对people 数组进行排序?

即它会产生

var people = [{name: "JANE DOE", age: 40}, {name: "Joe Schmo", age: 36}];

在这种特殊情况下,手工操作并不难,

people.sort(function (a, b) {
    return a.name.localeCompare(b.name);
});

但我想不出一种方法可以让我使用预先存在的排序功能。在排序功能更加自定义的情况下,这将很有用。

编辑:我认为这里的核心问题是,要做到这一点,您需要能够在对代理数组进行排序时弄清楚原始索引映射到什么。在一般情况下,使用 JS 的原生 sort 函数获取这些新索引似乎是不可能的。但我很高兴被证明是错误的。

编辑:我尝试这样做的方式效率太低而无用。请参阅下面的答案,了解使用比较函数的解决方案。

【问题讨论】:

  • 如果caseInsensitiveSort 接受一个数组,您需要将一个名称数组传递给该函数,对名称进行排序,然后根据具有名称的数组对包含对象的数组进行排序。听起来像是一种非常复杂的方法来做一些简单的事情。
  • 它会处理几个元素吗?因为Array.prototype.map 方法创建了一个新数组,所以对于数百万条记录,最好的选择是就地排序。
  • @adeneo @Jordan 你们都是对的。正如 xdazz 在下面指出的那样,有效地执行此操作的正确方法是将我的比较逻辑抽象为一个单独的函数,并将其提供给Array.prototype.sort,而不是试图将我的排序函数插入其中。

标签: javascript arrays sorting dictionary


【解决方案1】:

您可以使用现有函数获取排序后的名称数组,然后通过比较排序后的名称数组中的索引对 people 数组进行排序。

var names = caseInsensitiveSort(people.map(function(person) { 
    return person.name;
}));

people.sort(function (a, b) {
    return names.indexOf(a.name) - names.indexOf(b.name);
});

但这效率不高,你应该尝试将caseInsensitiveSort函数中的比较逻辑抽象为caseInsensitiveCompare函数。

那么你的例子会变成:

people.sort(function (a, b) {
    return caseInsensitiveCompare(a.name, b.name);
});

【讨论】:

  • 每次排序迭代使用indexOf 两次会使大数组的速度变慢,但这可能是唯一可能的通用方式。 +1。
  • @ChrisMiddleton 你不能从caseInsensitiveSort 函数中抽象出比较逻辑吗?
  • 是的,你是对的。这是我认为的真正答案。要以通用且有效的方式执行此操作,您需要在比较函数的级别工作,而不是排序函数。谢谢。
  • 您应该添加一条关于使用比较功能执行此操作的评论,而不是添加到您对任何未来读者的回答中——我试图这样做的方式根本上是错误的。
  • 这充其量是一个 O(n^2 log^2 n) 操作。请不要这样做。
【解决方案2】:

根据您的caseInsensitiveSort() 函数的工作方式,您可以使用.toString() 方法来执行此操作:

var toSort = people.map(function (person) {
    return {
        person: person,
        toString: function () {
            return person.name;
        }
    };
});

caseInsensitiveSort(toSort);

people = toSort.map(function (item) { return item.person; });

如果这不是一个选项,一个更复杂但仍然有效的方法是对名称进行排序,将它们映射到它们的索引,然后根据它进行排序:

var names = people.map(function (person) { return person.name; });

caseInsensitiveSort(names);

var nameMap = {};
names.forEach(function (name, i) { 
    nameMap[name] = i;
});

people.sort(function (a, b) {
    return nameMap[a.name] - nameMap[b.name];
});

【讨论】:

  • 感谢您添加此内容-您的第二个解决方案与我询问时的想法相似。当然,它仅在您排序的值是可键值时才有效,但这可能是一个不可避免的限制,除非您将哈希值用于不可键值。
  • @ChrisMiddleton 是的,确实如此。它适用于名称,但不适用于所有类型的值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-16
  • 2011-02-10
  • 2019-06-11
相关资源
最近更新 更多