Reputation: 1572
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
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
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
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 true
s are occuring.
Upvotes: 1