簡體   English   中英

C#linq查詢聚合可以為空的布爾值

[英]C# linq query aggregate nullable boolean

我想使用linq按照邏輯聚合可空的bool:

  • 如果一切都是真的那么真實;
  • 如果一切都是假的那么假;
  • 否則為null

這是我的代碼,我無法得到bool聚合。

class T1
{
  public string property1{get;set;}
  public string property2{get;set;}
  public bool? BoolProperty{get;set;}
}

///initialize a list<T1> t2 with values......
List<T1> t2 = new List<T1>();
t2.Add(new T1() 
        {
            property1="hello",
            property2="world",
            BoolProperty=true
        });
t2.Add(new T1() 
        {
            property1="hello",
            property2="world",
            BoolProperty=false
        });

List<T1> t1 = t2.GroupBy(g => new
        {
            g.property1,
            g.property2               
        })
        .Select(g => new T1
        {
            property1 = g.Key.property1,
            property2 = g.Key.property2,                
            BoolProperty = ////can someone help? if all object in t2 are true, true; if all object in t2 are false, false; else null
 ///in this case i am expecting a null
        }).ToList();

所以t1將是“hello”,“world”,null; 謝謝

這個怎么樣?

BoolProperty = g.All(p => p.BoolProperty.GetValueOrDefault())
    ? true
    : (g.All(p => !(p.BoolProperty ?? true))
        ? (bool?)false
        : null)
            }).ToList();

我建議以下解決方案。 它需要使用MoreLINQ

List<T1> t1 = t2.GroupBy(g => new
{
    g.property1,
    g.property2
}).Select(groupedItems =>
    {
        // My code starts here
        bool? result = null;
        var bools = groupedItems
            .OrderBy(z => z.BoolProperty)
            .TagFirstLast((z, first, last) => new { z, firstOrLast = first || last })
            .Where(z => z.firstOrLast)
            .Select(z => z.z.BoolProperty).ToList();

        if (bools.Count == 0)
        {
            // Do nothing
        }
        else if (bools.First() == bools.Last())
        {
            result = bools.First();
        }
        // My code ends here

        return new T1
        {
            property1 = groupedItems.Key.property1,
            property2 = groupedItems.Key.property2,
            BoolProperty = result
        };
    }).ToList();

關鍵位是使用OrderBy ,以確保truefalsenull值是相鄰的(即按順序組合在一起)。

然后TagFirstLast允許我們忽略除第一個和最后一個條目之外的所有條目(這是我們所需要的)。

然后我們檢查第一個和最后一個條目是否相同 - 如果是,則使用該值(無論是null還是truefalse - 無論哪種方式符合要求)。

否則,使用null (因為那將是你的else條件)。

把它放在你的代碼中,

List<T1> t1 = t2.GroupBy(g => new
        {
            g.property1,
            g.property2               
        })
        .Select(g => new T1
        {
            property1 = g.Key.property1,
            property2 = g.Key.property2,                
            BoolProperty =  g.GroupBy(grp => grp.BoolProperty).Count() > 1 ? null : g.Select(g_val=>g_val.BoolProperty).First()
        }).ToList();

這一切都可以通過linq完成,但這可能永遠不會是最有效的方式,因為你需要不止一次枚舉或其他shenenigans。 有時最好的方法是簡單的舊代碼。

public static bool? CantFindAgoodName(IEnumerable<bool?> items)
{ 
     bool? check = null;
     bool first = true;
     foreach(var item in items)
     {
         if(first)
         {
            check = item;
            first = false;
         }
         else if(check != item) 
            return null;
     }
     return check;
}

然后就寫

BoolProperty = CantFindAGoodName(g)

如果你真的想用標准的Linq來做,它可以通過一次迭代來完成,但它既不高效(因為它不會立即中斷)也不能超過...

BoolProperty = g.Aggregate(-1,
              (n, b) => n == -1
                ? (b == null ? 0 : (b.Value ? 1 : 2))
                : (n == (b == null ? 0 : (b.Value ? 1 : 2)) ? n : -2), 
              i => i == -2 ? default(bool?) : i == 1);

那么它是怎樣工作的?

-1  = seed
 0  = null
 1  = true
 2  = false
-2  = different values aka null

雖然價值要么是第一個,要么等於它的前身,它會不斷被提供。 因此,如果序列純粹由相同的元素組成,那么它將被轉發。 然而,如果它不同,-2將被轉發,這將總是與剩余的任何其他元素不同。 最后,第二個lambda只是將結果轉換回bool?

您可以使用

var allTrue = t2.All(x=>x.BoolProperty == true);
var allFalse = t2.All(x=>x.BoolProperty == false);
var anyNull = t2.Any(x=>x.BoolProperty.HasValue == false);

在分組之前。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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