Линейный список разделов в списки 8 членов [дубликат

StackOverflow https://stackoverflow.com/questions/3773403

  •  04-10-2019
  •  | 
  •  

Вопрос

Этот вопрос уже имеет ответ здесь:

Как можно взять список (используя LINQ) и разбить его в список списков, разделяющих исходный список на каждой 8-й записи?

Я представляю что-то вроде этого будет включать пропустить и / или взять, но я все еще довольно новичок в LINQ.

Редактировать: Использование C # / .NET 3.5

Редактировать2: Этот вопрос сформулирован по-разному, чем другой «дубликат» вопрос. Хотя проблемы аналогичны, ответы в этом вопросе превосходят: как «принятый» ответ очень прочный (с yield Заявление), а также предложение Джона Скеета использовать Morelinq (который не рекомендуется в «другом» вопросе.) Иногда дубликаты хороши в том, что они заставляют повторную экспертизу проблемы.

Это было полезно?

Решение

Используйте следующий метод расширения, чтобы сломать вход в подмножество

public static class IEnumerableExtensions
{
    public static IEnumerable<List<T>> InSetsOf<T>(this IEnumerable<T> source, int max)
    {
        List<T> 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())
        {
                yield return toReturn;
        }
    }
}

Другие советы

У нас есть просто такой метод в Morelinq. как то Партия Метод:

// As IEnumerable<IEnumerable<T>>
var items = list.Batch(8);

или

// As IEnumerable<List<T>>
var items = list.Batch(8, seq => seq.ToList());

Вам лучше использовать библиотеку, как Morelinq., но если вам действительно пришлось сделать это, используя «простой LINQ», вы можете использовать GroupBy:

var sequence = new[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

var result = sequence.Select((x, i) => new {Group = i/8, Value = x})
                     .GroupBy(item => item.Group, g => g.Value)
                     .Select(g => g.Where(x => true));

// result is: { {1,2,3,4,5,6,7,8}, {9,10,11,12,13,14,15,16} }

В основном мы используем версию Select() Это дает индекс для потребленного значения, мы разделяем индекс на 8, чтобы определить, к какой группе принадлежат каждое значение. Затем мы группируем последовательность этой группировкой ключа. Последний Select просто уменьшает IGrouping<> вниз до АН IEnumerable<IEnumerable<T>> (и не является строго необходимым, поскольку IGrouping является IEnumerable).

Это достаточно легко, чтобы превратить это в многоразовый метод, факторинг нашей постоянной 8 В примере и замена его указанным параметром. Это не обязательно самое элегантное решение, и он больше не ленивый, потоковое решение ... Но это работает.

Вы также можете написать свой собственный метод расширения с использованием блоков итератора (yield return) который может дать вам лучшую производительность и использовать меньше памяти, чем GroupBy. Отказ Это то, что Batch() Метод Morelinq делает IIRC.

Это совсем не то, что имеется в виду оригинальные дизайнеры Linq, но проверьте это неправильное использование Groupby:

public static IEnumerable<IEnumerable<T>> BatchBy<T>(this IEnumerable<T> items, int batchSize)
{
    var count = 0;
    return items.GroupBy(x => (count++ / batchSize)).ToList();
}

[TestMethod]
public void BatchBy_breaks_a_list_into_chunks()
{
    var values = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    var batches = values.BatchBy(3);
    batches.Count().ShouldEqual(4);
    batches.First().Count().ShouldEqual(3);
    batches.Last().Count().ShouldEqual(1);
}

Я думаю, что это выигрывает премию «Golf» за этот вопрос. То ToList Очень важно, поскольку вы хотите убедиться, что группировка фактически была выполнена, прежде чем пытаться делать что-либо с выходом. Если вы удалите ToList, вы получите некоторые странные побочные эффекты.

Возьми не будет очень эффективным, потому что он не удаляет принимаемые записи.

Почему бы не использовать простую петлю:

public IEnumerable<IList<T>> Partition<T>(this/* <-- see extension methods*/ IEnumerable<T> src,int num)  
{  
    IEnumerator<T> enu=src.getEnumerator();  
    while(true)  
    {  
        List<T> result=new List<T>(num);  
        for(int i=0;i<num;i++)  
        {  
            if(!enu.MoveNext())  
            {  
                if(i>0)yield return result;  
                yield break;  
            }  
            result.Add(enu.Current);  
        }  
        yield return result;  
    }  
}
from b in Enumerable.Range(0,8) select items.Where((x,i) => (i % 8) == b);

Самое простое решение дается Мелом:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    int i = 0;
    return items.GroupBy(x => i++ / partitionSize).ToArray();
}

Кратко, но медленнее. Вышеуказанный способ расщепляется IEnumerable в кусочки желаемого фиксированного размера с общей численностью неважных кусочков. Разделить IENumerableable в число кусочков равных размеров или близко к равным размерам, вы можете сделать:

public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, 
                                                   int numOfParts)
{
    int i = 0;
    return items.GroupBy(x => i++ % numOfParts);
}

Ускорить вещи, простой подход будет делать:

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> items, 
                                                       int partitionSize)
{
    if (partitionSize <= 0)
        throw new ArgumentOutOfRangeException("partitionSize");

    int innerListCounter = 0;
    int numberOfPackets = 0;
    foreach (var item in items)
    {
        innerListCounter++;
        if (innerListCounter == partitionSize)
        {
            yield return items.Skip(numberOfPackets * partitionSize).Take(partitionSize);
            innerListCounter = 0;
            numberOfPackets++;
        }
    }

    if (innerListCounter > 0)
        yield return items.Skip(numberOfPackets * partitionSize);
}

Это быстрее всего, чем на планете сейчас :) эквивалентные методы для Split эксплуатация здесь

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top