LINQ를 사용하여 컬렉션을 'n'부분으로 분할 하시겠습니까?


122

nLINQ를 사용 하여 컬렉션을 여러 부분으로 분할하는 좋은 방법이 있습니까? 물론 균등할 필요는 없습니다.

즉, 컬렉션을 하위 컬렉션으로 나누고 싶습니다. 각 하위 컬렉션에는 요소의 하위 집합이 포함되어 있으며 마지막 컬렉션은 비정형 일 수 있습니다.


1
Retagged : 질문은 asp.net과 관련이 없습니다. 질문에 적절하게 태그를 지정하십시오.

균등하지 않더라도 얼마나 정확하게 분할 하시겠습니까 (물론 끝까지 허용)?
Marc Gravell

1
누가이 질문에 연결 했습니까? 존이 당신 이었나요? :-) 갑자기 모든 답변 :-)
Simon_Weaver


@Simon_Weaver 받아 들인 답변을 바탕으로 당신이 요구하는 것을 명확히하려고했습니다. 사실, 목록의 각 요소를 요소로 분해하고이를 소위 '병렬'목록에 넣는 등 목록을 '분할'하는 방법에는 여러 가지가 있습니다.
jpaugh

답변:


127

순수한 linq와 가장 간단한 해결책은 아래와 같습니다.

static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
        int i = 0;
        var splits = from item in list
                     group item by i++ % parts into part
                     select part.AsEnumerable();
        return splits;
    }
}

3
당신은 할 수 있습니다 : select (IEnumerable <T>) part 대신 part.AsEnumerable ()을 선택하십시오. 더 우아하게 느껴집니다.
tuinstoel 2009.02.11

2
이러한 모든 모듈러스 작업을 수행하면 긴 목록에서 약간의 비용이 발생할 수 있습니다.
Jonathan Allen

8
인덱스를 포함하는 Select 오버로드를 사용하는 것이 좋습니다.
Marc Gravell

1
선택 오버로드 및 메서드 체인 구문을 사용하는 응답을 추가했습니다
reustmd 2011 년

1
.AsEnumerable()필요하지 않습니다. IGrouping <T>는 이미 IEnumerable <T>입니다.
Alex

58

편집 : 좋아, 내가 질문을 잘못 읽은 것 같습니다. 나는 그것을 "n 개"라기보다는 "길이 n 개"로 읽었다. 도! 답변 삭제 고려 중 ...

(원래 답변)

LINQ to Objects에 대한 추가 항목 집합에 하나를 작성하려고하지만 기본 제공 분할 방법이 있다고 생각하지 않습니다. Marc Gravell은 여기구현이 있지만 읽기 전용 뷰를 반환하도록 수정합니다.

public static IEnumerable<IEnumerable<T>> Partition<T>
    (this IEnumerable<T> source, int size)
{
    T[] array = null;
    int count = 0;
    foreach (T item in source)
    {
        if (array == null)
        {
            array = new T[size];
        }
        array[count] = item;
        count++;
        if (count == size)
        {
            yield return new ReadOnlyCollection<T>(array);
            array = null;
            count = 0;
        }
    }
    if (array != null)
    {             
        Array.Resize(ref array, count);
        yield return new ReadOnlyCollection<T>(array);
    }
}

젠장-나를 이길 ;-p
Marc Gravell

3
"배열 [count ++]" 이 정말 마음에 들지 않습니다. eh ;-p
Marc Gravell

18
OP에 대한 답이 아니더라도 삭제하지 않으셔서 감사합니다. 나는 똑같은 것을 원했습니다-길이 n :).
Gishu

2
@Dejan : 아니요. 의 사용을합니다 yield return. 한 번에 하나의 배치 가 메모리에 있어야 하지만 그게 전부입니다.
Jon Skeet 2014

1
@Dejan : 오른쪽 - 나는 않을 것 같은이 : 솔직히 말해서, 병렬 LINQ 분할과 상호 작용하는 방법에 대해 생각하기
존 소총을

39
static class LinqExtensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int parts)
    {
            return list.Select((item, index) => new {index, item})
                       .GroupBy(x => x.index % parts)
                       .Select(x => x.Select(y => y.item));
    }
}

28
저는 SQL 스타일 Linq를 비합리적으로 싫어하므로 이것이 제가 가장 좋아하는 대답입니다.
piedar 2014 년

