Linq, using ToLookup to project values to different named variables
var tmpProjection = myCollection.ToLookup(t => t.SomeBoolValue);
var listOneFinal = tmpProjection[true];
var listTwo = tmpProjection[false];
First question, is there a way to assign it to listOne and listTwo in some shorter way, I know I'm being pedantic here, ... just asking.
Now,
var listThree = listTwo.ToLookup(t => t.SomeOtherBoolValue);
var listFourFinal = listThree[false];
var listFiveFinal = listThree[true];
So in thise case, I just need (ultimately) listOneFinal, listFourFinal and listFiveFinal -- but i'm creating thi开发者_JAVA百科s temp stuff in between ... is there a way to reduce this.
i'm only talk code-wise not performance or code criticality.
bool is kind of weak for communicating intent. Int is a little better and enum would be best.
Lookup<int, T> myLookup = myCollection
.ToLookup(t =>
t.someBoolValue ? 1 :
t.someOtherBoolValue ? 4 :
5
);
var listOne = myLookup[1];
var listFour = myLookup[4];
var listFive = myLookup[5];
You can do it in fewer statements, but since you need to end op with 3 values, you need at least 3 assignments. Your code is very readable, don't sacrifice readability for "being smart" and reducing to fewer statements. That being said, here is a 3 - statement version; that will work well if collections are small (your own version will perform better with larger collections, since this version iterates multiple times through the collection):
var listOneFinal = myCollection.Where(t => t.SomeBoolValue);
var listFourFinal = myCollection.Where(t => !t.SomeBoolValue && !t.SomeOtherBoolValue);
var listFiveFinal = myCollection.Where(t => !t.SomeBoolValue && t.SomeOtherBoolValue);
Depending on your real usage scenario, the above might be more readable.
I think what you've got there is pretty good already, to be honest.
If you're simply looking to minimize the number of statements, I doubt you could do better than:
var listOneFinal = myCollection.Where(t => t.SomeBoolValue);
var listFourFinal = myCollection.Where(t => !t.SomeBoolValue && !t.SomeOtherBoolValue);
var listFiveFinal = myCollection.Where(t => !t.SomeBoolValue && t.SomeOtherBoolValue);
Or perhaps:
var predicates = new Func<MyClass,bool>[]{ t => t.SomeBoolValue, t => t.SomeOtherBoolValue};
var listOneFinal = myCollection.Where(predicates.First());
var listFourFinal = myCollection.Where(t => !predicates.Any(p => p(t)));
var listFiveFinal = myCollection.Where(t => !predicates[0](t) && predicates[1](t));
(Call ToList()
on each query if desired)
But really, I prefer your technique much better, the code I have provided is not particularly more readable or efficient.
You might want to consider just storing the 2 lookups instead of each list and inline each 'final lookup' where necessary since it's cheap to call Lookup[key]
. So whenever you need listFourFinal
, just call listThree[false].
Better variable names would help, obviously.
If you find yourself doing this often, you can write a function to do it. For a boolean ToLookup
, we can use C#'s out parameters to return multiple values.
public static void Dichotomize<T>(this IEnumerable<T> source,
Func<T,bool> keySelector,
out IEnumerable<T> affirmative,
out IEnumerable<T> negative) {
if (source == null) throw new ArgumentNullException("source");
if (keySelector == null) throw new ArgumentNullException("keySelector");
var affirmativeList = new List<T>();
var negativeList = new List<T>();
foreach (var element in source) {
(keySelector(element) ? affirmativeList : negativeList).Add(element);
}
affirmative = affirmativeList.AsReadOnly();
negative = negativeList.AsReadOnly();
}
Now we can do:
IEnumerable<T> listOneFinal, listTwo, listFourFinal, listFiveFinal;
myCollection.Dichotomize(t => t.SomeBoolValue, out listOneFinal, out listTwo);
listTwo.Dichotomize(t => t.SomeOtherBoolValue, out listFiveFinal, out listFourFinal);
精彩评论