我试图创建一个LINQ查询,它是SelectMany的派生物。 我有N个项目:
new {
{ Text = "Hello", Width = 2 },
{ Text = "Something else", Width = 1 },
{ Text = "Another", Width = 1 },
{ Text = "Extra-wide", Width = 3 },
{ Text = "Random", Width = 1 }
}
我希望结果为List<List<object>>()
,其中:
List<List<object>> = new {
// first "row"
{
{ Text = "Hello", Width = 2 },
{ Text = "Something else", Width = 1 },
{ Text = "Another", Width = 1 }
},
// second "row"
{
{ Text = "Extra-wide", Width = 3 },
{ Text = "Random", Width = 1 }
}
}
所以这些项目被分组为&#34;行&#34;其中内部List中的Sum(width)小于或等于数字(maxWidth - 在我的实例中,4)。 它有点像GroupBy的衍生物,但是GroupBy依赖于数组中较早的值 - 这是我难以接受的地方。
任何想法都会受到赞赏。
答案 0 :(得分:2)
我们可以将LINQ的Aggregate方法的思想与GroupWhile方法相结合,在满足条件时对连续项进行分组,以便为谓词中使用的当前组构建聚合值:
public static IEnumerable<IEnumerable<T>> GroupWhileAggregating<T, TAccume>(
this IEnumerable<T> source,
TAccume seed,
Func<TAccume, T, TAccume> accumulator,
Func<TAccume, T, bool> predicate)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
List<T> list = new List<T>() { iterator.Current };
TAccume accume = accumulator(seed, iterator.Current);
while (iterator.MoveNext())
{
accume = accumulator(accume, iterator.Current);
if (predicate(accume, iterator.Current))
{
list.Add(iterator.Current);
}
else
{
yield return list;
list = new List<T>() { iterator.Current };
accume = accumulator(seed, iterator.Current);
}
}
yield return list;
}
}
使用这种分组方法,我们现在可以写:
var query = data.GroupWhileAggregating(0,
(sum, item) => sum + item.Width,
(sum, item) => sum <= 4);
答案 1 :(得分:1)
您可以使用MoreLinq库中的Batch
方法来执行此操作,该方法可用作NuGet包。结果是List<IEnumerable<object>>
。这是代码:
class Obj
{
public string Text {get;set;}
public int Width {get;set;}
}
void Main()
{
var data = new [] {
new Obj { Text = "Hello", Width = 2 },
new Obj { Text = "Something else", Width = 1 },
new Obj { Text = "Another", Width = 1 },
new Obj { Text = "Extra-wide", Width = 3 },
new Obj { Text = "Random", Width = 1 }
};
var maxWidth = data.Max (d => d.Width );
var result = data.Batch(maxWidth).ToList();
result.Dump(); // Dump is a linqpad method
<强>输出强>
答案 2 :(得分:0)
我认为你不能用LINQ做到这一点。一种替代方法如下:
var data = ... // original data
var newdata = new List<List<object>>();
int csum = 0;
var crow = new List<object>();
foreach (var o in data) {
if (csum + o.Width > 4) { //check if the current element fits into current row
newdata.Add(crow); //if not add current row to list
csum = 0;
crow = new List<object>(); //and create new row
}
crow.Add(o); //add current object to current row
csum += o.Width;
}
if (crow.Count() > 0) //last row
newData.Add(c);
编辑:另一个答案建议使用MoreLinq库中的批处理。事实上,上面的源代码或多或少是相同的,Batch所做的,但不仅计算每个批次中的元素,而且总结所需的属性。可以使用自定义选择器来概括我的代码,以便在批量大小&#34;方面更加灵活。