Linq to Objects - вернуть пары чисел из списка номеров - PullRequest
12 голосов
/ 16 декабря 2010
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}.

Ответы [ 12 ]

7 голосов
/ 16 декабря 2010

Ни один из методов 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;
}
3 голосов
/ 16 декабря 2010

Попробуйте это:

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});
1 голос
/ 28 июля 2016
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);
1 голос
/ 17 декабря 2010
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 голос
/ 16 декабря 2010

(предупреждение: выглядит ужасно)

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 голос
/ 16 декабря 2010
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);
1 голос
/ 16 декабря 2010

Это может быть немного более общим, чем требуется - вы можете установить пользовательский 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();

EDIT:

Если вы хотите добавить нули (или другое число), если последняя группа имеет другой размер:

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();
0 голосов
/ 17 июня 2019

Другое простое решение с использованием index и index + 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().

0 голосов
/ 03 декабря 2018

Другой вариант - использовать метод 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});
0 голосов
/ 16 декабря 2010
    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() };
...