Lambda way to populate value field in ToDictionary () method?

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 the allowed objects.
I want to create IDictionary<MyObject, bool>

. Objects, where the key is the set of MyObjects of allowed objects and bool is true if the MyObject instance was also included in the preferred objects list.

I can do this by listing them and adding them one by one, but I would like to do something like this:

IDictionary<MyObject, bool> selectedObjects = allowedObjects
    .ToDictionary(o => new KeyValuePair<MyObject, bool>()
        { Key = q,
          Value = preferedObjects.Any(q)
        }
     );

      

UPDATE
Exchange changed with anyone; The most recommended solution was what I tried in the first place, but for some reason it is not accepted:

IDictionary<MyObject, bool> selectedObjects = allowedObjects
    .ToDictionary<MyObject, bool>(o => o, preferedObjects.Any(o));

      

Visual studio says the first method does not return bool. This is true, but mainly because bool would not be the correct result to start with ...
And then it says it cannot determine the type of the second lambda ...
As you can see, I tried to explicitly define the types which will help make conclusion, but that doesn't solve anything.

Suggestions?

Disclaimer: names and code are sniffed to keep focus where it should be

+4


a source to share


4 answers


I can't remember if Linq provides the Contains extension method on IEnumerable<T>

. If not, you can use Any:

var dict = allowedObjects.ToDictionary(o => o,
    o => preferredObjects.Contains(o));

      



** Edit ** Yes, just test it and there is indeed an extension method Contains (), so the above code works.

+5


a source


Enumerable.ToDictionary

has several overloads. Some of them take a second delegate (so you can pass in a lambda) to return the value to use with the key.

Sort of:



var secondDict = firstDictionary
                 .Where(p => SomeCondition(p))
                 .ToDictionary(p => p.Key, p => p.Value);

      

+3


a source


You are almost there:

IDictionary<MyObject, bool> selectedObjects = allowedObjects
    .ToDictionary(o => o, o => preferedObjects.Contains(q));

      

An extension method ToDictionary

works by using two lambda expressions to generate a key / value pair, not a type KeyValuePair

.

You can greatly increase the speed by using HashSet<T>

objects for collections, allowedObjects

and preferredObjects

although I wouldn't bother if your lists are particularly significant / performance is important.

+1


a source


You can of course create your own extension method. ToDictionary simply creates a dictionary by adding all the elements in the source as values ​​and using lambda as the key. You can create your own with a different approach. Since you are using "contains" I don't think this is a good idea as it is a slow operation (linear search for each element in allowedObjects, you are listing the preferred objects).

One approach might be to add all preferred objects to the HashSet and use Contains for that. Another could be using a linq query where you join actualObjects with preferredObjects using join and use DefaultIfEmpty. It's 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);
}

      

+1


a source







All Articles