C # linq запрос агрегат обнуляемый логический - PullRequest
3 голосов
/ 21 марта 2019

Я хотел бы использовать 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 будет "привет", мир", ноль;Спасибо

Ответы [ 5 ]

3 голосов
/ 21 марта 2019

Как насчет этого?

BoolProperty = g.All(p => p.BoolProperty.GetValueOrDefault())
    ? true
    : (g.All(p => !(p.BoolProperty ?? true))
        ? (bool?)false
        : null)
            }).ToList();
1 голос
/ 21 марта 2019

Это все может быть сделано с помощью linq, но, вероятно, это никогда не будет самым эффективным способом, потому что вам нужно перечислять не раз, или другим шенинганам.Иногда лучшим подходом является простой старый код.

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, это можно сделать за одну итерацию, но егони эффективный (потому что он не ломается сразу), ни beatuiful ...

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 будет перенаправлено, что всегда будет отличаться от любого другого оставшегося элемента.В конце вторая лямбда просто преобразует результат обратно в bool?

1 голос
/ 21 марта 2019

Вставьте это в свой код,

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();
1 голос
/ 21 марта 2019

Я бы предложил следующее решение.Требуется использование 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, чтобы гарантировать, что true, false и nullзначения являются смежными (т. е. сгруппированы по порядку).

Тогда TagFirstLast позволяет нам игнорировать все, кроме первой и последней записей (это все, что нам нужно).

Затем мы проверяемявляются ли первая и последняя запись одинаковыми - если они есть, используйте это значение (будь то null или true или false - в любом случае это соответствует требованиям).

В противном случае используйтеnull (так как это будет ваше else состояние).

0 голосов
/ 21 марта 2019

Вы можете использовать

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);

перед группировкой.

...