【问题标题】:Linq to Objects - return pairs of numbers from list of numbersLinq to Objects - 从数字列表中返回数字对
【发布时间】:2011-05-26 13:43:00
【问题描述】:
var nums = new[]{ 1, 2, 3, 4, 5, 6, 7};
var pairs  = /* some linq magic here*/ ;

=> 对 = { {1, 2}, {3, 4}, {5, 6}, {7, 0} }

pairs 的元素应该是两个元素的列表,或者是一些具有两个字段的匿名类的实例,例如 new {First = 1, Second = 2}

【问题讨论】:

  • 完全重复自己提出的问题stackoverflow.com/questions/3575925/…
  • @Jani 不,不是。这要求等效于 Python(或 Ruby)的 Zip() 方法 -> 需要两个列表并生成一个元组列表。这个问题是关于对单个列表进行分区的。
  • 一个非常相似的答案——滑动窗口得到{{1,2},{2,3},{3,4}...——应该很容易适应在这里:stackoverflow.com/questions/577590/…
  • @Jani 其实我也错了,这个问题与 Zip() 方法无关,但仍然是另一个问题。
  • @Richard 使用自定义迭代器,而不是 Linq 表达式。我承认这可能是最干净的方法。

标签: c# linq linq-to-objects aggregate slice


【解决方案1】:

没有一个默认的 linq 方法可以通过单次扫描懒惰地执行此操作。使用自身压缩序列进行 2 次扫描,并且分组并不完全是懒惰的。最好的办法是直接实现它:

public static IEnumerable<T[]> Partition<T>(this IEnumerable<T> sequence, int partitionSize) {
    Contract.Requires(sequence != null)
    Contract.Requires(partitionSize > 0)

    var buffer = new T[partitionSize];
    var n = 0;
    foreach (var item in sequence) {
        buffer[n] = item;
        n += 1;
        if (n == partitionSize) {
            yield return buffer;
            buffer = new T[partitionSize];
            n = 0;
        }
    }
    //partial leftovers
    if (n > 0) yield return buffer;
}

【讨论】:

  • 查看所有答案,Linq 似乎不是最好的方法。你的实现非常干净。
  • 应该是public static IEnumerable&lt;T[]&gt; Partition&lt;T&gt;...
【解决方案2】:

试试这个:

int i = 0;
var pairs = 
  nums
    .Select(n=>{Index = i++, Number=n})
    .GroupBy(n=>n.Index/2)
    .Select(g=>{First:g.First().Number, Second:g.Last().Number});

【讨论】:

  • 你可以使用 .Select((n, i)=>...) 来获得一个自动计数器
【解决方案3】:
int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var result = numbers.Zip(numbers.Skip(1).Concat(new int[] { 0 }), (x, y) => new
        {
            First = x,
            Second = y
        }).Where((item, index) => index % 2 == 0);

【讨论】:

  • 我会一直添加一个值为0的元素,如果数字长度是偶数会自动忽略。
  • 我不知道有支持索引的 Where() 版本。 +1
【解决方案4】:

(警告:看起来很丑)

var pairs = x.Where((i, val) => i % 2 == 1)
            .Zip(
            x.Where((i, val) => i % 2 == 0),
                (first, second) =>
                new
                {
                    First = first,
                    Second = second
                })
            .Concat(x.Count() % 2 == 1 ? new[]{
                new
                {
                    First = x.Last(),
                    Second = default(int)
                }} : null);

【讨论】:

  • 从 Danny Chen 那里窃取信息,您可以将 '0' 元素附加到 Zip 的第二个参数,从而摆脱最后一个 .Concat(...) 块。
【解决方案5】:

这可能比您需要的更通用 - 您可以设置自定义itemsInGroup

int itemsInGroup = 2;
var pairs = nums.
            Select((n, i) => new { GroupNumber = i / itemsInGroup, Number = n }).
            GroupBy(n => n.GroupNumber).
            Select(g => g.Select(n => n.Number).ToList()).
            ToList();

编辑:

如果您想附加零(或其他数字)以防最后一组大小不同:

int itemsInGroup = 2;
int valueToAppend = 0;
int numberOfItemsToAppend = itemsInGroup - nums.Count() % itemsInGroup;

var pairs = nums.
            Concat(Enumerable.Repeat(valueToAppend, numExtraItems)).
            Select((n, i) => new { GroupNumber = i / itemsInGroup, Number = n }).
            GroupBy(n => n.GroupNumber).
            Select(g => g.Select(n => n.Number).ToList()).
            ToList();

【讨论】:

  • 酷!需要注意的是,如果数字列表具有奇数,则成对的最后一项将只有 1 个元素。例如。 nums = {1,2,3} => 对 = { {1, 2}, {3} }
  • 查看一般情况的更新。如果您只需要每组两个项目,则可以对 nums.Count() 进行更简单的检查,如果奇怪,则附加一个 0
【解决方案6】:
public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
{
    return InSetsOf(source, max, false, default(T));
}

public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max, bool fill, T fillValue)
{
    var toReturn = new List<T>(max);
    foreach (var item in source)
    {
        toReturn.Add(item);
        if (toReturn.Count == max)
        {
            yield return toReturn;
            toReturn = new List<T>(max);
        }
    }
    if (toReturn.Any())
    {
        if (fill)
        {
            toReturn.AddRange(Enumerable.Repeat(fillValue, max-toReturn.Count));
        }
        yield return toReturn;
    }
}

