我试图将一个列表拆分为一系列较小的列表。
我的问题:我的拆分列表功能没有将它们拆分为正确大小的列表。它应该把它们分成大小为30的列表,但它却把它们分成大小为114的列表?
如何让我的函数将一个列表分割成X个大小为30或更小的列表?
public static List<List<float[]>> splitList(List <float[]> locations, int nSize=30)
{
List<List<float[]>> list = new List<List<float[]>>();
for (int i=(int)(Math.Ceiling((decimal)(locations.Count/nSize))); i>=0; i--) {
List <float[]> subLocat = new List <float[]>(locations);
if (subLocat.Count >= ((i*nSize)+nSize))
subLocat.RemoveRange(i*nSize, nSize);
else subLocat.RemoveRange(i*nSize, subLocat.Count-(i*nSize));
Debug.Log ("Index: "+i.ToString()+", Size: "+subLocat.Count.ToString());
list.Add (subLocat);
}
return list;
}
如果我在144大小的列表上使用该函数,那么输出是:
指数:4,尺寸:120
指数:3,尺寸:114
指数:2,尺寸:114
指数:1,尺寸:114
索引:0,大小:114
我建议使用这个扩展方法按指定的块大小将源列表块到子列表:
/// <summary>
/// Helper methods for the lists.
/// </summary>
public static class ListExtensions
{
public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / chunkSize)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
}
}
例如,如果你把18个项目分成5个块,它会给你一个包含4个子列表的列表,其中包含以下项目:5-5-5-3。
注意:在。net 6中即将对LINQ进行的分块改进
会像这样从盒子里出来:
const int PAGE_SIZE = 5;
IEnumerable<Movie[]> chunks = movies.Chunk(PAGE_SIZE);
虽然上面的很多答案都是可行的,但它们在永不结束的序列(或非常长的序列)上都失败了。下面是一个完全在线的实现,它保证了最好的时间和内存复杂度。我们只迭代源枚举对象一次,并使用yield return进行惰性求值。使用者可以在每次迭代时丢弃列表,使内存占用等于元素数量为w/ batchSize的列表的内存占用。
public static IEnumerable<List<T>> BatchBy<T>(this IEnumerable<T> enumerable, int batchSize)
{
using (var enumerator = enumerable.GetEnumerator())
{
List<T> list = null;
while (enumerator.MoveNext())
{
if (list == null)
{
list = new List<T> {enumerator.Current};
}
else if (list.Count < batchSize)
{
list.Add(enumerator.Current);
}
else
{
yield return list;
list = new List<T> {enumerator.Current};
}
}
if (list?.Count > 0)
{
yield return list;
}
}
}
编辑:刚刚意识到OP要求将List<T>分解为更小的List<T>,所以我关于无限枚举值的评论不适用于OP,但可能会帮助其他在这里结束的人。这些评论是对其他发布的解决方案的回应,这些解决方案使用IEnumerable<T>作为其函数的输入,但却多次枚举源可枚举对象。
public static List<List<float[]>> SplitList(List<float[]> locations, int nSize=30)
{
var list = new List<List<float[]>>();
for (int i = 0; i < locations.Count; i += nSize)
{
list.Add(locations.GetRange(i, Math.Min(nSize, locations.Count - i)));
}
return list;
}
通用版:
public static IEnumerable<List<T>> SplitList<T>(List<T> locations, int nSize=30)
{
for (int i = 0; i < locations.Count; i += nSize)
{
yield return locations.GetRange(i, Math.Min(nSize, locations.Count - i));
}
}