Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda way to fill the value field in the 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 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

like image 954
Boris Callens Avatar asked May 25 '09 08:05

Boris Callens


People also ask

What is ToDictionary?

In LINQ, ToDictionary() Method is used to convert the items of list/collection(IEnumerable<T>) to new dictionary object (Dictionary<TKey,TValue>) and it will optimize the list/collection items by required values only.

What does ToDictionary do C#?

The ToDictionary method is an extension method in C# and converts a collection into Dictionary.

How do you find lambda value?

The formula for calculating lambda is: Lambda = (E1 – E2) / E1. Lambda may range in value from 0.0 to 1.0. Zero indicates that there is nothing to be gained by using the independent variable to predict the dependent variable. In other words, the independent variable does not, in any way, predict the dependent variable.


4 Answers

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.

like image 113
Matt Hamilton Avatar answered Nov 14 '22 22:11

Matt Hamilton


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);
like image 37
Richard Avatar answered Nov 14 '22 21:11

Richard


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.

like image 26
Noldorin Avatar answered Nov 14 '22 22:11

Noldorin


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);
}
like image 22
Frans Bouma Avatar answered Nov 14 '22 21:11

Frans Bouma