Лямбда способ заполнить поле значения в методе ToDictionary()?

У меня есть два

IEnumerable<MyObject> allowedObjects = MyService.GetAllowedObjects();
IEnumerable<MyOBject> preferedObjects = MyService.GetPreferedObjects();

Мы можем с уверенностью предположить, что PreredObjects всегда будет подмножеством позволенных объектов.
Я хочу создать IDictionary<MyObject, bool>, Объекты, ключом которых является набор MyObjects из allowObjects, а bool имеет значение true, если экземпляр MyObject также был в перечисляемом PreredObjects.

Я могу сделать это, перечисляя их и добавляя их один за другим, но я хотел бы иметь возможность сделать что-то вроде этого:

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

ОБНОВИТЬ
Обменяется с любым; Решение, которое предлагается чаще всего, было тем, что я попробовал первым, но по какой-то причине оно не принято:

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

Visual studio говорит, что первый метод не возвращает бул. Что верно, но главным образом потому, что bool не будет правильным результатом для начала...
И тогда это говорит, что это не может вывести тип второй лямбды...
Как вы можете видеть, я попытался явно определить типы, чтобы помочь выводу, но это ничего не решает..

Предложения?

Отказ от ответственности: имена и код сняты, чтобы держать фокус там, где он должен быть

4 ответа

Решение

Я не могу вспомнить, предоставляет ли Linq метод расширения "Contains" IEnumerable<T>, Если нет, вы можете использовать Any:

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

** Edit ** Да, только что проверил его, и действительно есть метод расширения Contains(), поэтому приведенный выше код работает.

Enumerable.ToDictionary имеет несколько перегрузок. Некоторые из них требуют второго делегата (чтобы вы могли передать лямбду), чтобы вернуть значение, соответствующее ключу.

Что-то вроде:

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

Ты почти там:

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

ToDictionary Метод расширения работает вокруг использования двух лямбда-выражений для генерации пары ключ / значение, а не KeyValuePair тип.

Вы можете резко ускорить процесс, используя HashSet<T> объекты для вашего allowedObjects а также preferredObjects коллекции, хотя я бы не стал беспокоиться, если бы ваши списки не были особенно большими / важна производительность.

Конечно, вы можете создать свой собственный метод расширения. ToDictionary просто создает словарь, добавляя все элементы в источнике в качестве значений и используя лямбду в качестве ключа. Вы можете создать свой собственный с другим подходом. Так как вы используете "содержит", я не думаю, что это хорошая идея, так как это медленная операция (линейный поиск, для каждого элемента в allowObjects вы перечисляете предпочитаемые объекты).

Один из подходов может состоять в том, чтобы добавить все предпочтительные объекты в HashSet и использовать для этого Contains. Другим может быть использование запроса linq, в котором вы объединяете фактические объекты с предпочтительными объектами, используя соединение с и используете DefaultIfEmpty. Это более эффективно:

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);
}
Другие вопросы по тегам