IEnumerable where All meet X and at least one meets y
I have 2 IEnumerable<decimal>
. for example {0,0.1,0.5,1}
and {a,b,c,d}
assume equal lengths
Sample Domain Object Code:
public class Foo //does not implement IEnumerable because nothing outside of itself should treat it as such
{
private readonly decimal _a;
private readonly decimal _b;
private readonly decimal _c;
//another class also has private readonly decimal _d;
public decimal A {get{return _a;}}
//...
public decimal C {get{return _c;}}
}
I want to define Foo1>Foo2
- if all meet
>=
(as inFoo1.A>=Foo2.A && ' .. 'Foo1.C>=Foo2.C
..) - At least one
>
(as inFoo1.B>Foo2.B
)
Sample iterative code:
//DRY violation, but probably the shortest route to the goal
private static IEnumerable<Func<Foo,decimal>> Accessors=
new List{f=>f.A,f=>f.B,f=>f.C};
public static bool operator>(Foo foo1, Foo foo2)
{
if (foo1==null||foo2==null)
return false;
bool foundGreater=false;
foreach (var accessor in _Accessors)
{
if (accessor(foo1)<accessor(foo2))
return false;
if (foundGreater==false&&accessor(foo1)>accessor(foo2))
foundGreater=true;
}
return foundGreater;
}
information and answers involving zip are welcomed from a learning perspective, as are attacking the entire problem of variable property lengths using the same comparison functionality that don't involve reflection.
However, I'm currently working within the confines of .net 2.0 with LinqBridge.
I'm considering something like the following to cover all classes that need the same functionality
//Needs a better name for sure
public static bool AllWithAny<T,TValue>(IEnumerable<Func<T,TValue>> accessors,T item1, T item2,
Func<TValue,TValue,bool> shortCircuitBreak,Func<TValue,TValue,bool> atLeastOneCondition)
{
GuardStrategy.ThrowIfNull(accessors,"accessors");
GuardStrategy.ThrowIfNull(item1, "item1");
GuardStrategy.ThrowIfNull(item2, "item2");
var foundOne=false;
foreach(var accessor in accessors)
{
var values=new {Value1=accessor(item1),Value2=accessor(item2)};
if (shortCircuitBreak(values.Value1, values.Value2))
return false;
if(foundOne==false && atLeastOneCondition(values.Value1,values.Value2))
{
foundO开发者_如何学Gone=true;
}
}
return foundOne;
}
The Question(s):
Is there an existing Linq keyword/operator combination that will do all this more gracefully? Is there a more elegant/simpler way to do this type of comparison that is better DRY, less coding, and more reuse?
This would work (using @gaeron's idea to expose IEnumerable
of values to ease access)
public static bool operator >(Foo foo1, Foo foo2)
{
if (foo1 == null || foo2 == null)
return false;
var zipSeq = foo1.Values.Zip(foo2.Values, (a, b) => a - b);
bool isGreater = zipSeq.All(x => x >= 0) && zipSeq.Any(x => x > 0);
return isGreater;
}
The basic idea is that for foo1
to be greater than foo2
, the subtraction of each element in foo1.Values
with the corresponding one in foo2.Values
must be >=0 AND there must at least be one entry that is > 0.
Haven't tested but with little modifications it should work.
public class Foo {
public decimal A { get; set; }
public decimal B { get; set; }
public decimal C { get; set; }
IEnumerable<decimal> Values {
get { return new [] { A, B, C }; }
}
public static bool operator > (Foo x, Foo y)
{
var pairs = x.Values.Zip (y.Values,
(xv, yv) => Tuple.Create (xv, yv));
return pairs.All (pair => pair.Item1 >= pair.Item2)
&& pairs.Any (pair => pair.Item1 > pair.Item2);
}
}
P.S. I didn't quite understand your question so I just implemented what you asked in the beginning.
精彩评论