Chia bộ sưu tập thành các phần `n` bằng LINQ?


122

Có cách nào hay để chia bộ sưu tập thành nnhiều phần với LINQ không? Tất nhiên không nhất thiết phải đồng đều.

Đó là, tôi muốn chia bộ sưu tập thành các tập hợp con, mỗi tập hợp chứa một tập hợp con của các phần tử, nơi tập hợp cuối cùng có thể bị xáo trộn.


1
Được gắn thẻ lại: Câu hỏi không liên quan gì đến asp.net. Vui lòng gắn thẻ câu hỏi của bạn một cách thích hợp.

Chính xác thì bạn muốn chúng phân chia như thế nào, nếu không (tất nhiên là cho phép kết thúc)?
Marc Gravell

1
ai đã liên kết với câu hỏi này? john có phải là bạn? :-) đột nhiên tất cả những câu trả lời này :-)
Simon_Weaver


@Simon_Weaver Tôi đã cố gắng làm rõ những gì bạn đang hỏi dựa trên câu trả lời được chấp nhận. Trên thực tế, có nhiều cách để 'tách' một danh sách, bao gồm việc phân tách từng phần tử của danh sách thành các phần tử của nó và đưa chúng vào cái gọi là danh sách 'song song'.
jpaugh

Câu trả lời:


127

Một linq thuần túy và giải pháp đơn giản nhất như hình dưới đây.

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
Bạn có thể làm: chọn part.AsEnumerable () thay vì chọn part (IEnumerable <T>). Nó cảm thấy thanh lịch hơn.
tuinstoel

2
Thực hiện tất cả các hoạt động mô-đun đó có thể tốn kém một chút trong danh sách dài.
Jonathan Allen

8
Sẽ tốt hơn nếu sử dụng quá tải Chọn bao gồm chỉ mục.
Marc Gravell

1
Tôi đã thêm một phản ứng có sử dụng các lựa chọn quá tải và phương pháp chaining cú pháp
reustmd

1
.AsEnumerable()là không cần thiết, IGrouping <T> đã là một IEnumerable <T>.
Alex,

58

CHỈNH SỬA: Được rồi, có vẻ như tôi đã đọc sai câu hỏi. Tôi đọc nó là "miếng có độ dài n" chứ không phải "n miếng". Doh! Đang cân nhắc xóa câu trả lời ...

(Câu trả lời gốc)

Tôi không tin rằng có một cách phân vùng tích hợp, mặc dù tôi dự định viết một cách trong tập hợp các bổ sung của mình cho LINQ vào Đối tượng. Marc Gravell có một triển khai ở đây mặc dù tôi có thể sẽ sửa đổi nó để trả về chế độ xem chỉ đọc:

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

Darn - đánh bại tôi với nó ;-p
Marc Gravell

3
Bạn thực sự không thích những "mảng [đếm ++]", eh ;-p
Marc Gravell

18
Cảm ơn bạn đã không xóa mặc dù nó không phải là câu trả lời cho OP, tôi muốn điều tương tự chính xác - mảnh có độ dài n :).
Gishu

2
@Dejan: Không, không. Lưu ý việc sử dụng yield return. Nó yêu cầu một loạt trong bộ nhớ tại một thời điểm, nhưng đó là tất cả.
Jon Skeet

1
@Dejan: Đúng vậy - tôi sẽ không như đoán về cách nó tương tác với phân vùng LINQ Parallel, phải trung thực :)
Jon Skeet

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
Tôi không thích Linq kiểu SQL một cách vô lý, vì vậy đây là câu trả lời yêu thích của tôi.
piedar

1
@ manu08, tôi đã thử mã ur, tôi có một danh sách var dept = {1,2,3,4,5}. Sau khi tách kết quả như thế nào dept1 = {1,3,5}dept2 = { 2,4 }ở đâu parts = 2. Nhưng kết quả tôi cần là dept1 = {1,2,3}dept2 = {4,5}
Karthik Arthik

3
Tôi gặp vấn đề tương tự với modulo, vì vậy tôi đã tính toán chiều dài cột với int columnLength = (int)Math.Ceiling((decimal)(list.Count()) / parts);sau đó thực hiện phép chia với .GroupBy(x => x.index / columnLength). Một nhược điểm là Count () liệt kê danh sách.
goodeye

24

Được rồi, tôi sẽ ném chiếc mũ của mình vào sàn đấu. Ưu điểm của thuật toán của tôi:

  1. Không có toán tử nhân, chia hoặc mô đun đắt tiền
  2. Tất cả các hoạt động là O (1) (xem ghi chú bên dưới)
  3. Hoạt động cho nguồn IEnumerable <> (không cần thuộc tính Count)
  4. Đơn giản

