如何分块列表<T>?

How to chunk List<T>?

我正在使用 Microsoft Translator API,特别是 TranslateArray 方法。我的挑战是 texts 参数的内置限制:

[...] 包含待翻译文本的数组。 [...] 所有要翻译的文本的总和不得超过 10000 个字符。数组元素的最大数量为 2000.

我得到了可变长度的列表元素——每个元素的长度都不同(标题、描述)。我想将此数据传递给 TranslateArray() 但它需要大小合适。我该怎么做?

    public class TranslateItem
    {
        public string Title { get; set; }
        public string Desc { get; set; }
    }

    private static void chunkNorris()
    {
        // list contains elements to be translated
        var list = new List<TranslateItem>();

        var chunkList = new List<TranslateItem>();
        int itemLength = 0; int totalLength = 0;

        foreach (var batch in list.Batch(1000))
        {
            foreach (var item in batch)
            {
                itemLength = item.Title.Length + item.Desc.Length;
                totalLength = totalLength + itemLength;

                if (itemLength <= 10000 && totalLength <= 10000)
                {
                    chunkList.Add(new TranslateItem() { Title = item.Title, Desc = item.Desc });
                }
                else
                {
                    // do translation here...
                    // bug here: itemLength can be > 10000

                    // reset chunkList and add item to empty list
                    chunkList.Clear();
                    itemLength = totalLength = item.Title.Length + item.Desc.Length;
                    chunkList.Add(new TranslateItem() { Title = item.Title, Desc = item.Desc});
                }

                if (item.Equals(list.Last()))
                {
                    // do translation here...
                }
            }
        }
    }

更新:这就是我所拥有的。该代码将 运行 并将数组拆分为 1000 "batches"(即 2 列的 1000 个元素)以符合 2000 个数组元素的限制。但是,它不会解决大小超过 10,000 个字符的问题。此外,代码非常笨拙——想知道是否可以使用 LINQ 以更优雅的方式完成。 Batch method from here.

真的,您应该修改 Batch 方法以使用两个而不是一个分块规则。也就是说,让我们从头开始吧。这里的基本技术是利用 C# 对 IEnumerable/yield.

的内置支持

您的目标是批量翻译项目。

public class TranslateItem
{
    public string Title { get; set; }
    public string Desc { get; set; }
}

那么,让我们从签名开始吧。给定一些要翻译的项目 (IEnumerable<TranslateItem>),return 一些要翻译的分块项目 (IEnumerable<IEnumerable<TranslateItem>>)

private static IEnumerable<IEnumerable<TranslateItem>> 
    chunkNorris(IEnumerable<TranslateItem> data)
{

保留 运行 批要翻译的项目,跟踪该批中字符的总长度:

    var chunkList = new List<TranslateItem>();
    int totalLength = 0;

一项一项,处理每一项

    foreach (var item in data)
    {

找出批次中当前项目的长度:

        int itemLength = item.Title.Length + item.Desc.Length;

如果该项目本身有 10000 个字符,请以某种方式处理它:

        if (itemLength > 10000)
        {
            throw new NotImplementedException("TODO");
        }

如果不违反我们的两个规则(不超过 1000 个项目/2000 个字符串和不超过 10000 个字符),则将该项目添加到我们的当前批次是安全的。

        bool SafeToAddMoreData = 
            (itemLength+totalLength) <= 10000 && chunkList.Count < 1000;

如果扩展我们当前的块不安全,就让出当前的块,然后创建一个新的块。

        if(!SafeToAddMoreData)
        {
            yield return chunkList;
            chunkList = new List<TranslateItem>();
            totalLength = 0;
        }

现在可以安全地向我们当前的块添加更多数据,因为 SafeToAddMoreData 是错误的或者因为我们刚刚清除了我们的当前块。因此,向我们当前的块添加更多数据,确保更新块字符长度的 运行 总数。

        totalLength = totalLength + itemLength;
        chunkList.Add(item);

对每个块重复此过程

    }

我们的循环只有在被阻止扩展当前块时才吐出数据。吐出最后一块。

    if (chunkList.Any()) //Always be true (unless data was empty).
    {
        yield return chunkList;
    }

功能完成

}