使用 LINQ 将列表拆分为子列表

本文关键字:列表 拆分 LINQ 使用 | 更新日期: 2023-09-27 18:30:44

我有一个包含n 个 Foo 项目的列表。Foo 包含一个长属性 Foo.FileSize。现在我想将此列表拆分为具有 n 个元素的子列表,这些元素的总和 文件大小不超过 10000。当然,还有一些项目Foo.FileSize超过10000。对于这种特殊情况,只需要一个仅包含此项目的子列表。

请有人可以提出一些建议吗?

const long maxdownloadsize = 10485760;
long actualdownloadsize = 0;
List<TI> downloadTI = new List<TI>();
for (int i = 0; i < comparedTI.Count; i++)
{
    var ti = comparedTI[i];
    actualdownloadsize += ti.FileSize;
    downloadTI.Add(ti);
    if (actualdownloadsize > maxdownloadsize || i == comparedTI.Count-1)
    {
        actualdownloadsize = 0;
        AddToList(downloadTI);
        downloadTI = new List<TI>();
    }
}

使用 LINQ 将列表<T>拆分为子列表<T>

老实说,以传统方式而不是使用 Linq 更容易做到这一点,因为序列中每个元素所需的转换方法需要了解所有先前的元素。 所以,你可以做这样的事情:

    public static IEnumerable<List<T>> Partition<T>(IEnumerable<T> list, Func<T, long> getValue, long maxSum)
    {
        long sum = 0;
        int partition = 0;
        var query = list.Select((i, index) =>
            {
                if (index == 0)
                {
                    // Reset the external partition counter in case the query is run multiple times.
                    sum = 0;
                    partition = 0;
                }
                var value = getValue(i);
                sum = sum + value;
                if (sum > maxSum)
                {
                    sum = value;
                    partition++;
                }
                return new KeyValuePair<int, T>(partition, i);
            })
            .GroupBy(pair => pair.Key)
            .Select(g => g.Select(pair => pair.Value).ToList());
        return query;
    }

然后这样称呼它:

        var list = new List<Foo>();
        // Fill up the list.
        var query = Partition(list, f => f.FileSize, 10000);
        var partition = query.ToList();

虽然这满足了"使用 LINQ 将List<T>拆分为Sublists<T>"的要求,但实际上效率低于直接执行此操作。

这并不漂亮,但您可以使用局部变量和 LINQ GroupBy() 按大小将列表拆分为组。此外,此功能没有优化,因此您可能无法获得最佳数量的组。换句话说,如果列表中间有一个大文件,那么即使可以将更多较小的文件放入当前组,它也会启动一个新组。

var maxGroupSize = 10485760;
var groupId = 0;
var groupCount = 0;
long groupSize = 0;
var groups = files.GroupBy(f => 
{
    var size = f.FileSize;
    if ((groupCount > 0) && ((groupSize + size) > maxGroupSize))
    {
        // Start a new group and reset count and sum
        groupId++;
        groupCount = 0;
        groupSize = 0;
    }
    groupSize += size;
    groupCount++;
    return groupId;
});

GroupBy 方法返回 IGrouping<TKey, TSource>,如果需要将其转换为 List,请执行以下操作(假设TSource类型是从示例代码中TI的):

List<TI> downloadTI = groups.Select(p => new List<TI>(groups[p.Key]));