c# - 使用 LINQ 获取 IEnumerable 中的上一个和下一个项目

标签 c# linq ienumerable

我有一个自定义类型的 IEnumerable。 (我从 SelectMany 得到的)

我在那个 IEnumerable 中也有一个项目 (myItem),我希望 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) 获取上一个或下一个项目。

  1. 这两个选项哪个更好?
  2. 是否有更好的选择?

“更好”包括性能(内存和速度)和可读性的结合;可读性是我最关心的问题。

最佳答案

首先

"Better" includes a combination of performance (memory and speed)

一般来说你不能两者兼得,经验法则是,如果你优化速度,它会消耗内存,如果你优化内存,它会消耗你的速度。

有一个更好的选择,它在内存和速度方面都表现良好,并且可以以可读的方式使用(我对函数名称不满意,但是,FindItemReturningPreviousItemFoundItemAndNextItem 是一个有点含糊)。

所以,看起来是时候使用自定义查找扩展方法了,比如 . . .

public static IEnumerable<T> FindSandwichedItem<T>(this IEnumerable<T> items, Predicate<T> matchFilling)
{
    if (items == null)
        throw new ArgumentNullException("items");
    if (matchFilling == null)
        throw new ArgumentNullException("matchFilling");

    return FindSandwichedItemImpl(items, matchFilling);
}

private static IEnumerable<T> FindSandwichedItemImpl<T>(IEnumerable<T> items, Predicate<T> matchFilling)
{
    using(var iter = items.GetEnumerator())
    {
        T previous = default(T);
        while(iter.MoveNext())
        {
            if(matchFilling(iter.Current))
            {
                yield return previous;
                yield return iter.Current;
                if (iter.MoveNext())
                    yield return iter.Current;
                else
                    yield return default(T);
                yield break;
            }
            previous = iter.Current;
        }
    }
    // If we get here nothing has been found so return three default values
    yield return default(T); // Previous
    yield return default(T); // Current
    yield return default(T); // Next
}

如果您需要多次引用项目,您可以将此结果缓存到列表中,但它会返回找到的项目,前面是前一个项目,然后是下一个项目。例如

var sandwichedItems = myIEnumerable.FindSandwichedItem(item => item.objectId == "MyObjectId").ToList();
var previousItem = sandwichedItems[0];
var myItem = sandwichedItems[1];
var nextItem = sandwichedItems[2];

如果它是第一个或最后一个项目,则返回的默认值可能需要根据您的要求进行更改。

希望这对您有所帮助。

关于c# - 使用 LINQ 获取 IEnumerable 中的上一个和下一个项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8759849/

相关文章:

c# - 如何本地化 asp.net mvc 3 应用程序

c# - 需要 Func 提供给 IEnumerable 和 IQueryable 的 Where() 方法

c# - 在 IEnumerable 上使用扩展方法的 Foreach

c# - 如何将 IDictionary<TKey, List<TValue>> 转换为 IDictionary<TKey, IEnumerable<TValue>>?

c# - Enumerator 结构的可变性

c# - 创建访问属性的表达式

c# - DataGridView 中的选定行始终为 0

c# - 我可以结合使用 LINQ 和 foreach 来将对象添加到集合中吗?

c# - 如何在 linq 表达式中格式化字符串?

c# - 如何使用 C# 和 asp.net 创建带用户名的子域?