1
@ manu08, UR 코드를 시도해 보았고 목록이 var dept = {1,2,3,4,5}있습니다. 분할 후 결과는 비슷 dept1 = {1,3,5}하고 dept2 = { 2,4 }parts = 2. 그러나 필요 난 결과는 dept1 = {1,2,3}dept2 = {4,5}
KARTHIK Arthik

3
나는 모듈로와 같은 문제가 있었기 때문에 열 길이를 계산 int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);한 다음 .GroupBy(x => x.index / columnLength). 한 가지 단점은 Count ()가 목록을 열거한다는 것입니다.
goodeye

24

좋아, 반지에 모자를 던질 게. 내 알고리즘의 장점 :

  1. 값 비싼 곱셈, 나눗셈 또는 모듈러스 연산자 없음
  2. 모든 작업은 O (1)입니다 (아래 참고 참조).
  3. IEnumerable <> 소스에서 작동합니다 (Count 속성이 필요하지 않음).
  4. 단순한

코드:

public static IEnumerable<IEnumerable<T>>
  Section<T>(this IEnumerable<T> source, int length)
{
  if (length <= 0)
    throw new ArgumentOutOfRangeException("length");

  var section = new List<T>(length);

  foreach (var item in source)
  {
    section.Add(item);

    if (section.Count == length)
    {
      yield return section.AsReadOnly();
      section = new List<T>(length);
    }
  }

  if (section.Count > 0)
    yield return section.AsReadOnly();
}

아래 주석에서 지적했듯이이 접근 방식은 거의 동일한 길이의 고정 된 수의 섹션을 요구하는 원래 질문을 실제로 해결하지 않습니다. 즉, 내 접근 방식을 사용하여 원래 질문을 다음과 같이 호출하여 해결할 수 있습니다.

myEnum.Section(myEnum.Count() / number_of_sections + 1)

이 방식으로 사용하면 Count () 연산이 O (N)이므로 접근 방식은 더 이상 O (1)이 아닙니다.


Brilliant-여기에 최고의 솔루션! 몇 가지 최적화 : * 각 섹션에 대해 새 목록을 만드는 대신 연결 목록을 지 웁니다. 연결 목록에 대한 참조는 호출자에게 반환되지 않으므로 완전히 안전합니다. 소스가 비어있는 경우에는 할당이 없다 그런 식으로 - 당신은 첫 번째 항목에 도달 할 때까지 * 링크 된 목록을 작성하지 마십시오
ShadowChaser

3
@ShadowChaser MSDN에 따르면 LinkedList를 지우는 것은 O (N) 복잡성이므로 O (1)의 목표를 망칠 것입니다. 물론, foreach가 시작하려면 O (N)라고 주장 할 수 있습니다 ... :)
Mike

4
당신의 대답은 옳지 만 질문은 틀 렸습니다. 귀하의 답변은 각 청크에 대해 고정 된 크기의 알 수없는 청크 수를 제공합니다. 그러나 OP는 청크 당 크기에 상관없이 고정 된 수의 청크를 제공하는 분할 기능을 원합니다 (동일한 크기에 가깝거나 같음). 아마 자세한 내용은 여기 적합 stackoverflow.com/questions/3773403/...
nawfal

1
@ Mike 당신은 그것을 벤치마킹 했습니까? O (1)이 더 빠르다는 의미가 아니라 파티셔닝에 필요한 시간이 확장되지 않음을 의미한다는 것을 알고 있기를 바랍니다. 모든 실제 시나리오에서 다른 O (n)보다 느릴 수있을 때 O (1)을 맹목적으로 고수하는 이유가 무엇인지 궁금합니다. 나는 심지어 미친 10 ^ 8 강도 목록을 위해 그것을 테스트했고 내 것은 여전히 ​​더 빠른 것처럼 보였다. 난 당신이 10 ^ 12 개 항목을 저장할 수도 표준 콜렉션 유형 .. 거기 알고 희망
nawfal

1
@nawfal-자세한 분석을 해주셔서 감사합니다. 일반적으로 연결된 목록은 효율적인 최종 삽입으로 알려져 있으므로 여기에서 선택했습니다. 그러나 나는 그것을 벤치마킹했고 실제로 List <>가 훨씬 빠릅니다. 나는 이것이 일종의 .NET 구현 세부 사항이며 아마도 별도의 StackOverflow 질문이 필요하다고 생각합니다. 귀하의 제안에 따라 List <>를 사용하도록 내 대답을 수정했습니다. 목록 용량을 사전 할당하면 최종 삽입이 여전히 O (1)이고 원래 디자인 목표를 충족합니다. 또한 .NET 4.5에서 기본 제공 .AsReadOnly ()로 전환했습니다.
Mike

