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


150

我有一个带有一些标识符的列表,像这样:

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

Morover,我还有另一个<T>项目列表,这些列表由上述ID表示。

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

我需要在两个集合中保持相同的顺序,以便其中的项List<T>必须与第一个集合处于相同的位置(由于搜索引擎评分的原因)。并且此过程无法在GetDocsFromDb()函数中完成。

如有必要,可以将第二个列表更改为其他结构(Dictionary<long, T>例如),但是我不希望更改它。

是否有任何简单有效的方法来使用LINQ进行“根据某些ID进行排序”?


您确定每次都会docId发生一次docs,是否会保留的属性,Id或者Func<T, long>需要选择器?
Jodrell 2013年

第一个列表代表“主列表”吗?换句话说,第二个列表是否是代表第一个列表的一部分(或全部)的子集?
code4life 2013年

Answers:


332
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();

@Kaf这就是为什么我也投票赞成,确实依赖于知道文档ID属性被称为Id。在问题中未指定。
Jodrell 2013年

3
@BorjaLópez,快速笔记。您在问题中提到效率。IndexOf非常适合您的示例,并且非常简单。如果您有大量数据,我的答案可能更适合。stackoverflow.com/questions/3663014/...
Jodrell

2
@DenysDenysenko太棒了。非常感谢; 正是我想要的。
Silkfire '16

3
如果您的文档中的元素在订购列表中没有ID,则无法使用
Dan Hunex

4
效率很低-为源集合中的每个元素调用IndexOf,并且OrderBy必须对元素进行排序。@Jodrell的解决方案要快得多。
18

25

由于您未指定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;
        }
    }
}

如果source不能使用zip压缩,它将可以正常工作order


我使用了此解决方案,并且有效。就是这样,我不得不使方法静态和类静态。
Vinmm

5

乔德雷尔的答案是最好的,但实际上他重新实行了System.Linq.Enumerable.Join。Join还使用Lookup并保持源顺序。

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

这就是我们正在寻找的答案
Orace

2
这只是证明Join太难理解了,因为每个人都同意重写变得容易了。
PRMan

-3

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

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

为什么要在拉链后订购?
Jodrell 2013年

因为Zip将每个索引(到元组中)与文档在相应列表中的相同位置结合在一起。然后,OrderBy按索引部分对元组进行排序,然后select从现在排序的列表中仅挖掘文档。
Albin Sunnanbo 2013年

但是,GetDocsFromDb的结果是无序的,因此您将在Item1与无关的地方创建元组Item2
Jodrell 2013年

1
我认为这将产生不正确的结果,因为订购执行uppon id而不是索引。
Michael Logutov 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.