194

我有一个带有一些标识符的列表,如下所示:

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 进行排序”?

4

4 回答 4

411
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();
于 2013-03-07T15:42:22.663 回答
33

由于您没有指定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

于 2013-03-07T15:53:30.433 回答
17

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

    docIds.Join(
      docs,
      i => i,
      d => d.Id,
      (i, d) => d);
于 2019-04-12T11:11:27.937 回答
-4

一种简单的方法是使用排序顺序进行压缩:

List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
               .OrderBy(x => x.Item2).Select(x => x.Item1).ToList();
于 2013-03-07T15:44:00.897 回答