16

이것은 받아 들여지는 대답과 동일하지만 훨씬 더 간단한 표현입니다.

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

위의 방법은을 IEnumerable<T>크기가 같거나 크기가 비슷한 N 개의 청크로 분할 합니다.

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

위의 방법은를 IEnumerable<T>원하는 고정 크기의 청크로 분할하고 총 청크 수는 중요하지 않습니다. 이것은 질문의 내용이 아닙니다.

Split방법 의 문제점 은 속도가 느리다는 것 외에도 그룹화가 각 위치에 대해 N의 i 배수를 기반으로 수행된다는 의미에서 출력을 스크램블하거나 즉, 청크를 얻지 못한다는 것입니다. 원래 순서대로.

여기에있는 거의 모든 대답은 순서를 유지하지 않거나 분할하지 않고 분할하는 것에 관한 것이거나 명백히 잘못된 것입니다. 더 빠르고, 순서는 유지하지만 좀 더 장황한 방법을 시도해보십시오.

public static IEnumerable<IEnumerable<T>> Split<T>(this ICollection<T> items, 
                                                   int numberOfChunks)
{
    if (numberOfChunks <= 0 || numberOfChunks > items.Count)
        throw new ArgumentOutOfRangeException("numberOfChunks");

    int sizePerPacket = items.Count / numberOfChunks;
    int extra = items.Count % numberOfChunks;

    for (int i = 0; i < numberOfChunks - extra; i++)
        yield return items.Skip(i * sizePerPacket).Take(sizePerPacket);

    int alreadyReturnedCount = (numberOfChunks - extra) * sizePerPacket;
    int toReturnCount = extra == 0 ? 0 : (items.Count - numberOfChunks) / extra + 1;
    for (int i = 0; i < extra; i++)
        yield return items.Skip(alreadyReturnedCount + i * toReturnCount).Take(toReturnCount);
}

여기 에서 Partition작업에 해당하는 방법


6

이전에 게시 한 파티션 기능을 자주 사용하고 있습니다. 그것의 유일한 나쁜 점은 완전히 스트리밍되지 않는다는 것입니다. 시퀀스에서 몇 가지 요소로 작업하는 경우 문제가되지 않습니다. 시퀀스에서 10,000 개 이상의 요소로 작업을 시작했을 때 새로운 솔루션이 필요했습니다.

다음 솔루션은 훨씬 더 복잡하지만 (더 많은 코드!) 매우 효율적입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace LuvDaSun.Linq
{
    public static class EnumerableExtensions
    {
        public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int partitionSize)
        {
            /*
            return enumerable
                .Select((item, index) => new { Item = item, Index = index, })
                .GroupBy(item => item.Index / partitionSize)
                .Select(group => group.Select(item => item.Item)                )
                ;
            */

            return new PartitioningEnumerable<T>(enumerable, partitionSize);
        }

    }


    class PartitioningEnumerable<T> : IEnumerable<IEnumerable<T>>
    {
        IEnumerable<T> _enumerable;
        int _partitionSize;
        public PartitioningEnumerable(IEnumerable<T> enumerable, int partitionSize)
        {
            _enumerable = enumerable;
            _partitionSize = partitionSize;
        }

        public IEnumerator<IEnumerable<T>> GetEnumerator()
        {
            return new PartitioningEnumerator<T>(_enumerable.GetEnumerator(), _partitionSize);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }


    class PartitioningEnumerator<T> : IEnumerator<IEnumerable<T>>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitioningEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
            _enumerator.Dispose();
        }

        IEnumerable<T> _current;
        public IEnumerable<T> Current
        {
            get { return _current; }
        }
        object IEnumerator.Current
        {
            get { return _current; }
        }

        public void Reset()
        {
            _current = null;
            _enumerator.Reset();
        }

        public bool MoveNext()
        {
            bool result;

            if (_enumerator.MoveNext())
            {
                _current = new PartitionEnumerable<T>(_enumerator, _partitionSize);
                result = true;
            }
            else
            {
                _current = null;
                result = false;
            }

            return result;
        }

    }



    class PartitionEnumerable<T> : IEnumerable<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        public PartitionEnumerable(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new PartitionEnumerator<T>(_enumerator, _partitionSize);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }


    class PartitionEnumerator<T> : IEnumerator<T>
    {
        IEnumerator<T> _enumerator;
        int _partitionSize;
        int _count;
        public PartitionEnumerator(IEnumerator<T> enumerator, int partitionSize)
        {
            _enumerator = enumerator;
            _partitionSize = partitionSize;
        }

        public void Dispose()
        {
        }

        public T Current
        {
            get { return _enumerator.Current; }
        }
        object IEnumerator.Current
        {
            get { return _enumerator.Current; }
        }
        public void Reset()
        {
            if (_count > 0) throw new InvalidOperationException();
        }

        public bool MoveNext()
        {
            bool result;

            if (_count < _partitionSize)
            {
                if (_count > 0)
                {
                    result = _enumerator.MoveNext();
                }
                else
                {
                    result = true;
                }
                _count++;
            }
            else
            {
                result = false;
            }

            return result;
        }

    }
}

