Lambda way to fill the value field in the ToDictionary() method?

20,917

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);
}
Share:
20,917

Related videos on Youtube

Boris Callens
Author by

Boris Callens

Senior .net programmer. Belgium(Antwerp) based. linked-in My real email is gmail.

Updated on January 04, 2022

Comments

  • Boris Callens
    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 an IDictionary<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
    Boris Callens almost 15 years
    Bizar, 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
    Boris Callens almost 15 years
    Although it doesn't really answer my question, there is some usefull info in here. Not accepted yet upped :) Thx
  • Matt Hamilton
    Matt Hamilton almost 15 years
    Well 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
    Boris Callens almost 15 years
    Please 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
    Boris Callens almost 15 years
    Because you mentioned it, I just had to code it with hashsets :P Something about premature optimization and the devil.. ;)
  • Boris Callens
    Boris Callens almost 15 years
    I 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
    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
    Jennifer Miles almost 15 years
    Thanks, 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).