如何分块列表<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;
}
功能完成
}
我正在使用 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;
}
功能完成
}