用法:

var pairs = nums.InSetsOf(2, true, 0).ToArray();

【讨论】:

    【解决方案7】:
    IList<int> numbers = new List<int> {1, 2, 3, 4, 5, 6, 7};
    var grouped = numbers.GroupBy(num =>
    {
       if (numbers.IndexOf(num) % 2 == 0)
       {
          return numbers.IndexOf(num) + 1;
       }
       return numbers.IndexOf(num);
    });
    

    如果您需要最后一对用零填充,如果列表计数是奇数,您可以在进行分组之前添加它。

    if (numbers.Count() % 2 == 1)
    {
       numbers.Add(0);
    }
    

    另一种方法可能是:

    var groupedIt = numbers
       .Zip(numbers.Skip(1).Concat(new[]{0}), Tuple.Create)
       .Where((x,i) => i % 2 == 0);
    

    或者你使用有很多有用扩展的 MoreLinq:

    IList<int> numbers = new List<int> {1, 2, 3, 4, 5, 6, 7};
    var batched = numbers.Batch(2);
    

    【讨论】:

      【解决方案8】:
          var w =
              from ei in nums.Select((e, i) => new { e, i })
              group ei.e by ei.i / 2 into g
              select new { f = g.First(), s = g.Skip(1).FirstOrDefault() };
      

      【讨论】:

        【解决方案9】:
         var nums = new float[] { 1, 2, 3, 4, 5, 6, 7 };
         var enumerable = 
                Enumerable
                  .Range(0, nums.Length)
                  .Where(i => i % 2 == 0)
                  .Select(i => 
                     new { F = nums[i], S = i == nums.Length - 1 ? 0 : nums[i + 1] });
        

        【讨论】:

        • 这将返回对为 {{1,2}, {2,3}, ...}
        • @Lasse 它工作正常,因为我理解这个问题,所以请撤销你的反对意见
        【解决方案10】:

        另一个选项是使用 SelectMany LINQ 方法。这更适合那些希望遍历项目列表并为每个项目返回 2 个或更多属性的人。无需为每个属性再次遍历列表,只需一次。

        var list = new [] {//Some list of objects with multiple properties};
        
        //Select as many properties from each Item as required. 
        IEnumerable<string> flatList = list.SelectMany(i=> new[]{i.NameA,i.NameB,i.NameC});
        

        【讨论】:

        • 感谢您的回答,但这会从每个项目中获取属性并将它们展平为一个列表。这不是最初的问题。
        【解决方案11】:

        另一个使用indexindex + 1 的简单解决方案。

        var nums = Enumerable.Range(1, 10);
        var pairs = nums.Select((item, index) =>
          new { First = item, Second = nums.ElementAtOrDefault(index + 1) })
            .SkipLastN(1);
        
        pairs.ToList().ForEach(p => Console.WriteLine($"({p.First}, {p.Second}) "));
        

        最后一项无效,必须使用SkipLastN() 删除。

        【讨论】:

          【解决方案12】:

          这给出了所有可能的对(vb.net):

          Dim nums() = {1, 2, 3, 4, 5, 6, 7}
          Dim pairs = From a In nums, b In nums Where a <> b Select a, b
          

          编辑:

           Dim allpairs = From a In nums, b In nums Where b - a = 1 Select a, b
           Dim uniquePairs = From p In allpairs Where p.a Mod 2 <> 0 Select p
          

          注意:最后一对丢失了,正在处理中

          编辑:

          联合 uniquePairs{nums.Last,0}

          【讨论】:

          • 我没有注意到这些对是按顺序排列的。我编辑了答案
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-12-25
          • 1970-01-01
          • 1970-01-01
          • 2018-03-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多