다른 목록 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

첫 번째 목록은 "마스터 목록"을 나타 냅니까? 다시 말해서, 두 번째 목록은 첫 번째 목록의 일부 (또는 전체)를 나타내는 하위 집합입니까?
code4life

답변:


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

@Kaf는 내가 upvoted 한 이유는 문서 ID 속성이라는 것을 아는 것에 의존합니다 Id. 질문에 명시되어 있지 않습니다.
Jodrell

3
@ BorjaLópez, 빠른 메모. 당신은 당신의 질문에 효율성을 언급합니다. IndexOf예를 들어 완벽하고 훌륭하며 훌륭합니다. 데이터가 많으면 내 대답이 더 적합 할 수 있습니다. stackoverflow.com/questions/3663014/…
Jodrell

2
트윗 담아 가기 정말 고마워; 정확히 내가 찾던 것.
silkfire

3
주문 목록에 ID가없는 문서의 요소가있는 경우 작동하지 않습니다.
Dan Hunex

4
매우 비효율적-소스 콜렉션의 모든 요소에 대해 IndexOf가 호출되고 OrderBy가 요소를 정렬해야합니다. @Jodrell의 솔루션이 훨씬 빠릅니다.
sdds

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

Jodrell의 답변이 가장 좋지만 실제로 그는 다시 구현했습니다 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

Zip각 색인 (튜플로)을 해당 목록에서 동일한 위치에있는 문서와 결합 하기 때문 입니다. 그런 다음 OrderBy는 색인 부분별로 Tuple을 정렬 한 다음 select는 지금 정렬 된 목록에서 문서를 파냅니다.
Albin Sunnanbo

그러나 GetDocsFromDb의 결과는 정렬되지 않으므로 Item1에 관련이없는 Tuples를 작성하게 됩니다 Item2.
Jodrell

1
인덱스가 아닌 uppon id를 주문한 이후 잘못된 결과가 발생할 것이라고 생각합니다.
Michael Logutov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.