简体   繁体   English

将相同对象的列表合并为具有布尔属性 C# 的单个对象

[英]Merge List of identical objects into single one with bool properties C#

I'm really trying to figure it out how to do it and if its possible, but seems I'm stuck.我真的很想弄清楚如何去做,如果可能的话,但似乎我被困住了。

I have some object我有一些 object

public class Obj 
{
    public bool Allow { get; set;}
    public bool Forbidden { get; set; }
    public bool Forgotten { get; set; }
}

And I have List<Obj> , which all of those objects in the list has to be combined in single Obj , where if the property is true in any of those objects should be set to true , else keep it false .我有List<Obj> ,列表中的所有这些对象都必须组合在单个Obj中,如果这些对象中的任何一个属性为true ,则应将其设置为true ,否则保持为false

Eg例如

List<Obj> list = new List<Obj>() 
{
    new Obj() { Allow = false, Forbidden = false, Forgotten = true },
    new Obj() { Allow = true, Forbidden = false, Forgotten = false },
    new Obj() { Allow = false, Forbidden = false, Forgotten = true }
}

In that case I would get new Obj with values Allow = true (in one of the objects its set to True), Forbidden = false, Forgotten = true (there is object with this property set to True as well)在这种情况下,我会得到新的Obj ,其值Allow = true (in one of the objects its set to True), Forbidden = false, Forgotten = true (there is object with this property set to True as well)

Is this possible in elegant way without doing multiple .Where() for example?例如,这是否可能以优雅的方式而不需要执行多个.Where()

This should work:这应该有效:

public Obj CoalesceObjs(IEnumerable<Obj> items)
{
    var result = new Obj();
    foreach(var item in items)
    {
        result.Allow = result.Allow || item.Allow;
        result.Forbidden = result.Forbidden || item.Forbidden;
        result.Forgotten = result.Forgotten || item.Forgotten;

        if (result.Allow && result.Forbidden && result.Fogotten) return result;
    }
    return result;
}

If you really want to use linq, you could also do this:如果你真的想使用 linq,你也可以这样做:

var seed = new Obj(); 
list.Aggregate(seed, (cur, next) => {
    cur.Allow = cur.Allow || next.Allow;
    cur.Forbidden = cur.Forbidden || next.Forbidden;
    cur.Fogotten = cur.Forgotten || next.Forgotten;
    return cur;
});
//seed is now also the result

Which I could again wrap in a function like so:我可以再次将其包装在 function 中,如下所示:

public Obj CoalesceObjs(IEnumerable<Obj> items)
{
    var seed = new Obj(); 
    return list.Aggregate(seed, (cur, next) => {
        cur.Allow = cur.Allow || next.Allow;
        cur.Forbidden = cur.Forbidden || next.Forbidden;
        cur.Fogotten = cur.Forgotten || next.Forgotten;
        return cur;
    });
}

Update: added an early-exit to the first option.更新:在第一个选项中添加了提前退出。

If performance doesn't matter, a really elegant and really easy to read way is like:如果性能无关紧要,那么一种非常优雅且非常易于阅读的方式就像:

Obj result = new Obj()
{
    Allow = list.Any(o => o.Allow),
    Forbidden = list.Any(o => o.Forbidden),
    Forgotten = list.Any(o => o.Forgotten)
};

Note that you are querying your list 3 times, for a potentially more performant solution you could make use of LINQs Aggregate .请注意,您正在查询您的列表 3 次,对于可能更高性能的解决方案,您可以使用 LINQs Aggregate But it really depends where the first true s are occuring.但这真的取决于第一个true s 出现在哪里。

I've actually made it with reflection, because I don't want to go and add related properties if new one comes我实际上是通过反射完成的,因为我不想 go 并在新属性出现时添加相关属性

public static T MergeInSingleObjectBool<T>(this List<T> list)
        where T : new()
    {
        var obj = new T();

        foreach (var item in list)
        {
            var type = item.GetType();
            foreach (var prop in type.GetProperties())
            {
                var name = prop.Name;
                var value = (bool)type.GetProperty(name).GetValue(item);
                if (value)
                {
                    obj.GetType().GetProperty(name).SetValue(obj, value);

                    // we found true for our prop
                    break;
                }
            }
        }

        return obj;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM