.net 将集合拆分为n个部分不会产生所需的结果安全性
我正在尝试将一个集合拆分为特定数量的部分,我已经找到了一些关于StackOverflow的帮助解决方案: 这是我从@Hasan Khan solution的VB.Net翻译:.net 将集合拆分为n个部分不会产生所需的结果安全性,.net,vb.net,linq,ienumerable,.net,Vb.net,Linq,Ienumerable,我正在尝试将一个集合拆分为特定数量的部分,我已经找到了一些关于StackOverflow的帮助解决方案: 这是我从@Hasan Khan solution的VB.Net翻译: ''' <summary> ''' Splits an <see cref="IEnumerable(Of T)"/> into the specified amount of secuences. ''' </summary> Public Shared Function SplitI
''' <summary>
''' Splits an <see cref="IEnumerable(Of T)"/> into the specified amount of secuences.
''' </summary>
Public Shared Function SplitIntoParts(Of T)(ByVal col As IEnumerable(Of T),
ByVal amount As Integer) As IEnumerable(Of IEnumerable(Of T))
Dim i As Integer = 0
Dim splits As IEnumerable(Of IEnumerable(Of T)) =
From item As T In col
Group item By item = Threading.Interlocked.Increment(i) Mod amount
Into Group
Select Group.AsEnumerable()
Return splits
End Function
''' <summary>
''' Splits an <see cref="IEnumerable(Of T)"/> into the specified amount of secuences.
''' </summary>
Public Shared Function SplitIntoParts(Of T)(ByVal col As IEnumerable(Of T),
ByVal amount As Integer) As IEnumerable(Of IEnumerable(Of T))
Return col.Select(Function(item, index) New With {index, item}).
GroupBy(Function(x) x.index Mod amount).
Select(Function(x) x.Select(Function(y) y.item))
End Function
这两个函数都给出了此结果:
1: { 1, 3, 5, 7, 9 }
2: { 2, 4, 6, 8, 10 }
而不是这些秘密:
1: { 1, 2, 3, 4, 5 }
2: { 6, 7, 8, 9, 10 }
我做错了什么?你没有做错什么;只是你使用的方法并没有按照你想要的方式来排序。想想mod和GroupBy是如何工作的,你就会明白为什么了 我建议您使用,因为它保持了您收藏的顺序,我冒昧地将它翻译成VB.Net 您只需事先计算每个分区的大小,因为它不会将集合拆分为n个块,而是拆分为长度为n的块:
请随意隐藏分区CintMath.天花板。。。参与新方法。你没有做错什么;只是你使用的方法并没有按照你想要的方式来排序。想想mod和GroupBy是如何工作的,你就会明白为什么了 我建议您使用,因为它保持了您收藏的顺序,我冒昧地将它翻译成VB.Net 您只需事先计算每个分区的大小,因为它不会将集合拆分为n个块,而是拆分为长度为n的块:
请随意隐藏分区CintMath.天花板。。。参与新方法。低效解决方案对数据的迭代次数过多:
class Program
{
static void Main(string[] args)
{
var data = Enumerable.Range(1, 10);
var result = data.Split(2);
}
}
static class Extensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> col, int amount)
{
var chunkSize = (int)Math.Ceiling((double)col.Count() / (double)amount);
for (var i = 0; i < amount; ++i)
yield return col.Skip(chunkSize * i).Take(chunkSize);
}
}
低效的解决方案数据迭代次数过多:
class Program
{
static void Main(string[] args)
{
var data = Enumerable.Range(1, 10);
var result = data.Split(2);
}
}
static class Extensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> col, int amount)
{
var chunkSize = (int)Math.Ceiling((double)col.Count() / (double)amount);
for (var i = 0; i < amount; ++i)
yield return col.Skip(chunkSize * i).Take(chunkSize);
}
}
MyExtensions类有两个公共拆分方法: 对于ICollection-仅在集合中迭代一次-用于拆分。 For IEnumerable-遍历可枚举项两次:计数项和拆分项。如果可能的话,不要使用这个。第一个是安全的,速度快两倍。 更重要的是:该算法试图返回指定数量的集合
MyExtensions类有两个公共拆分方法: 对于ICollection-仅在集合中迭代一次-用于拆分。 For IEnumerable-遍历可枚举项两次:计数项和拆分项。如果可能的话,不要使用这个。第一个是安全的,速度快两倍。 更重要的是:该算法试图返回指定数量的集合
我不是LINQ专家。。但是mod在这里是错误的。你会想按某种计数进行分组特别是分区method@Ric谢谢,我已经试过了,但那是为了将集合拆分为若干个元素/秒,我正在尝试将集合拆分为若干个秒。@Sam Axe谢谢你的评论,但是VB.Net中的C%运算符相当于Mod,我不对?是的,Mod相当于%。但是如果你想要一个序列,Mod是错误的操作。我不是LINQ专家。。但是mod在这里是错误的。你会想按某种计数进行分组特别是分区method@Ric谢谢,我已经试过了,但那是为了将集合拆分为若干个元素/秒,我正在尝试将集合拆分为若干个秒。@Sam Axe谢谢你的评论,但是VB.Net中的C%运算符相当于Mod,我不对?是的,Mod相当于%。但是如果你想要一个序列,Mod是一个错误的操作。谢谢你解释了顺序和代码,但是你的函数在每个集合中分裂成若干个元素,而不是分裂成若干个集合,至少我的VB翻译就是这样做的,我错了?@ElektroStudios是的,正如我在回答中所说,该函数将每个集合拆分为若干个元素;因此,您必须计算每个集合中的数字元素,正如我在回答中所说的:CIntMath.CeilingmainCol.Count/amount,这正是Szer在回答中所做的,希望我的解决方案中的函数更有效,因为它不会在源可枚举项上迭代多次。感谢您解释排序和代码,但您的函数会分解为每个集合的多个元素,而不是多个集合,至少我的VB翻译就是这样做的,我错了?@ElektroStudios是的,正如我在回答中所说,该函数将每个集合拆分为若干个元素;因此,您必须计算每个集合中的数字元素,正如我在回答中所说的:CIntMath.CeilingmainCol.Count/amount,这正是Szer在回答中所做的,希望我的解决方案中的函数更有效,因为它不会在源枚举上重复多次。我对该函数有问题,在某种程度上似乎是不完美的,如果我尝试将一个包含10个项目的集合拆分为10个片段,它应该会生成10个包含1个元素的集合,但不会。我正在研究为什么这个方法会迭代IEnumerable col 1+数量次。第一次计算ITME,下一次在for循环的每个迭代中。这是可行的,但不是有效的解决办法。首先,是的
最好拆分ICollection,而不是IEnumerable ICollection具有Count。第二:不可能对每个块都调用Skip。@ElektroStudios我自己测试过,你的新代码一切正常example@rtf_leg是的,你说得对。在我的回答中添加了免责声明我的功能有问题,在某种程度上似乎不完美,如果我尝试将10个项目的集合拆分为10个片段,它应该会生成10个集合,每个集合包含1个元素,但不会。我正在研究为什么这个方法会迭代IEnumerable col 1+数量次。第一次计算ITME,下一次在for循环的每个迭代中。这是可行的,但不是有效的解决办法。首先:最好拆分ICollection,而不是IEnumerable ICollection有Count。第二:不可能对每个块都调用Skip。@ElektroStudios我自己测试过,你的新代码一切正常example@rtf_leg是的,你说得对。在我的回答中添加了免责声明惊人,谢谢,我实际上在处理金额值时执行了一个错误,并且在值不是“collection.Count”的乘数时引发了一个异常。为了避免使用@Szer的解决方案生成空secuences,您的解决方案考虑了该功能。惊人,谢谢,实际上,我在处理amount值时执行了一个错误,并在该值不是“collection.Count”的乘数时引发了一个异常。为了避免使用@Szer的解决方案生成空secuences,您的解决方案考虑了该功能。
mainCol.Partition(CInt(Math.Ceiling(mainCol.Count() / 2)))
class Program
{
static void Main(string[] args)
{
var data = Enumerable.Range(1, 10);
var result = data.Split(2);
}
}
static class Extensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> col, int amount)
{
var chunkSize = (int)Math.Ceiling((double)col.Count() / (double)amount);
for (var i = 0; i < amount; ++i)
yield return col.Skip(chunkSize * i).Take(chunkSize);
}
}
Public Shared Iterator Function SplitIntoParts(Of T)(ByVal col As IEnumerable(Of T),
ByVal amount As Integer) As IEnumerable(Of IEnumerable(Of T))
Dim chunkSize As Integer = CInt(Math.Ceiling(CDbl(col.Count()) / CDbl(amount)))
For i As Integer = 0 To amount - 1
Yield col.Skip(chunkSize * i).Take(chunkSize)
Next
End Function
public static class MyExtensions
{
// Works with ICollection - iterates through collection only once.
public static IEnumerable<IEnumerable<T>> Split<T>(this ICollection<T> items, int count)
{
return Split(items, items.Count, count);
}
// Works with IEnumerable and iterates items TWICE: first for count items, second to split them.
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, int count)
{
// ReSharper disable PossibleMultipleEnumeration
var itemsCount = items.Count();
return Split(items, itemsCount, count);
// ReSharper restore PossibleMultipleEnumeration
}
private static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> items, int itemsCount, int partsCount)
{
if (items == null)
throw new ArgumentNullException("items");
if (partsCount <= 0)
throw new ArgumentOutOfRangeException("partsCount");
var rem = itemsCount % partsCount;
var min = itemsCount / partsCount;
var max = rem != 0 ? min + 1 : min;
var index = 0;
var enumerator = items.GetEnumerator();
while (index < itemsCount)
{
var size = 0 < rem-- ? max : min;
yield return SplitPart(enumerator, size);
index += size;
}
}
private static IEnumerable<T> SplitPart<T>(IEnumerator<T> enumerator, int count)
{
for (var i = 0; i < count; i++)
{
if (!enumerator.MoveNext())
break;
yield return enumerator.Current;
}
}
}
var items = new [] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
for(var i = 1; i <= items.Length + 3; i++)
{
Console.WriteLine("{0} part(s)", i);
foreach (var part in items.Split(i))
Console.WriteLine(string.Join(", ", part));
Console.WriteLine();
}
1 part(s)
a, b, c, d, e, f, g, h, i, j
2 part(s)
a, b, c, d, e
f, g, h, i, j
3 part(s)
a, b, c, d
e, f, g
h, i, j
4 part(s)
a, b, c
d, e, f
g, h
i, j
5 part(s)
a, b
c, d
e, f
g, h
i, j
6 part(s)
a, b
c, d
e, f
g, h
i
j
7 part(s)
a, b
c, d
e, f
g
h
i
j
8 part(s)
a, b
c, d
e
f
g
h
i
j
9 part(s)
a, b
c
d
e
f
g
h
i
j
10 part(s)
a
b
c
d
e
f
g
h
i
j
11 part(s) // Only 10 items in collection.
a
b
c
d
e
f
g
h
i
j
12 part(s) // Only 10 items in collection.
a
b
c
d
e
f
g
h
i
j
13 part(s) // Only 10 items in collection.
a
b
c
d
e
f
g
h
i
j