Sim*_*ver 121 .net c# linq data-structures
有没有一种很好的方法可以n
使用LINQ 将集合拆分为多个部分?当然不一定均匀.
也就是说,我想将集合划分为子集合,每个子集合包含元素的子集,其中最后一个集合可以是不规则的.
Muh*_*han 126
纯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;
}
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 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);
}
}
Run Code Online (Sandbox Code Playgroud)
man*_*u08 37
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));
}
}
Run Code Online (Sandbox Code Playgroud)
Mik*_*ike 24
好吧,我会戴上帽子.我的算法的优点:
代码:
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();
}
Run Code Online (Sandbox Code Playgroud)
正如下面的评论中指出的那样,这种方法实际上并没有解决原始问题,该问题要求固定数量的长度近似相等的部分.也就是说,您仍然可以通过这种方式调用原始问题来解决原始问题:
myEnum.Section(myEnum.Count() / number_of_sections + 1)
Run Code Online (Sandbox Code Playgroud)
当以这种方式使用时,该方法不再是O(1),因为Count()操作是O(N).
naw*_*fal 16
这与接受的答案相同,但更简单的表示:
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items,
int numOfParts)
{
int i = 0;
return items.GroupBy(x => i++ % numOfParts);
}
Run Code Online (Sandbox Code Playgroud)
上述方法将一个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();
}
Run Code Online (Sandbox Code Playgroud)
上面的方法将一个IEnumerable<T>
块分成所需的固定大小的块,块的总数是不重要的 - 这不是问题所在.
Split
除了速度较慢之外,该方法的问题在于,在分组将基于每个位置的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);
}
Run Code Online (Sandbox Code Playgroud)
这里Partition
操作的等效方法
我一直在使用我之前发布的分区功能.关于它的唯一坏处是它不是完全流式传输.如果您使用序列中的少数元素,这不是问题.当我开始使用我的序列中的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<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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
请享用!
归档时间: |
|
查看次数: |
71770 次 |
最近记录: |