[英]LINQ group by sum of property
我正在嘗試創建一個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 }
}
}
因此,項目被分組為“行”,其中內部List中的Sum(寬度)小於或等於數字(maxWidth - 在我的實例中,4)。 它有點像GroupBy的衍生物,但GroupBy依賴於數組中較早的值 - 這是我難以接受的地方。
任何想法,將不勝感激。
我們可以將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);
您可以使用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
產量
我不認為你可以用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所做的,但不僅計算每個批次中的元素,而且總結所需的屬性。 可以使用自定義選擇器來概括我的代碼,以便在“批量大小”方面更靈活。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.