【问题标题】:C# & LINQ, Select two (consecutive) items at once [duplicate]C#和LINQ,一次选择两个(连续)项目[重复]
【发布时间】:2016-02-10 22:28:33
【问题描述】:

在有序集(数组、列表)上使用 LINQ,有没有办法选择或以其他方式使用两个连续的项目?我在想象语法:

list.SelectTwo((x, y) => ...)

其中xy 是列表/数组中索引ii + 1 处的项目。 可能没有办法做到这一点,我接受这是一种可能性,但我至少想说我试图找到答案。

我知道我可以使用其他东西和 LINQ 来实现这一点。

提前谢谢你。

【问题讨论】:

  • LINQ 只是 IEnumerables 上的扩展方法。如果最终目标只是能够以 LINQ-y 方式使用它,您可以非常轻松地自己实现它

标签: c# linq


【解决方案1】:

Another answer 使用 LINQ 的 SkipZip 提供了一个不错且干净的解决方案。

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

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);
                }
            }
        }
    }
}

例子:

var seq = new[] {"A", "B", "C", "D"}.SelectTwo((a, b) => a + b);

生成的序列包含"AB""BC""CD"

【讨论】:

    【解决方案2】:

    System.Linq.Enumerable.Zip 通过为每个i 配对i-th 元素来组合两个IEnumerables。所以你只需要Zip你的列表,并使用它的移动版本。

    作为一个不错的扩展方法:

    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);
        }
    }
    

    例子:

    Enumerable.Range(1,5).SelectTwo((a,b) => $"({a},{b})");
    

    结果:

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

    【讨论】:

    • 我不知道这个。这很棒!我只想指出它会枚举源两次,这可能是可取的,也可能不是可取的(视情况而定)。
    【解决方案3】:

    你可以的

    list.Skip(i).Take(2)
    

    这将返回一个IEnumerable&lt;T&gt;,其中只有两个连续的项目。

    【讨论】:

    • 我不认为这是要求的(尽管问题有点模糊)
    【解决方案4】:

    我认为您可以像这样在有序列表中选择项目和下一个项目数据:

    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});
    

    但是,index 的选定项应该小于列表大小 - 1。

    【讨论】:

      【解决方案5】:

      如果源序列有一个索引器,即至少是IReadOnlyList&lt;T&gt;(问题中提到的数组,列表),并且想法是将序列拆分为连续对(从问题中不太清楚) ,那么就可以这样简单的完成了

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

      【讨论】:

        【解决方案6】:

        您可以使用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]));
            }
        }
        

        你可以这样使用它:

        var list = new[] {1, 2, 3, 4, 5, 6};
        
        var result = list.SelectTwo((x, y) => x + y).ToList();
        

        这将返回 {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;
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2012-07-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-10-14
          • 1970-01-01
          • 1970-01-01
          • 2020-10-29
          相关资源
          最近更新 更多