我有一个数组,其中包含以下格式的数据结构字段;
[0] = Record 1 (Name Field)
[1] = Record 1 (ID Field)
[2] = Record 1 (Other Field)
[3] = Record 2 (Name Field)
[4] = Record 2 (ID Field)
[5] = Record 2 (Other Field)
等
我正在按如下方式将其处理成一个集合;
for (int i = 0; i < components.Length; i = i + 3)
{
results.Add(new MyObj
{
Name = components[i],
Id = components[i + 1],
Other = components[i + 2],
});
}
这很好用,但我想知道是否有一种很好的方法可以用LINQ实现相同的输出?这里没有功能要求,我只是好奇它是否可以完成。
我确实尝试过按索引分组(在ToList()
数组之后);
var groupings = components
.GroupBy(x => components.IndexOf(x) / 3)
.Select(g => g.ToArray())
.Select(a => new
{
Name = a[0],
Id = a[1],
Other = a[2]
});
这样可行,但我认为这对我正在尝试做的事情有点过分。是否有更简单的方法来实现与for
循环相同的输出?
答案 0 :(得分:2)
我会说坚持你的for循环。但是,这应该适用于Linq:
List<MyObj> results = components
.Select((c ,i) => new{ Component = c, Index = i })
.GroupBy(x => x.Index / 3)
.Select(g => new MyObj{
Name = g.First().Component,
Id = g.ElementAt(1).Component,
Other = g.Last().Component
})
.ToList();
答案 1 :(得分:2)
看起来像是约什爱因斯坦IEnumerable.Batch扩展的完美候选人。它将一个可枚举的片段切成一定大小的块,然后将它们作为数组的枚举输出:
public static IEnumerable<T[]> Batch<T>(this IEnumerable<T> self, int batchSize)
在这个问题的情况下,你会做这样的事情:
var results =
from batch in components.Batch(3)
select new MyObj { Name = batch[0], Id = batch[1], Other = batch[2] };
更新:2年后,我链接的批量扩展似乎已经消失。由于它被认为是问题的答案,并且万一其他人认为它有用,这是我目前对Batch
的实现:
public static partial class EnumExts
{
/// <summary>Split sequence into blocks of specified size.</summary>
/// <typeparam name="T">Type of items in sequence</typeparam>
/// <param name="sequence"><see cref="IEnumerable{T}"/> sequence to split</param>
/// <param name="batchLength">Number of items per returned array</param>
/// <returns>Arrays of <paramref name="batchLength"/> items, with last array smaller if sequence count is not a multiple of <paramref name="batchLength"/></returns>
public static IEnumerable<T[]> Batch<T>(this IEnumerable<T> sequence, int batchLength)
{
if (sequence == null)
throw new ArgumentNullException("sequence");
if (batchLength < 2)
throw new ArgumentException("Batch length must be at least 2", "batchLength");
using (var iter = sequence.GetEnumerator())
{
var bfr = new T[batchLength];
while (true)
{
for (int i = 0; i < batchLength; i++)
{
if (!iter.MoveNext())
{
if (i == 0)
yield break;
Array.Resize(ref bfr, i);
break;
}
bfr[i] = iter.Current;
}
yield return bfr;
bfr = new T[batchLength];
}
}
}
}
此操作是延迟的,单个枚举并在线性时间内执行。与我见过的其他一些Batch
实现相比,它相对较快,即使它为每个结果分配一个新数组。
这只是表明:在你描述之前你永远无法分辨,你应该总是引用代码以防它消失。
答案 2 :(得分:1)
也许迭代器可能是合适的。
声明自定义迭代器:
static IEnumerable<Tuple<int, int, int>> ToPartitions(int count)
{
for (var i = 0; i < count; i += 3)
yield return new Tuple<int, int, int>(i, i + 1, i + 2);
}
准备以下LINQ:
var results = from partition in ToPartitions(components.Length)
select new {Name = components[partition.Item1], Id = components[partition.Item2], Other = components[partition.Item3]};
答案 3 :(得分:1)
此方法可以让您了解如何使代码更具表现力。
public static IEnumerable<MyObj> AsComponents<T>(this IEnumerable<T> serialized)
where T:class
{
using (var it = serialized.GetEnumerator())
{
Func<T> next = () => it.MoveNext() ? it.Current : null;
var obj = new MyObj
{
Name = next(),
Id = next(),
Other = next()
};
if (obj.Name == null)
yield break;
yield return obj;
}
}
就目前而言,我不喜欢检测输入结束的方式,但您可能拥有关于如何更好地执行此操作的特定于域的信息。