从另一个列表 ID 对列表进行排序

Posted

技术标签:

【中文标题】从另一个列表 ID 对列表进行排序【英文标题】:Sort a list from another list IDs 【发布时间】:2013-02-22 21:24:40 【问题描述】:

我有一个这样的标识符列表:

List<long> docIds = new List<long>()  6, 1, 4, 7, 2 ;

Morover,我有另一个&lt;T&gt; 项目列表,它们由上述 id 表示。

List<T> docs = GetDocsFromDb(...)

我需要在两个集合中保持相同的顺序,以便List&lt;T&gt; 中的项目必须与第一个中的项目位于相同的位置(由于搜索引擎评分的原因)。而这个过程不能在GetDocsFromDb()函数中完成。

如有必要,可以将第二个列表更改为其他结构(例如Dictionary&lt;long, T&gt;),但我不想更改它。

是否有任何简单有效的方法可以使用 LINQ 执行此“取决于某些 ID 的排序”?

【问题讨论】:

您确定每个docIddocs 中只出现一次,什么属性将保存Id 或需要选择器Func&lt;T, long&gt; 第一个列表是否代表“主列表”?换句话说,第二个列表是代表第一个列表的一部分(或全部)的子集吗? 【参考方案1】:
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();

【讨论】:

@BorjaLópez,一个简短的说明。您在问题中提到了效率。 IndexOf 对于您的示例来说是完全可以接受的,而且非常简单。如果您有很多数据,我的答案可能更适合。 ***.com/questions/3663014/… @DenysDenysenko 太棒了。非常感谢;正是我想要的。 如果文档中的元素在排序列表中没有 id,则不起作用 相当低效 - IndexOf 正在为源集合中的每个元素调用,而 OrderBy 必须对元素进行排序。 @Jodrell 的解决方案要快得多。 @DanHunex 如果您需要不存在的 id 出现在列表的末尾,您可以这样做: .OrderBy(d=> var index = docIds.IndexOf(d.Id); if ( index == -1) index = docIds.Count; 返回索引; )【参考方案2】:

一种简单的方法是按顺序压缩:

List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
               .OrderBy(x => x.Item2).Select(x => x.Item1).ToList();

【讨论】:

因为Zip将每个索引(成一个元组)与相应列表中相同位置的文档组合在一起。然后 OrderBy 按索引部分对元组进行排序,然后 select 从现在排序的列表中挖掘我们的文档。 但是,GetDocsFromDb 的结果是无序的,因此您将创建 Item1Item2 无关的元组。 我认为这会产生不正确的结果,因为订购执行 uppon id 而不是索引。【参考方案3】:

由于您没有指定T

IEnumerable<T> OrderBySequence<T, TId>(
       this IEnumerable<T> source,
       IEnumerable<TId> order,
       Func<T, TId> idSelector)

    var lookup = source.ToDictionary(idSelector, t => t);
    foreach (var id in order)
    
        yield return lookup[id];
    

是你想要的通用扩展。

也许你可以使用这样的扩展,

var orderDocs = docs.OrderBySequence(docIds, doc => doc.Id);

一个更安全的版本可能是

IEnumerable<T> OrderBySequence<T, TId>(
       this IEnumerable<T> source,
       IEnumerable<TId> order,
       Func<T, TId> idSelector)

    var lookup = source.ToLookup(idSelector, t => t);
    foreach (var id in order)
    
        foreach (var t in lookup[id])
        
           yield return t;
        
    

如果sourceorder 不完全压缩,这将起作用。

【讨论】:

我使用了这个解决方案,它奏效了。就是这样,我必须使方法静态和类静态。【参考方案4】:

Jodrell 的答案是最好的,但实际上他重新实现了System.Linq.Enumerable.Join。 Join 也使用 Lookup 并保持源的排序。

    docIds.Join(
      docs,
      i => i,
      d => d.Id,
      (i, d) => d);

【讨论】:

这就是我们正在寻找的答案 这只是证明Join太难理解了,因为大家一致认为重写更容易。

以上是关于从另一个列表 ID 对列表进行排序的主要内容,如果未能解决你的问题,请参考以下文章

从另一个排序列表创建过滤列表

如何按另一个列表对列表进行排序

根据列表的顺序对过滤后的查询集进行排序

如何根据另一个数组的顺序对对象数组进行排序?

在 ReactJS 上使用 Lodash 对映射列表进行排序

如何使用 LINQ 使用嵌套属性对列表进行排序