C# - 分区列表的优雅方式?

Dav*_*son 35 c# list data-partitioning

我想通过指定每个分区中的元素数量将列表分区为列表列表.

例如,假设我有列表{1,2,... 11},并且想要对其进行分区,使得每个集合具有4个元素,最后一个集合尽可能多地填充元素.生成的分区看起来像{{1..4},{5..8},{9..11}}

写这个的优雅方式是什么?

And*_*are 52

这是一个扩展方法,可以执行您想要的操作:

public static IEnumerable<List<T>> Partition<T>(this IList<T> source, Int32 size)
{
    for (int i = 0; i < (source.Count / size) + (source.Count % size > 0 ? 1 : 0); i++)
        yield return new List<T>(source.Skip(size * i).Take(size));
}
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个更清洁的功能版本:

public static IEnumerable<List<T>> Partition<T>(this IList<T> source, Int32 size)
{
    for (int i = 0; i < Math.Ceiling(source.Count / (Double)size); i++)
        yield return new List<T>(source.Skip(size * i).Take(size));
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在实际的LINQ实现中,`Skip`和`Take`只是在给定的序列上循环,如果源实现`IList`则没有检查/优化,因此可以通过索引访问.因为它们是`O(m)`(其中`m`是你想要跳过或取的元素的数量),这个`Partition()`扩展可能不会给出预期的性能. (6认同)
  • for(int i = 0; i <source.Count; i + = size){/*...*/} (3认同)
  • 正是我要提出的建议.+1读我的想法. (2认同)

Sco*_*vey 30

使用LINQ,您可以在一行代码中剪切您的组,就像这样......

var x = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

var groups = x.Select((i, index) => new
{
    i,
    index
}).GroupBy(group => group.index / 4, element => element.i);
Run Code Online (Sandbox Code Playgroud)

然后你可以迭代这些组,如下所示......

foreach (var group in groups)
{
    Console.WriteLine("Group: {0}", group.Key);

    foreach (var item in group)
    {
        Console.WriteLine("\tValue: {0}", item);
    }
}
Run Code Online (Sandbox Code Playgroud)

你会得到一个看起来像这样的输出......

Group: 0
        Value: 1
        Value: 2
        Value: 3
        Value: 4
Group: 1
        Value: 5
        Value: 6
        Value: 7
        Value: 8
Group: 2
        Value: 9
        Value: 10
        Value: 11
Run Code Online (Sandbox Code Playgroud)

  • 不能完全满足问题的要求,但是对+1的思考有点不同。 (2认同)

Joe*_*Joe 11

像(未经测试的航空代码):

IEnumerable<IList<T>> PartitionList<T>(IList<T> list, int maxCount)
{
    List<T> partialList = new List<T>(maxCount);
    foreach(T item in list)
    {
        if (partialList.Count == maxCount)
        {
           yield return partialList;
           partialList = new List<T>(maxCount);
        }
        partialList.Add(item);
    }
    if (partialList.Count > 0) yield return partialList;
}
Run Code Online (Sandbox Code Playgroud)

这将返回列表的枚举而不是列表列表,但您可以轻松地将结果包装在列表中:

IList<IList<T>> listOfLists = new List<T>(PartitionList<T>(list, maxCount));
Run Code Online (Sandbox Code Playgroud)


Jod*_*ell 6

避免分组,数学和重复.

该方法避免了不必要的计算,比较和分配.包括参数验证.

这是关于小提琴工作演示.

public static IEnumerable<IList<T>> Partition<T>(
    this IEnumerable<T> source,
    int size)
{
    if (size < 2)
    {
        throw new ArgumentOutOfRangeException(
            nameof(size),
            size,
            "Must be greater or equal to 2.");  
    }

    T[] partition;
    int count;

    using (var e = source.GetEnumerator())
    {
        if (e.MoveNext())
        {
            partition = new T[size];
            partition[0] = e.Current;
            count = 1;
        }
        else
        {
            yield break;    
        }

        while(e.MoveNext())
        {
            partition[count] = e.Current;
            count++;

            if (count == size)
            {
                yield return partition;
                count = 0;
                partition = new T[size];
            }
        }
    }

    if (count > 0)
    {
        Array.Resize(ref partition, count);
        yield return partition;
    }
}
Run Code Online (Sandbox Code Playgroud)