즐겨!


이 버전은 IEnumerator의 계약을 위반합니다. Reset이 호출 될 때 InvalidOperationException을 throw하는 것은 유효하지 않습니다. 많은 LINQ 확장 메서드가이 동작에 의존한다고 생각합니다.
ShadowChaser

1
@ShadowChaser 나는 Reset ()이 NotSupportedException을 던져야한다고 생각하며 모든 것이 잘 될 것입니다. MSDN 문서에서 "Reset 메서드는 COM 상호 운용성을 위해 제공됩니다. 반드시 구현할 필요는 없습니다. 대신 구현자는 단순히 NotSupportedException을 throw 할 수 있습니다."
toong

@toong 와우, 맞아요. 이 모든 시간이 지난 후에도 어떻게 놓쳤는 지 잘 모르겠습니다.
ShadowChaser 2013 년

유모차입니다! 나는 정확히 기억하지 못하지만 (내가 기억하는 한) 원하지 않는 단계를 수행하고 추악한 부작용으로 이어질 수 있습니다 (예 : datareader 사용). 최고의 솔루션은 여기에 있습니다 (Jeppe Stig Nielsen) : stackoverflow.com/questions/13709626/…
SalientBrain

4

흥미로운 스레드. Split / Partition의 스트리밍 버전을 얻으려면 열거자를 사용하고 확장 메서드를 사용하여 열거 자에서 시퀀스를 생성 할 수 있습니다. yield를 사용하여 명령형 코드를 기능 코드로 변환하는 것은 실제로 매우 강력한 기술입니다.

먼저 요소 수를 지연 시퀀스로 바꾸는 열거 자 확장 :

public static IEnumerable<T> TakeFromCurrent<T>(this IEnumerator<T> enumerator, int count)
{
    while (count > 0)
    {
        yield return enumerator.Current;
        if (--count > 0 && !enumerator.MoveNext()) yield break;
    }
}

그리고 시퀀스를 분할하는 열거 가능한 확장 :

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> seq, int partitionSize)
{
    var enumerator = seq.GetEnumerator();

    while (enumerator.MoveNext())
    {
        yield return enumerator.TakeFromCurrent(partitionSize);
    }
}

최종 결과는 매우 간단한 코드에 의존하는 매우 효율적이고 스트리밍 및 지연 구현입니다.

즐겨!


처음에는 같은 것을 프로그래밍했지만 중첩 된 IEnumerable <T> 인스턴스 중 하나에서 Reset이 호출되면 패턴이 중단됩니다.
ShadowChaser

1
내부 열거 형이 아닌 파티션 만 열거하는 경우에도 여전히 작동합니까? 내부 열거자가 지연되기 때문에 내부 (현재에서 가져 오기)에 대한 코드는 열거 될 때까지 실행되지 않으므로 movenext ()는 외부 파티션 함수에 의해서만 호출됩니다. 내 가정이 사실이라면 이것은 잠재적으로 원래 열거 형에 n 개의 요소가있는 n 개의 파티션을 생성 할 수 있으며 내부 열거 형은 예상치 못한 결과를 생성 할 것입니다
Brad

