Ответ 1
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();
У меня есть список с некоторыми идентификаторами вроде этого:
List<long> docIds = new List<long>() { 6, 1, 4, 7, 2 };
Morover, у меня есть еще один список элементов <T>
, которые представлены идентификаторами, описанными выше.
List<T> docs = GetDocsFromDb(...)
Мне нужно сохранить один и тот же порядок в обеих коллекциях, так что элементы в List<T>
должны находиться в том же положении, что и в первом (из-за ошибок в поисковых системах). И этот процесс не может быть выполнен в функции GetDocsFromDb()
.
При необходимости можно изменить второй список на некоторую другую структуру (например, Dictionary<long, T>
), но я бы предпочел не изменять его.
Есть ли простой и эффективный способ сделать это "упорядочение в зависимости от некоторых идентификаторов" с LINQ?
docs = docs.OrderBy(d => docsIds.IndexOf(d.Id)).ToList();
Поскольку вы не указываете 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
.
Ответ Джодрелла самый лучший, но на самом деле он переопределил System.Linq.Enumerable.Join
. Join также использует Lookup и сохраняет порядок источника.
docIds.Join(
docs,
i => i,
d => d.Id,
(i, d) => d);
Одним простым подходом является zip с последовательностью упорядочения:
List<T> docs = GetDocsFromDb(...).Zip(docIds, Tuple.Create)
.OrderBy(x => x.Item2).Select(x => x.Item1).ToList();