簡體   English   中英

LINQ集團的財產總和

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM