如何从IEnumerable中剥离块
本文关键字:剥离 IEnumerable | 更新日期: 2023-09-27 18:13:20
我发布这篇文章更多的是作为一个学习练习,而不是因为我需要改变命令式循环的实现。我的问题是如何将这个循环转换为LINQ?给定一个输入IEnumerable字节,它被认为是在8字节的"块"中。输出的IEnumerable应该删除所有包含0的块。
粗略的命令式实现
private static IEnumerable<byte> StripBlocksWithZeroes(IEnumerable<byte> input)
{
var stripped = new List<byte>();
var inputArray = input.ToArray();
for (int i = 0; i < inputArray.Length; i += 8)
{
if (inputArray[i + 0] != 0 &&
inputArray[i + 1] != 0 &&
inputArray[i + 2] != 0 &&
inputArray[i + 3] != 0 &&
inputArray[i + 4] != 0 &&
inputArray[i + 5] != 0 &&
inputArray[i + 6] != 0 &&
inputArray[i + 7] != 0)
{
stripped.Add(inputArray[i + 0]);
stripped.Add(inputArray[i + 1]);
stripped.Add(inputArray[i + 2]);
stripped.Add(inputArray[i + 3]);
stripped.Add(inputArray[i + 4]);
stripped.Add(inputArray[i + 5]);
stripped.Add(inputArray[i + 6]);
stripped.Add(inputArray[i + 7]);
}
}
return stripped;
}
Off the top of my head:
inputArray.Select((item, index) => new {item, groupIndex = index / 8})
.GroupBy(x => x.groupIndex)
.Where(g => g.All(x => x.item != 0))
//.Select(g => g.First().item)
.SelectMany(g => g.Select(x => x.item))
说明:
利用整数除法,用一个groupIndex标记每一项,这样每个连续的8个组将有相同的groupIndex。
按群索引分组,所以现在我们有一个序列的序列。
对于每一个内序列,确保它不包含零。
将结果序列平放回单个序列
还有一个稍微不同的版本:
return input.Select((value, index) => new { value, index })
.GroupBy(x => x.index / 8, x => x.value)
.Where(g => g.All(value => value != 0))
.SelectMany(g => g);
注意:这段代码使用了这个GroupBy
重载,它使用一个元素选择器来尽快摆脱索引。
您可以使用"整除技巧"按8个字节分组并检查其中是否有0:
private static IEnumerable<byte> StripBlocksWithZeroes(IEnumerable<byte> input)
{
IEnumerable<byte> stripped = input
.Select((b, i) => new { Byte = b, Index = i })
.GroupBy(x => x.Index / 8)
.Where(g => g.All(x => x.Byte != 0))
.SelectMany(g => g.Select(x => x.Byte))
.ToList();
return stripped;
}