У меня есть IEnumerable настраиваемого типа. (Это я получил от SelectMany)
У меня также есть элемент (myItem) в этом IEnumerable, который мне нужен предыдущий и следующий элемент из IEnumerable.
В настоящее время я делаю желаемое следующим образом:
var previousItem = myIEnumerable.Reverse().SkipWhile(
i => i.UniqueObjectID != myItem.UniqueObjectID).Skip(1).FirstOrDefault();
Я могу получить следующий элемент, просто опуская .Reverse
.
или , я мог бы:
int index = myIEnumerable.ToList().FindIndex(
i => i.UniqueObjectID == myItem.UniqueObjectID)
, а затем использовать .ElementAt (index +/- 1)
, чтобы получить предыдущий или следующий элемент.
«Лучше» включает сочетание производительности (памяти и скорости) и удобочитаемости; с удобочитаемостью, что является моей главной заботой.
Вот некоторые дополнительные методы, как обещано. Названия родовые и повторно используемые с любым простым типом и есть перегрузки поиска, чтобы достигнуть товар, должен был получить следующие или предыдущие товары. Я определил бы эффективность решений и затем видел бы, где Вы могли отжать циклы.
public static class ExtensionMethods
{
public static T Previous<T>(this List<T> list, T item) {
var index = list.IndexOf(item) - 1;
return index > -1 ? list[index] : default(T);
}
public static T Next<T>(this List<T> list, T item) {
var index = list.IndexOf(item) + 1;
return index < list.Count() ? list[index] : default(T);
}
public static T Previous<T>(this List<T> list, Func<T, Boolean> lookup) {
var item = list.SingleOrDefault(lookup);
var index = list.IndexOf(item) - 1;
return index > -1 ? list[index] : default(T);
}
public static T Next<T>(this List<T> list, Func<T,Boolean> lookup) {
var item = list.SingleOrDefault(lookup);
var index = list.IndexOf(item) + 1;
return index < list.Count() ? list[index] : default(T);
}
public static T PreviousOrFirst<T>(this List<T> list, T item) {
if(list.Count() < 1)
throw new Exception("No array items!");
var previous = list.Previous(item);
return previous == null ? list.First() : previous;
}
public static T NextOrLast<T>(this List<T> list, T item) {
if(list.Count() < 1)
throw new Exception("No array items!");
var next = list.Next(item);
return next == null ? list.Last() : next;
}
public static T PreviousOrFirst<T>(this List<T> list, Func<T,Boolean> lookup) {
if(list.Count() < 1)
throw new Exception("No array items!");
var previous = list.Previous(lookup);
return previous == null ? list.First() : previous;
}
public static T NextOrLast<T>(this List<T> list, Func<T,Boolean> lookup) {
if(list.Count() < 1)
throw new Exception("No array items!");
var next = list.Next(lookup);
return next == null ? list.Last() : next;
}
}
И Вы можете использовать их как это.
var previous = list.Previous(obj);
var next = list.Next(obj);
var previousWithLookup = list.Previous((o) => o.LookupProperty == otherObj.LookupProperty);
var nextWithLookup = list.Next((o) => o.LookupProperty == otherObj.LookupProperty);
var previousOrFirst = list.PreviousOrFirst(obj);
var nextOrLast = list.NextOrLast(ob);
var previousOrFirstWithLookup = list.PreviousOrFirst((o) => o.LookupProperty == otherObj.LookupProperty);
var nextOrLastWithLookup = list.NextOrLast((o) => o.LookupProperty == otherObj.LookupProperty);
Вот дополнительный метод LINQ, который возвращает текущий объект, наряду с предыдущим и следующим. Это уступает ValueTuple
типы для предотвращения выделений. Источник перечисляется однажды.
public static IEnumerable<(T Previous, T Current, T Next)> WithPreviousAndNext<T>(
this IEnumerable<T> source, T firstPrevious = default, T lastNext = default)
{
Queue<T> queue = new Queue<T>(2);
queue.Enqueue(firstPrevious);
foreach (var item in source)
{
if (queue.Count > 1)
{
yield return (queue.Dequeue(), queue.Peek(), item);
}
queue.Enqueue(item);
}
if (queue.Count > 1) yield return (queue.Dequeue(), queue.Peek(), lastNext);
}
пример Использования:
var source = Enumerable.Range(1, 5);
Console.WriteLine($"Source: {String.Join(", ", source)}");
var result = source.WithPreviousAndNext(firstPrevious: -1, lastNext: -1);
Console.WriteLine($"Result: {String.Join(", ", result)}");
Вывод:
Источник: 1, 2, 3, 4, 5
Результат: (-1, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5,-1)
Для получения предыдущего и следующего из определенного объекта (использующий разрушение кортежа ):
var (previous, current, next) = myIEnumerable
.WithPreviousAndNext()
.First(e => e.Current.UniqueObjectID == myItem.UniqueObjectID);