问题 Linq to Objects - 从数字列表中返回数字对


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}


12768
2017-12-16 13:45


起源

您自己提出的问题的完全重复 stackoverflow.com/questions/3575925/... - Jahan Zinedine
@Jani不,不是。那要求相当于Python的(或Ruby的)Zip()方法 - >需要两个列表并生成一个元组列表。这个问题是关于分区单个列表。 - Cristi Diaconescu
@Cristi得到了,抱歉 - Jahan Zinedine
一个非常相似的答案 - 滑动窗口得到 {{1,2},{2,3},{3,4}...  - 应该很容易适应这里: stackoverflow.com/questions/577590/... - Richard
@Jani其实我也错了,这个问题与Zip()方法无关,但仍然是一个不同的问题。 - Cristi Diaconescu


答案:


没有任何默认的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;
}

7
2017-12-16 14:47



看看所有答案,Linq似乎不是最好的方法。你的实现很干净。 - Cristi Diaconescu
它应该是 public static IEnumerable<T[]> Partition<T>...。 - Hamish Grubijan


尝试这个:

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

3
2017-12-16 13:49



你可以。选择((n,i)=> ...)来获得一个自动计数器 - Cristi Diaconescu


(警告:看起来很难看)

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

1
2017-12-16 14:14



从Danny Chen窃取,你可以在Zip的第二个参数上附加一个'0'元素,从而摆脱你的最后一个.Concat(...)块。 - Cristi Diaconescu


这可能比您需要的更通用 - 您可以设置自定义 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
2017-12-16 14:08



凉!一个警告,如果数字列表具有奇数,则成对的最后一项将只有1个元素。例如。 nums = {1,2,3} => pairs = {{1,2},{3}} - Cristi Diaconescu
请参阅一般情况的更新。如果您只需要每组有两个项目,您可以进行更简单的检查 nums.Count() 并附加一个 0 如果它很奇怪 - Yakimych


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

1
2017-12-17 14:48





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
2017-12-16 14:09



我将始终添加一个值为0的元素,如果数字的长度是偶数,它将自动被忽略。 - Danny Chen
我不知道有一个支持索引的Where()版本。 +1 - Cristi Diaconescu
它适用于奇数长度的列表:) - Cristi Diaconescu


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

0
2017-12-16 14:28





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

0
2017-12-16 13:51



-1:那不是他要求的。 - Lasse Vågsæther Karlsen
这将返回{{1,2},{2,3},...}对 - Yakimych
@Lasse它工作正常,因为我理解这个问题所以请撤销你的downvote - Jahan Zinedine


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

如果你需要填充零的最后一对,你可以在进行分组之前添加它,如果listcount是奇数。

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

0
2017-07-28 16:33





这给了所有可能的对(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}


-1
2017-12-16 13:58



那不是我要求的。 - Cristi Diaconescu
我并没有注意到这些配对是有效的。我编辑了答案 - Ali Tarhini