Mật mã:

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

Như đã chỉ ra trong các nhận xét dưới đây, cách tiếp cận này không thực sự giải quyết câu hỏi ban đầu yêu cầu một số phần cố định có độ dài xấp xỉ bằng nhau. Điều đó nói rằng, bạn vẫn có thể sử dụng cách tiếp cận của tôi để giải quyết câu hỏi ban đầu bằng cách gọi nó theo cách này:

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

Khi được sử dụng theo cách này, cách tiếp cận không còn là O (1) nữa vì phép toán Count () là O (N).


Brilliant - giải pháp tốt nhất ở đây! Một số tối ưu hóa: * Xóa danh sách liên kết thay vì tạo một danh sách mới cho mỗi phần. Tham chiếu đến danh sách được liên kết không bao giờ được trả lại cho người gọi, vì vậy nó hoàn toàn an toàn. * Đừng tạo danh sách được liên kết cho đến khi bạn đạt được mục đầu tiên - theo cách đó sẽ không có phân bổ nếu nguồn trống
ShadowChaser

3
@ShadowChaser Theo MSDN, việc xóa LinkedList là độ phức tạp O (N) nên nó sẽ làm hỏng mục tiêu O (1) của tôi. Tất nhiên, bạn có thể tranh luận rằng foreach là O (N) để bắt đầu bằng ... :)
Mike

4
câu trả lời của bạn là đúng, nhưng câu hỏi là sai đối với nó. Câu trả lời của bạn đưa ra số lượng khối chưa biết với kích thước cố định cho mỗi đoạn. Nhưng OP muốn có chức năng Split, nơi nó cung cấp số lượng cố định với bất kỳ kích thước nào trên mỗi đoạn (hy vọng là bằng hoặc gần với kích thước bằng nhau). Có lẽ phù hợp hơn ở đây stackoverflow.com/questions/3773403/…
nawfal

1
@Mike bạn đã benchmark nó chưa? Tôi hy vọng bạn biết O (1) không có nghĩa là nhanh hơn, nó chỉ có nghĩa là thời gian cần thiết để phân vùng không tăng. Tôi chỉ tự hỏi lý do nào để bạn mù quáng bám vào O (1) khi nó có thể chậm hơn các O (n) khác trong tất cả các tình huống trong cuộc sống thực. Tôi thậm chí đã thử nghiệm nó cho một danh sách sức mạnh 10 ^ 8 điên rồ và của tôi dường như vẫn nhanh hơn. Tôi hy vọng bạn biết không có thậm chí loại bộ sưu tập tiêu chuẩn có thể chứa 10 ^ 12 mặt hàng ..
nawfal

1
@nawfal - Cảm ơn bạn đã phân tích chi tiết, nó giúp tôi luôn cố gắng. Các danh sách được liên kết nói chung được biết đến với việc chèn cuối hiệu quả, đó là lý do tại sao tôi chọn nó ở đây. Tuy nhiên tôi chỉ đánh giá nó và thực sự là Danh sách <> nhanh hơn nhiều. Tôi nghi ngờ đây là một số loại chi tiết triển khai .NET, có lẽ xứng đáng với một câu hỏi StackOverflow riêng biệt. Tôi đã sửa đổi câu trả lời của mình để sử dụng Danh sách <> theo đề xuất của bạn. Việc phân bổ trước dung lượng danh sách đảm bảo rằng phần chèn cuối vẫn là O (1) và đáp ứng mục tiêu thiết kế ban đầu của tôi. Tôi cũng đã chuyển sang .AsReadOnly () tích hợp trong .NET 4.5.
Mike

16

Đây giống như câu trả lời được chấp nhận, nhưng một cách biểu diễn đơn giản hơn nhiều:

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

Phương pháp trên chia một IEnumerable<T>thành N số khối có kích thước bằng nhau hoặc gần bằng kích thước bằng nhau.

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

Phương pháp trên chia một IEnumerable<T>phần thành các phần có kích thước cố định mong muốn với tổng số phần là không quan trọng - đó không phải là câu hỏi.

Vấn đề với Splitphương pháp, bên cạnh việc chậm hơn, là nó xáo trộn đầu ra theo nghĩa là việc nhóm sẽ được thực hiện trên cơ sở bội số thứ của N cho mỗi vị trí, hay nói cách khác là bạn không nhận được các phần theo thứ tự ban đầu.

