当前位置:  开发笔记 > 编程语言 > 正文

使用LINQ将集合拆分为`n`部分?

如何解决《使用LINQ将集合拆分为`n`部分?》经验,为你挑选了6个好方法。

有没有一种很好的方法可以n使用LINQ 将集合拆分为多个部分?当然不一定均匀.

也就是说,我想将集合划分为子集合,每个子集合包含元素的子集,其中最后一个集合可以是不规则的.



1> Muhammad Has..:

纯linq和最简单的解决方案如下所示.

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


最好使用包含索引的Select重载.
你可以这样做:选择part.AsEnumerable()而不是select(IEnumerable )部分.感觉更优雅.
在长列表上执行所有这些模数运算可能会有点贵.

2> Jon Skeet..:

编辑:好的,看起来我误解了这个问题.我把它读作"长度为n的片段"而不是"n片".卫生署!考虑删除答案......

(原始答案)

我不相信有一种内置的分区方法,虽然我打算在LINQ to Objects的一组添加中编写一个.Marc Gravell 在这里有一个实现,虽然我可能会修改它以返回一个只读视图:

public static IEnumerable> Partition
    (this IEnumerable 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(array);
            array = null;
            count = 0;
        }
    }
    if (array != null)
    {             
        Array.Resize(ref array, count);
        yield return new ReadOnlyCollection(array);
    }
}


谢谢你没有删除,即使它不是OP的答案,我想要完全相同的东西 - 长度为n :)的部分.
你*真的*不喜欢那些"array [count ++]",eh ;-p
@Dejan:不,不.注意使用`yield return`.它需要一个*批处理*一次在内存中,但这就是全部.

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


我对SQL风格的Linq有一种非理性的厌恶,所以这是我最喜欢的答案.
我有modulo的相同问题,所以我用`int columnLength =(int)Math.Ceiling((decimal)(list.Count())/ parts)计算列长度``然后用`.GroupBy(x)进行除法=> x.index/columnLength)`.一个缺点是Count()枚举列表.

4> Mike..:

好吧,我会戴上帽子.我的算法的优点:

    没有昂贵的乘法,除法或模数运算符

    所有操作都是O(1)(见下面的注释)

    适用于IEnumerable <> source(不需要Count属性)

    简单

代码:

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

  var section = new List(length);

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

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

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

正如下面的评论中指出的那样,这种方法实际上并没有解决原始问题,该问题要求固定数量的长度近似相等的部分.也就是说,您仍然可以通过这种方式调用原始问题来解决原始问题:

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

当以这种方式使用时,该方法不再是O(1),因为Count()操作是O(N).


你的答案是对的,但问题是错的.您的答案为每个块提供了固定大小的未知数量的块.但OP需要一个Split功能,它可以提供固定数量的块,每个块具有任意大小(希望等于或接近相同的大小).也许更适合这里http://stackoverflow.com/questions/3773403/linq-partition-list-into-lists-of-8-members
@ShadowChaser根据MSDN清除LinkedList是O(N)的复杂性,所以它会破坏我的O(1)的目标.当然,你可以说foreach是O(N)开头...... :)

5> nawfal..:

这与接受的答案相同,但更简单的表示:

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

上述方法将一个IEnumerable相等大小或接近相等大小的块分成N个.

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

上面的方法将一个IEnumerable块分成所需的固定大小的块,块的总数是不重要的 - 这不是问题所在.

Split除了速度较慢之外,该方法的问题在于,在分组将基于每个位置的N的倍数进行分组的意义上,它会对输出进行加扰,或者换句话说,您没有得到块.按原始顺序.

这里几乎每个答案要么不保留顺序,要么是分区而不是分裂,或者显然是错误的.试试这个更快,保留订单但是更详细:

public static IEnumerable> Split(this ICollection 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> Elmer..:

我一直在使用我之前发布的分区功能.关于它的唯一坏处是它不是完全流式传输.如果您使用序列中的少数元素,这不是问题.当我开始使用我的序列中的100.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> Partition(this IEnumerable 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(enumerable, partitionSize);
        }

    }


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

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

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


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

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

        IEnumerable _current;
        public IEnumerable 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(_enumerator, _partitionSize);
                result = true;
            }
            else
            {
                _current = null;
                result = false;
            }

            return result;
        }

    }



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

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

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


    class PartitionEnumerator : IEnumerator
    {
        IEnumerator _enumerator;
        int _partitionSize;
        int _count;
        public PartitionEnumerator(IEnumerator 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;
        }

    }
}

请享用!

推荐阅读
手机用户2402851155
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有