@Brad 예상대로 "실패"합니다.이 스레드의 일부 문제와 유사합니다. stackoverflow.com/questions/419019/… (특히 stackoverflow.com/a/20953521/1037948 )
drzaus

4

나는 이것을 사용한다 :

public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> instance, int partitionSize)
{
    return instance
        .Select((value, index) => new { Index = index, Value = value })
        .GroupBy(i => i.Index / partitionSize)
        .Select(i => i.Select(i2 => i2.Value));
}

이유를 설명 해주세요. 이 기능을 아무 문제없이 사용하고 있습니다!
엘머

다시 질문을 읽고 당신이 얻을 N 있는지 (거의) 함수와 동일한 길이 부품
무하마드 하산 칸

@Elmer 귀하의 대답은 옳지 만 질문이 잘못되었습니다. 귀하의 답변은 각 청크에 대해 고정 된 크기의 알 수없는 청크 수를 제공합니다 (정확히 파티션, 사용자가 지정한 이름). 그러나 OP는 청크 당 크기에 상관없이 고정 된 수의 청크를 제공하는 분할 기능을 원합니다 (동일한 크기에 가깝거나 같음). 아마 자세한 내용은 여기 적합 stackoverflow.com/questions/3773403/...
nawfal

i.Index / partitionSize를 i.Index % partitionSize로 변경하고 요청한 결과를 얻을 수 있다고 생각합니다. 나는 또한 더 간결하고 읽기 쉽기 때문에 받아 들여지는 대답보다 이것을 선호합니다.
제이크 드류

2

이것은 메모리 효율적이며 가능한 한 (배치 당) 실행을 연기하고 선형 시간 O (n)에서 작동합니다.

    public static IEnumerable<IEnumerable<T>> InBatchesOf<T>(this IEnumerable<T> items, int batchSize)
    {
        List<T> batch = new List<T>(batchSize);
        foreach (var item in items)
        {
            batch.Add(item);

            if (batch.Count >= batchSize)
            {
                yield return batch;
                batch = new List<T>();
            }
        }

        if (batch.Count != 0)
        {
            //can't be batch size or would've yielded above
            batch.TrimExcess();
            yield return batch;
        }
    }

2

이 질문 (및 그 사촌들)에 대한 훌륭한 답변이 많이 있습니다. 필자는 이것을 직접 필요로했고 소스 컬렉션을 목록으로 처리 할 수있는 시나리오에서 효율적이고 오류를 허용하도록 설계된 솔루션을 만들었습니다. 지연 반복을 사용하지 않으므로 메모리 압력을 가할 수있는 알 수없는 크기의 컬렉션에는 적합하지 않을 수 있습니다.

static public IList<T[]> GetChunks<T>(this IEnumerable<T> source, int batchsize)
{
    IList<T[]> result = null;
    if (source != null && batchsize > 0)
    {
        var list = source as List<T> ?? source.ToList();
        if (list.Count > 0)
        {
            result = new List<T[]>();
            for (var index = 0; index < list.Count; index += batchsize)
            {
                var rangesize = Math.Min(batchsize, list.Count - index);
                result.Add(list.GetRange(index, rangesize).ToArray());
            }
        }
    }
    return result ?? Enumerable.Empty<T[]>().ToList();
}

static public void TestGetChunks()
{
    var ids = Enumerable.Range(1, 163).Select(i => i.ToString());
    foreach (var chunk in ids.GetChunks(20))
    {
        Console.WriteLine("[{0}]", String.Join(",", chunk));
    }
}

GetRange 및 Math.Min을 사용하는이 질문 군에서 몇 가지 답변을 보았습니다. 그러나 전반적으로 이것이 오류 검사 및 효율성 측면에서 더 완벽한 솔루션이라고 생각합니다.


1
   protected List<List<int>> MySplit(int MaxNumber, int Divider)
        {
            List<List<int>> lst = new List<List<int>>();
            int ListCount = 0;
            int d = MaxNumber / Divider;
            lst.Add(new List<int>());
            for (int i = 1; i <= MaxNumber; i++)
            {
                lst[ListCount].Add(i);
                if (i != 0 && i % d == 0)
                {
                    ListCount++;
                    d += MaxNumber / Divider;
                    lst.Add(new List<int>());
                }
            }
            return lst;
        }

1