Hầu hết mọi câu trả lời ở đây hoặc không duy trì thứ tự, hoặc về phân vùng và không tách, hoặc rõ ràng là sai. Hãy thử cách này nhanh hơn, giữ được thứ tự nhưng dài dòng hơn:

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

Phương pháp tương đương cho một Partitionhoạt động ở đây


6

Tôi đã sử dụng chức năng Phân vùng mà tôi đã đăng trước đó khá thường xuyên. Điều tồi tệ duy nhất về nó là không hoàn toàn phát trực tuyến. Đây không phải là vấn đề nếu bạn làm việc với ít phần tử trong trình tự của mình. Tôi cần một giải pháp mới khi bắt đầu làm việc với hơn 100.000 phần tử trong chuỗi của mình.

Giải pháp sau phức tạp hơn rất nhiều (và nhiều mã hơn!), Nhưng nó rất hiệu quả.

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

    }
}

Thưởng thức!


Phiên bản này phá vỡ hợp đồng của IEnumerator. Không hợp lệ để ném InvalidOperationException khi Đặt lại được gọi - Tôi tin rằng nhiều phương thức mở rộng LINQ dựa trên hành vi này.
ShadowChaser

1
@ShadowChaser Tôi nghĩ Reset () nên ném NotSupportedException và mọi thứ sẽ ổn. Từ tài liệu MSDN: "Phương thức Đặt lại được cung cấp cho khả năng tương tác COM. Nó không nhất thiết phải được triển khai; thay vào đó, người triển khai có thể chỉ cần ném NotSupportedException."
toong

@toong Wow, bạn nói đúng. Không chắc làm thế nào tôi đã bỏ lỡ điều đó sau tất cả thời gian.
ShadowChaser

Nó là lỗi! Tôi không nhớ chính xác, nhưng (theo như tôi nhớ) nó thực hiện bước không mong muốn và nó có thể dẫn đến các tác dụng phụ xấu xí (với datareader cho ví dụ.). Giải pháp tốt nhất là ở đây (Jeppe Stig Nielsen): stackoverflow.com/questions/13709626/…
SalientBrain,

4

Chủ đề thú vị. Để có được phiên bản phân chia / phân vùng trực tuyến, người ta có thể sử dụng các điều tra viên và trình tự thu được từ điều tra viên bằng các phương pháp mở rộng. Chuyển đổi mã mệnh lệnh thành mã chức năng sử dụng năng suất thực sự là một kỹ thuật rất mạnh mẽ.

Đầu tiên, một tiện ích mở rộng điều tra viên biến số phần tử thành một chuỗi lười biếng:

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

Và sau đó là một tiện ích mở rộng có thể liệt kê phân vùng một chuỗi:

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

Kết quả cuối cùng là một triển khai hiệu quả cao, phát trực tuyến và lười biếng dựa trên mã rất đơn giản.

Thưởng thức!


Ban đầu, tôi đã lập trình tương tự, nhưng mẫu bị hỏng khi Đặt lại được gọi trên một trong các trường hợp IEnumerable <T> lồng nhau.
ShadowChaser

1
Điều này vẫn hoạt động nếu bạn chỉ liệt kê phân vùng và không liệt kê bên trong? vì trình kê khai bên trong được hoãn lại nên không có mã nào cho nội dung (lấy từ hiện tại) sẽ thực thi cho đến khi nó được liệt kê do đó movenext () sẽ chỉ được gọi bởi hàm phân vùng bên ngoài, phải không? Nếu giả định của tôi là đúng sự thật thì đây có khả năng có thể mang lại n phân vùng với các yếu tố n trong đếm được gốc và enumerables bên trong sẽ mang lại kết quả bất ngờ
Brad

@Brad nó sẽ "fail" như bạn mong đợi, tương tự như một số vấn đề trong chuỗi này stackoverflow.com/questions/419019/… (cụ thể là stackoverflow.com/a/20953521/1037948 )
drzaus

4

Tôi sử dụng cái này:

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

Hãy giải thích tại sao. Tôi đã sử dụng chức năng này mà không gặp bất kỳ rắc rối nào!
Elmer

đọc những câu hỏi một lần nữa và xem nếu bạn nhận được n (gần như) phần chiều dài bằng nhau với chức năng của bạn
Muhammad Hasan Khan

@Elmer câu trả lời của bạn là đúng, nhưng câu hỏi là sai. Câu trả lời của bạn cung cấp số lượng khối không xác định với kích thước cố định cho mỗi đoạn (chính xác là Phân vùng, tên bạn đã đặt cho nó). Nhưng OP muốn có chức năng Split, nơi nó cung cấp số lượng cố định với bất kỳ kích thước nào trên mỗi đoạn (hy vọng là bằng hoặc gần với kích thước bằng nhau). Có lẽ phù hợp hơn ở đây stackoverflow.com/questions/3773403/…
nawfal

