C# C&LINQ,一次选择两个(连续)项

C# C&LINQ,一次选择两个(连续)项,c#,linq,C#,Linq,在有序集数组list上使用LINQ,是否有方法选择或以其他方式使用两个连续项?我在想象语法: list.SelectTwo((x, y) => ...) 其中x和y是列表/数组中索引i和i+1处的项。 可能没有办法做到这一点,我接受这一点,但我至少想说,我试图找到一个答案 我知道我可以使用其他的工具和LINQ来实现这一点 提前谢谢你。你可以这样做 list.Skip(i).Take(2) 这将返回一个IEnumerable,其中只包含两个连续项。我认为您可以选择有序列表中的项和下一项数

在有序集数组list上使用LINQ,是否有方法选择或以其他方式使用两个连续项?我在想象语法:

list.SelectTwo((x, y) => ...)
其中x和y是列表/数组中索引i和i+1处的项。 可能没有办法做到这一点,我接受这一点,但我至少想说,我试图找到一个答案

我知道我可以使用其他的工具和LINQ来实现这一点

提前谢谢你。

你可以这样做

list.Skip(i).Take(2)

这将返回一个IEnumerable,其中只包含两个连续项。

我认为您可以选择有序列表中的项和下一项数据,如下所示:

var theList = new List<T>();
theList
    .Select((item, index) => new { CurrIndex = index, item.Prop1, item.Prop2, theList[index + 1].Prop1 })
    .Where(newItem => {some condition on the item});
var list = new[] {1, 2, 3, 4, 5, 6};

var result = list.SelectTwo((x, y) => x + y).ToList();
然而,所选项目的索引应该小于列表大小-1。

使用LINQ的Skip和Zip提供了一个很好且干净的解决方案

这是绝对正确的,但我想指出,它列举了源代码两次。这可能重要,也可能不重要,取决于每个单独的用例。如果这对您的情况很重要,这里有一个较长的替代方案,它在功能上是等效的,但只列举一次源代码:

static class EnumerableUtilities
{
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
                                                                   Func<TSource, TSource, TResult> selector)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        if (selector == null) throw new ArgumentNullException(nameof(selector));

        return SelectTwoImpl(source, selector);
    }

    private static IEnumerable<TResult> SelectTwoImpl<TSource, TResult>(this IEnumerable<TSource> source,
                                                                        Func<TSource, TSource, TResult> selector)
    {
        using (var iterator = source.GetEnumerator())
        {
            var item2 = default(TSource);
            var i = 0;
            while (iterator.MoveNext())
            {
                var item1 = item2;
                item2 = iterator.Current;
                i++;

                if (i >= 2)
                {
                    yield return selector(item1, item2);
                }
            }
        }
    }
}
结果序列包含AB、BC、CD。

System.Linq.Enumerable.Zip通过将每个i的第i个元素配对,组合了两个IEnumerable。所以你只需要用一个移动版本来压缩你的列表

作为一种很好的扩展方法:

using System.Collections.Generic;
using System.Linq;

static class ExtMethods
{
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
                                                                        Func<TSource, TSource, TResult> selector)
    {
        return Enumerable.Zip(source, source.Skip(1), selector);
    }
}
结果:

(1,2) (2,3) (3,4) (4,5)

您可以使用一个特殊的Select重载,该重载允许您使用项目的索引,并使用GroupBy方法将列表拆分为多个组。每组将有两个项目。下面是一个扩展方法,它可以:

public static class ExtensionMethods
{
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
        Func<TSource, TSource, TResult> selector)
    {
        return source.Select((item, index) => new {item, index})
            .GroupBy(x => x.index/2)
            .Select(g => g.Select(i => i.item).ToArray())
            .Select(x => selector(x[0], x[1]));
    }
}
这将返回{3,7,11}

请注意,在开始产生结果之前,上述方法将数据分组到内存中。如果您有大型数据集,则可能希望在枚举源数据时使用流式方法生成数据,例如:

public static class ExtensionMethods
{
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
        Func<TSource, TSource, TResult> selector)
    {
        bool first_item_got = false;

        TSource first_item = default(TSource);

        foreach (var item in source)
        {
            if (first_item_got)
            {
                yield return selector(first_item, item);
            }
            else
            {
                first_item = item;
            }

            first_item_got = !first_item_got;
        }
    }
}

如果源序列有一个索引器,即至少是问题中提到的IReadOnlyList数组,list,并且想法是将序列拆分为连续对,这与问题不太清楚,那么可以像这样简单地完成

var pairs = Enumerable.Range(0, list.Count / 2)
    .Select(i => Tuple.Create(list[2 * i], list[2 * i + 1]));

LINQ只是IEnumerables上的扩展方法。你可以很容易地自己实现这一点,如果最终目标只是能够以一种灵巧的方式使用它,我不认为这是要求的,尽管问题有点模糊,我不知道这一点。这太棒了!我只想指出,它列举了源代码两次,这可能是可取的,也可能是不可取的,这取决于具体情况。
public static class ExtensionMethods
{
    public static IEnumerable<TResult> SelectTwo<TSource, TResult>(this IEnumerable<TSource> source,
        Func<TSource, TSource, TResult> selector)
    {
        bool first_item_got = false;

        TSource first_item = default(TSource);

        foreach (var item in source)
        {
            if (first_item_got)
            {
                yield return selector(first_item, item);
            }
            else
            {
                first_item = item;
            }

            first_item_got = !first_item_got;
        }
    }
}
var pairs = Enumerable.Range(0, list.Count / 2)
    .Select(i => Tuple.Create(list[2 * i], list[2 * i + 1]));