Great Answers, 내 시나리오에 대해 수락 된 답변을 테스트했는데 순서가 유지되지 않는 것 같습니다. 질서를 유지하는 Nawfal의 훌륭한 답변도 있습니다. 그러나 내 시나리오에서는 나머지를 정규화 된 방식으로 나누고 싶었습니다. 내가 본 모든 답변은 나머지 또는 시작 또는 끝에 퍼졌습니다.

내 대답은 또한 나머지가 더 정규화 된 방식으로 확산됩니다.

 static class Program
{          
    static void Main(string[] args)
    {
        var input = new List<String>();
        for (int k = 0; k < 18; ++k)
        {
            input.Add(k.ToString());
        }
        var result = splitListIntoSmallerLists(input, 15);            
        int i = 0;
        foreach(var resul in result){
            Console.WriteLine("------Segment:" + i.ToString() + "--------");
            foreach(var res in resul){
                Console.WriteLine(res);
            }
            i++;
        }
        Console.ReadLine();
    }

    private static List<List<T>> splitListIntoSmallerLists<T>(List<T> i_bigList,int i_numberOfSmallerLists)
    {
        if (i_numberOfSmallerLists <= 0)
            throw new ArgumentOutOfRangeException("Illegal value of numberOfSmallLists");

        int normalizedSpreadRemainderCounter = 0;
        int normalizedSpreadNumber = 0;
        //e.g 7 /5 > 0 ==> output size is 5 , 2 /5 < 0 ==> output is 2          
        int minimumNumberOfPartsInEachSmallerList = i_bigList.Count / i_numberOfSmallerLists;                        
        int remainder = i_bigList.Count % i_numberOfSmallerLists;
        int outputSize = minimumNumberOfPartsInEachSmallerList > 0 ? i_numberOfSmallerLists : remainder;
        //In case remainder > 0 we want to spread the remainder equally between the others         
        if (remainder > 0)
        {
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                normalizedSpreadNumber = (int)Math.Floor((double)i_numberOfSmallerLists / remainder);    
            }
            else
            {
                normalizedSpreadNumber = 1;
            }   
        }
        List<List<T>> retVal = new List<List<T>>(outputSize);
        int inputIndex = 0;            
        for (int i = 0; i < outputSize; ++i)
        {
            retVal.Add(new List<T>());
            if (minimumNumberOfPartsInEachSmallerList > 0)
            {
                retVal[i].AddRange(i_bigList.GetRange(inputIndex, minimumNumberOfPartsInEachSmallerList));
                inputIndex += minimumNumberOfPartsInEachSmallerList;
            }
            //If we have remainder take one from it, if our counter is equal to normalizedSpreadNumber.
            if (remainder > 0)
            {
                if (normalizedSpreadRemainderCounter == normalizedSpreadNumber-1)
                {
                    retVal[i].Add(i_bigList[inputIndex]);
                    remainder--;
                    inputIndex++;
                    normalizedSpreadRemainderCounter=0;
                }
                else
                {
                    normalizedSpreadRemainderCounter++;
                }
            }
        }
        return retVal;
    }      

}

0

이 부분의 순서가 그다지 중요하지 않은 경우 다음을 시도해 볼 수 있습니다.

int[] array = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int n = 3;

var result =
   array.Select((value, index) => new { Value = value, Index = index }).GroupBy(i => i.Index % n, i => i.Value);

// or
var result2 =
   from i in array.Select((value, index) => new { Value = value, Index = index })
   group i.Value by i.Index % n into g
   select g;

그러나 이들은 어떤 이유로 IEnumerable <IEnumerable <int >>로 캐스팅 될 수 없습니다.


할 수 있습니다. 대신 직접 주물 단지 일반적인 기능을 다음 INT 배열에 대한 호출
nawfal

0

이것은 멋지고 짧은 내 코드입니다.

 <Extension()> Public Function Chunk(Of T)(ByVal this As IList(Of T), ByVal size As Integer) As List(Of List(Of T))
     Dim result As New List(Of List(Of T))
     For i = 0 To CInt(Math.Ceiling(this.Count / size)) - 1
         result.Add(New List(Of T)(this.GetRange(i * size, Math.Min(size, this.Count - (i * size)))))
     Next
     Return result
 End Function

0

이것은 내 방식으로 항목을 나열하고 열별로 행을 나눕니다.

  int repat_count=4;

  arrItems.ForEach((x, i) => {
    if (i % repat_count == 0) 
        row = tbo.NewElement(el_tr, cls_min_height);
    var td = row.NewElement(el_td);
    td.innerHTML = x.Name;
  });