Tôi nghĩ bạn chỉ có thể thay đổi i.Index / partitionSize thành i.Index% partitionSize và nhận được kết quả được yêu cầu. Tôi cũng thích điều này hơn câu trả lời được chấp nhận vì nó nhỏ gọn và dễ đọc hơn.
Jake Drew

2

Điều này là hiệu quả về bộ nhớ và hạn chế việc thực thi càng nhiều càng tốt (mỗi đợt) và hoạt động trong thời gian tuyến tính 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

Có rất nhiều câu trả lời tuyệt vời cho câu hỏi này (và những người anh em họ của nó). Bản thân tôi cần điều này và đã tạo ra một giải pháp được thiết kế để hiệu quả và có khả năng chịu lỗi trong một trường hợp mà tập hợp nguồn có thể được coi như một danh sách. Nó không sử dụng bất kỳ phép lặp lười nào nên có thể không phù hợp với các bộ sưu tập có kích thước không xác định có thể gây áp lực bộ nhớ.

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

Tôi đã thấy một số câu trả lời trong nhóm câu hỏi này sử dụng GetRange và Math.Min. Nhưng tôi tin rằng về tổng thể đây là một giải pháp hoàn thiện hơn về mặt kiểm tra lỗi và hiệu quả.


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

Câu trả lời tuyệt vời, đối với kịch bản của tôi, tôi đã thử nghiệm câu trả lời được chấp nhận và có vẻ như nó không giữ trật tự. cũng có câu trả lời tuyệt vời của Nawfal giúp giữ trật tự. Nhưng trong kịch bản của tôi, tôi muốn chia phần còn lại theo cách chuẩn hóa, tất cả các câu trả lời tôi thấy đều trải đều phần còn lại hoặc ở đầu hoặc ở cuối.

Câu trả lời của tôi cũng đưa phần còn lại lan truyền theo cách bình thường hóa hơn.

 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

Nếu thứ tự trong các phần này không quan trọng lắm, bạn có thể thử cách này:

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;

Tuy nhiên, chúng không thể được truyền tới IEnumerable <IEnumerable <int>> bởi một số lý do ...


Nó có thể được thực hiện. Thay vì trực tiếp đúc chỉ cần thực hiện chức năng chung và sau đó gọi nó cho int mảng của bạn
nawfal

0

Đây là mã của tôi, đẹp và ngắn gọn.

 <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

Đây là cách của tôi, liệt kê các mục và ngắt hàng theo cột

  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

Tôi đang tìm kiếm một phần tách giống như phần có chuỗi, vì vậy toàn bộ Danh sách được chia theo một số quy tắc, không chỉ phần đầu tiên, đây là giải pháp của tôi

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

Lần sau hãy thử: var nrs = Enumerable.Range (1,2000) .ToList ();
MBoros

0

Đây là một chút điều chỉnh cho số lượng mục thay vì số bộ phận:

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

Chỉ cần xem qua chủ đề này và hầu hết các giải pháp ở đây liên quan đến việc thêm các mục vào bộ sưu tập, hiệu quả hóa từng trang trước khi trả lại. Điều này là không tốt vì hai lý do - thứ nhất nếu các trang của bạn lớn thì có bộ nhớ để lấp đầy trang, thứ hai là có các trình vòng lặp làm mất hiệu lực các bản ghi trước đó khi bạn chuyển sang bản tiếp theo (ví dụ: nếu bạn bọc một DataReader trong một phương thức liệt kê) .

Giải pháp này sử dụng hai phương pháp liệt kê lồng nhau để tránh bất kỳ nhu cầu nào phải lưu trữ các mục vào bộ sưu tập tạm thời. Vì các trình lặp bên ngoài và bên trong đang duyệt qua cùng một bảng liệt kê, chúng nhất thiết phải chia sẻ cùng một bảng liệt kê, vì vậy điều quan trọng là không nâng cấp trình vòng bên ngoài cho đến khi bạn xử lý xong trang hiện tại. Điều đó có nghĩa là, nếu bạn quyết định không lặp lại toàn bộ trang hiện tại, khi bạn chuyển sang trang tiếp theo, giải pháp này sẽ tự động lặp lại tới ranh giới trang.

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

Điều này không hoạt động ở tất cả! Tốt nhất có thể là ở đây stackoverflow.com/questions/13709626/… ! Xem ý kiến.
SalientBrain
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.