別のリストIDからリストを並べ替える


150

私はこのようないくつかの識別子を持つリストを持っています:

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

Morover、私は<T>上記のIDで表されるアイテムの別のリストを持っています。

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

両方のコレクションで同じ順序を維持する必要があるため、のアイテムList<T>は最初のアイテムと同じ位置になければなりません(検索エンジンのスコアリングの理由により)。そして、このプロセスはGetDocsFromDb()関数内では実行できません。

必要に応じて、2番目のリストを他の構造(Dictionary<long, T>たとえば)に変更することは可能ですが、変更しない方がよいでしょう。

LINQを使用してこの「いくつかのIDに応じた順序付け」を行う簡単で効率的な方法はありますか?


すべてdocIdがで1回だけ発生することが保証されていますか?docsどのプロパティがを保持するIdか、セレクタFunc<T, long>が必要になりますか?
Jodrell 2013年

最初のリストは「マスターリスト」を表していますか?言い換えると、2番目のリストは、最初のリストの一部(または全体)を表すサブセットになりますか?
code4life 2013年

回答:


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ファンタスティック。本当にありがとう; まさに私が探していたもの。
シルクファイア2016年

3
順序付けリストにIDがないドキュメントの要素がある場合は機能しません
Dan Hunex

4
かなり非効率的-ソースコレクションのすべての要素に対してIndexOfが呼び出されており、OrderByは要素を並べ替える必要があります。@Jodrellによるソリューションははるかに高速です。
sdds 2018年

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正確に圧縮されない場合に機能しorderます。


私はこのソリューションを使用しましたが、うまくいきました。それだけで、メソッドを静的にし、クラスを静的にする必要がありました。
vinmm

5

ジョドレルの答えは最高ですが、実際には再実装しましたSystem.Linq.Enumerable.Join。JoinもLookupを使用し、ソースの順序を維持します。

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

それが私たちが求めている答えです
Orace

2
これは、Joinの書き換えがより簡単であることに誰もが同意したため、Joinが理解しにくいことを証明しています。
PRMan

-3

簡単な方法の1つは、順序付けシーケンスで圧縮することです。

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

なぜジップ後に注文するのですか?
Jodrell 2013年

なぜならZip、(Tupleへの)各インデックスを、対応するリストの同じ位置にあるドキュメントと組み合わせます。次に、OrderByはタプルをインデックス部分で並べ替え、selectは現在順序付けされたリストからドキュメントだけを掘り下げます。
アルビンSunnanbo 2013年

ただし、GetDocsFromDbの結果は順序付けされていないため、Item1とは無関係のタプルを作成しItem2ます。
Jodrell

1
インデックスではなく、uppon idの実行を注文しているため、これは誤った結果を生成すると思います。
Michael Logutov 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.