使用 Lodash 根据外部数组对集合进行排序

46

我有一个带有如下键的数组:

['asdf12','39342aa','12399','129asg',...] 

并且集合中每个对象都具有这些键,如下所示:

[{guid: '39342aa', name: 'John'},{guid: '129asg', name: 'Mary'}, ... ]

有没有一种快速的方法根据第一个数组中键的顺序对集合进行排序?


你使用了哪些编程语言? - Angu
我正在使用JavaScript。 - silintzir
5个回答

92
var sortedCollection = _.sortBy(collection, function(item){
  return firstArray.indexOf(item.guid)
});

2
@silintzir,你在问题中提到了“快速排序集合”的需求。但是如果你真的关心运行时性能,这并不是一种快速的方法。 - thefourtheye
5
这个方案完美地运作,是我所找到的最优雅的解决方案。 - Backer
1
它是否保留与第一个数组中没有元素匹配的集合元素,还是删除它们? - Genjuro
1
这个没问题,除非一个项目不在订购数组中,它会出现在第一位。 - Robert Moskal
1
@RobertMoskal 这里提供了一个相对简洁的解决方案,虽然代码量稍多: `_.sortBy(collection, (item) => {const index = firstArray.indexOf(item.guid); return index === -1 ? Infinity : index; });` - sloreti
显示剩余2条评论

15

这里只是对已接受答案进行简单补充,以防您希望将未匹配的元素放在sortedCollection的末尾而不是开头:

const last = collection.length;

var sortedCollection = _.sortBy(collection, function(item) {
  return firstArray.indexOf(item.guid) !== -1? firstArray.indexOf(item.guid) : last;
});

3
尽管这不是被接受的答案,但对我来说它是正确的,因为在数组末尾放置未知元素似乎更自然,放在所有已排序的项目之后,而不是放在开头。 - pizzae

11

输入:

var data1 = ['129asg', '39342aa'];
var data2 = [{
    guid: '39342aa',
    name: 'John'
}, {
    guid: '129asg',
    name: 'Mary'
}];
  1. 首先使用_.reduce创建一个索引对象,如下所示:

var indexObject = _.reduce(data2, function(result, currentObject) {
    result[currentObject.guid] = currentObject;
    return result;
}, {});
  • 然后使用map函数将第一个数组的项与来自indexObject的对象进行映射,代码如下:

  • console.log(_.map(data1, function(currentGUID) {
        return indexObject[currentGUID]
    }));
    

    输出

    [ { guid: '129asg', name: 'Mary' },
      { guid: '39342aa', name: 'John' } ]
    

    注意: 如果你想对很多对象进行排序,那么这种方法将非常高效,因为它会减少第二个数组的线性查找,从而使整个逻辑以O(M * N)的时间复杂度运行。


    我非常喜欢这个答案,不过它没有涵盖多个具有相同guid的情况。只是指出一下。 - Ando

    1

    这是高效且简洁的方法:

    (引入lodash的identitysortBy):

    TS

    function sortByArray<T, U>({ source, by, sourceTransformer = identity }: { source: T[]; by: U[]; sourceTransformer?: (item: T) => U }) {
      const indexesByElements = new Map(by.map((item, idx) => [item, idx]));
      const orderedResult = sortBy(source, (p) => indexesByElements.get(sourceTransformer(p)));
      return orderedResult;
    }
    

    或者用JS
    function sortByArray({ source, by, sourceTransformer = _.identity }) {
        const indexesByElements = new Map(by.map((item, idx) => [item, idx]));
        const orderedResult = _.sortBy(source, (p) => indexesByElements.get(sourceTransformer(p)));
        return orderedResult;
    }
    

    1
    我真的很喜欢这个。 - ptoinson

    0

    你可以使用 indexBy(), 和 at() 对你的集合进行排序。优点是代码简洁且性能良好。在这里使用 sortBy() 就可以完成任务,但是你的外部数组已经被排序了:

    var ids = [ 'cbdbac14', 'cf3526e2', '189af064' ];
    
    var collection = [
        { guid: '189af064', name: 'John' },
        { guid: 'cf3526e2', name: 'Julie' },
        { guid: 'cbdbac14', name: 'James' }
    ];
    
    _(collection)
        .indexBy('guid')
        .at(ids)
        .pluck('name')
        .value();
    // → [ 'James', 'Julie', 'John' ]
    

    使用at(),您可以迭代排序的外部集合,从源collection构建新的集合。使用indexBy()将源集合转换为对象。这样做是为了使at()对于每个id都具有基于键的访问。


    3
    这个回答需要更新为 lodash 的新方法和函数,因为它们已经发生了变化。 - Dejan.S

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