Lambda way to fill the value field in the ToDictionary() method?
Solution 1
I can't remember whether Linq provides a "Contains" extension method on IEnumerable<T>
. If not, you could use Any:
var dict = allowedObjects.ToDictionary(o => o,
o => preferredObjects.Contains(o));
** Edit ** Yep, just tested it and there is indeed a Contains() extension method, so the above code works.
Solution 2
Enumerable.ToDictionary
has multiple overloads. Some of these take a second delegate (so you can pass a lambda) to return the value to go with the key.
Something like:
var secondDict = firstDictionary
.Where(p => SomeCondition(p))
.ToDictionary(p => p.Key, p => p.Value);
Solution 3
You're almost there:
IDictionary<MyObject, bool> selectedObjects = allowedObjects
.ToDictionary(o => o, o => preferedObjects.Contains(q));
The ToDictionary
extension method works around using two lambda expressions to generate the key/value pair rather than the KeyValuePair
type.
You could speed things dramatically by using HashSet<T>
objects for your allowedObjects
and preferredObjects
collections, though I wouldn't bother unless your lists are especially large/performance is important.
Solution 4
You can of course create your own extension method. ToDictionary simply creates a dictionary by adding all elements in the source as values and using a lambda as key. You can create your own with a different approach. As you use 'contains' I don't think it's a good idea to do it that way, as it is a slow operation (linear search, for every element in allowedObjects, you're enumerating preferredobjects).
One approach could be to add all preferredobjects to a HashSet and use Contains on that. Another could be to use a linq query where you join actualObjects with preferredObjects, using join into, and use DefaultIfEmpty. This is more efficient:
List<MyObject> allowedObjects = new List<MyObject>() { new MyObject("one"), new MyObject("two"), new MyObject("three")};
List<MyObject> preferredObjects = allowedObjects.Take(2).ToList();
var q = from a in allowedObjects
join p in preferredObjects on a equals p into ap
from x in ap.DefaultIfEmpty()
let isPreferred = (x != null)
let toUse = x ?? a
select new { toUse, isPreferred };
Dictionary<MyObject, bool> selectedObjects = new Dictionary<MyObject, bool>();
foreach(var v in q)
{
selectedObjects.Add(v.toUse, v.isPreferred);
Console.WriteLine("{0}, {1}", v.toUse.Foo, v.isPreferred);
}
Related videos on Youtube
Boris Callens
Senior .net programmer. Belgium(Antwerp) based. linked-in My real email is gmail.
Updated on January 04, 2022Comments
-
Boris Callens over 2 years
I have two IEnumerable
IEnumerable<MyObject> allowedObjects = MyService.GetAllowedObjects(); IEnumerable<MyOBject> preferedObjects = MyService.GetPreferedObjects();
We can safely assume that preferedObjects will always be a subset of allowedObjects.
I want to create anIDictionary<MyObject, bool>
. Objects where the key is the set of MyObjects from the allowedObjects and the bool is true if the MyObject instance was also in the preferedObjects enumerable.I can do this by enumerating them and adding them one by one, but I would like to be able to do something like this:
IDictionary<MyObject, bool> selectedObjects = allowedObjects .ToDictionary(o => new KeyValuePair<MyObject, bool>() { Key = q, Value = preferedObjects.Any(q) } );
UPDATE
Exchanged Contains with Any; The solution that is suggested most was what I tried first, but it isn't accepted for some reason:IDictionary<MyObject, bool> selectedObjects = allowedObjects .ToDictionary<MyObject, bool>(o => o, preferedObjects.Any(o));
Visual studio says the first method does not return a bool. Which is true, but mainly because a bool would not be the correct result to start with...
And then it says it cannot infere the type of the second lambda...
As you can see I tried to explicitly define the types to help the inference, but it doesn't solve a thing..Suggestions?
Disclaimer: names and code are snippyfied to keep the focus where it should be
-
Boris Callens almost 15 yearsBizar, here this syntax seems not to work: My real code: Dictionary<Quality, bool> selectedQualities = allowedQualities .ToDictionary<Quality, bool>(k => k, v => preferedQualities.Any(v)); With the first q VS says it cannot convert my Quality to a bool; The second lambda (v) it says it cannot infer the type...
-
Boris Callens almost 15 yearsAlthough it doesn't really answer my question, there is some usefull info in here. Not accepted yet upped :) Thx
-
Matt Hamilton almost 15 yearsWell that's odd. I tested with a couple of IEnumerable<object> variables, so perhaps your use of the Quality class confuses the compiler.
-
Boris Callens almost 15 yearsPlease see OP for more info on the problem with your suggestion. The lists are both rather small and come from a service that returns IEnumerables, so I can indeed pour them in a HashSet, but I don't think this will really be the method that's gonna slow my code down ;)
-
Boris Callens almost 15 yearsBecause you mentioned it, I just had to code it with hashsets :P Something about premature optimization and the devil.. ;)
-
Boris Callens almost 15 yearsI guess so, but I fear it goes beyond the scope (and my agreement of non-disclosure) to find what exactly is the problem then..
-
Noldorin almost 15 years@Boris: Yeah, never optimise prematurely, really. Leaving them as plain IEnumerables isn't a problem if they are small, as you say.
-
Jennifer Miles almost 15 yearsThanks, although I think it does answer your question, or better: it answers the underlying question: how to get the dictionary you want ;). (as the goal is IMHO more important here than how to get there).