Expressingx
Expressingx

Reputation: 1572

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

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.

E.g.

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)

Is this possible in elegant way without doing multiple .Where() for example?

Upvotes: 0

Views: 269

Answers (3)

Expressingx
Expressingx

Reputation: 1572

I've actually made it with reflection, because I don't want to go and add related properties if new one comes

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

Upvotes: 0

Joel Coehoorn
Joel Coehoorn

Reputation: 416059

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:

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:

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.

Upvotes: 2

Fruchtzwerg
Fruchtzwerg

Reputation: 11399

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. But it really depends where the first trues are occuring.

Upvotes: 1

Related Questions