0

나는 문자열이있는 것과 같은 분할을 찾고 있었으므로 전체 목록이 첫 번째 부분뿐만 아니라 일부 규칙에 따라 분할됩니다. 이것이 내 솔루션입니다.

List<int> sequence = new List<int>();
for (int i = 0; i < 2000; i++)
{
     sequence.Add(i);
}
int splitIndex = 900;
List<List<int>> splitted = new List<List<int>>();
while (sequence.Count != 0)
{
    splitted.Add(sequence.Take(splitIndex).ToList() );
    sequence.RemoveRange(0, Math.Min(splitIndex, sequence.Count));
}

다음 번 시도 : var nrs = Enumerable.Range (1,2000) .ToList ();
MBoros

0

다음은 부품 수 대신 항목 수를 약간 조정 한 것입니다.

public static class MiscExctensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> list, int nbItems)
    {
        return (
            list
            .Select((o, n) => new { o, n })
            .GroupBy(g => (int)(g.n / nbItems))
            .Select(g => g.Select(x => x.o))
        );
    }
}

-1
int[] items = new int[] { 0,1,2,3,4,5,6,7,8,9, 10 };

int itemIndex = 0;
int groupSize = 2;
int nextGroup = groupSize;

var seqItems = from aItem in items
               group aItem by 
                            (itemIndex++ < nextGroup) 
                            ? 
                            nextGroup / groupSize
                            :
                            (nextGroup += groupSize) / groupSize
                            into itemGroup
               select itemGroup.AsEnumerable();

-1

방금이 스레드를 발견했으며 여기에있는 대부분의 솔루션에는 컬렉션에 항목을 추가하고 각 페이지를 반환하기 전에 효과적으로 구체화하는 것이 포함됩니다. 이것은 두 가지 이유로 좋지 않습니다. 첫째로 페이지가 크면 페이지를 채우는 데 메모리 오버 헤드가 있고, 둘째로 다음 레코드로 진행할 때 이전 레코드를 무효화하는 반복기가 있습니다 (예 : 열거 자 메서드 내에서 DataReader를 래핑하는 경우). .

이 솔루션은 항목을 임시 컬렉션에 캐시 할 필요가 없도록 두 개의 중첩 된 열거 자 메서드를 사용합니다. 외부 및 내부 반복자는 동일한 열거 형을 순회하므로 반드시 동일한 열거자를 공유하므로 현재 페이지 처리를 완료 할 때까지 외부 반복기를 진행하지 않는 것이 중요합니다. 즉, 현재 페이지 전체를 반복하지 않기로 결정한 경우 다음 페이지로 이동하면이 솔루션이 자동으로 페이지 경계까지 반복됩니다.

using System.Collections.Generic;

public static class EnumerableExtensions
{
    /// <summary>
    /// Partitions an enumerable into individual pages of a specified size, still scanning the source enumerable just once
    /// </summary>
    /// <typeparam name="T">The element type</typeparam>
    /// <param name="enumerable">The source enumerable</param>
    /// <param name="pageSize">The number of elements to return in each page</param>
    /// <returns></returns>
    public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> enumerable, int pageSize)
    {
        var enumerator = enumerable.GetEnumerator();

        while (enumerator.MoveNext())
        {
            var indexWithinPage = new IntByRef { Value = 0 };

            yield return SubPartition(enumerator, pageSize, indexWithinPage);

            // Continue iterating through any remaining items in the page, to align with the start of the next page
            for (; indexWithinPage.Value < pageSize; indexWithinPage.Value++)
            {
                if (!enumerator.MoveNext())
                {
                    yield break;
                }
            }
        }
    }

    private static IEnumerable<T> SubPartition<T>(IEnumerator<T> enumerator, int pageSize, IntByRef index)
    {
        for (; index.Value < pageSize; index.Value++)
        {
            yield return enumerator.Current;

            if (!enumerator.MoveNext())
            {
                yield break;
            }
        }
    }

    private class IntByRef
    {
        public int Value { get; set; }
    }
}

이것은 전혀 작동하지 않습니다! 가장 좋은 방법은 여기 stackoverflow.com/questions/13709626/…입니다 ! 주석을 참조하십시오.
SalientBrain 2014-08-26
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.