Метод LINQ для сортировки списка на основе большего списка

List<int> _lstNeedToOrder = new List<int>();
_lstNeedToOrder.AddRange(new int[] { 1, 5, 6, 8 });

//I need to sort this based on the below list.

List<int> _lstOrdered = new List<int>();//to order by this list
_lstOrdered.AddRange(new int[] { 13, 5, 11, 1, 4, 9, 2, 7, 12, 10, 3, 8, 6 });

order will be -->_lstNeedToOrder = 5,1,8,6

Как я могу это сделать?

Ответы

Ответ 1

Ну, простой, но неэффективный способ:

var result = _lstNeedToOrder.OrderBy(x => _lstOrdered.IndexOf(x));

Альтернативой было бы выработать далеко путь получения желаемого индекса значения. Если ваши значения всегда будут в диапазоне [1... n], вы можете просто инвертировать этот "упорядоченный" список как "список индексов по значению". В этот момент вы можете использовать:

var result = _lstNeedToOrder.OrderBy(x => indexes[x]);

(где indexes будет иметь дополнительное значение в начале для 0, просто для упрощения).

В качестве альтернативы вы можете создать значение Dictionary<int, int> от значения до индекса. Это было бы более общим, поскольку он обрабатывал бы очень широкий диапазон значений, не занимая много памяти. Но поиск словаря, очевидно, менее эффективен, чем поиск массива или списка.

Как и примечание, которое не будет хорошо форматироваться как комментарий, вашу инициализацию можно упростить с помощью инициализатора коллекции:

var listToOrder = new List<int> { 1, 5, 6, 8 };
var orderedList = new List<int> { 13, 5, 11, 1, 4, 9, 2, 7, 12, 10, 3, 8, 6 };

Ответ 2

    List<int> results = _lstOrdered.Where(item => _lstNeedToOrder.Contains(item)).ToList();

Ответ 3

Вы можете создать пользовательский сопоставитель следующим образом:

public class SequenceComparer<T> : IComparer<T> {
    private readonly Dictionary<T, int> indexes;

    public SequenceComparer(IEnumerable<T> sequence) {
        this.indexes =
            sequence
                .Select((item, index) => new { Item = item, Index = index })
                .ToDictionary(x => x.Item, x => x.Index);
    }

    public int Compare(T x, T y) {
        return indexes[x].CompareTo(indexes[y]);
    }
}

Теперь вы можете сказать

var result = _lstNeedToOrder.OrderBy(x => x, new SequenceComparer(_lstOrdered));

Ответ 4

Это работает довольно хорошо:

var lookup = _lstOrdered
    .Select((x, n) => new { x, n })
    .ToLookup(x => x.x, x => x.n);

var query =
    from x in _lstNeedToOrder
    let rank = lookup[x]
        .DefaultIfEmpty(int.MaxValue)
        .First()
    orderby rank
    select x;

Ответ 5

Другой вариант - использовать Intersect, который гарантирует возврат элементов в том порядке, в котором они появляются в первой последовательности.

Итак, в этом примере

var result = _lstOrdered.Intersect(_lstNeedToOrder);

дает { 5, 1, 8, 6} по мере необходимости.

Ответ 6

Сохранение в промежуточном словаре порядка...

// dict key will be the values of _lstOrdered, value will be the index of the
// key in _lstOrdered
// I'm using a seldom used .Select overload that returns the current value 
// plus its index (ix)
var dict = _lstOrdered.Select((p, ix) => new { Value = p, Ix = ix })
                      .ToDictionary(p => p.Value, p => p.Ix);

// note that this will explode if _lstNeedToOrder contains values outside
// _lstOrdered.
_lstNeedToOrder.Sort((p, q) => dict[p] - dict[q]);

Метод .Sort сортируется на месте, поэтому будет упорядочен _lstNeedToOrder.