C# Предикат поиска по словарю в качестве аргумента метода

Я пытаюсь создать функцию, с помощью которой я могу передать функтор/предикат, который может вставляться в метод «Где» словаря.

(cardPool - это словарь типа "cardStats") Псевдо того, что я хотел бы сделать:

void CardStats findCard(Predicate<CardStats> pred)
{
    return cardPool.Where(pred);
}

Этот код, очевидно, не будет работать, но это просто грубый пример функциональности, которую я ищу. У меня не было проблем с настройкой этого для списков, но для словаря это действительно поставило меня в тупик.

Любая помощь будет здорово, спасибо!

Редактировать: Ах, извините, я должен был упомянуть больше: Cardstats - это значение, ключ имеет тип int. Я хотел бы просмотреть значения (cardStats) и проверить их свойства, такие как идентификатор (int) или имя (строка).


person Dahlvash    schedule 23.12.2014    source источник
comment
Словари имеют два параметра универсального типа (тип ключа и тип значения). В вашем словаре не может быть только один тип.   -  person JLRishe    schedule 24.12.2014
comment
Хорошо, CardStats тип значения или тип ключа? Похоже, вы можете просто использовать свойство Values или Keys, а затем использовать Where как обычно...   -  person Jon Skeet    schedule 24.12.2014


Ответы (2)


Dictionary<TKey, TValue> реализует IEnumerable<KeyValuePair<TKey, TValue>>, поэтому его метод расширения Where принимает предикат типа Func<KeyValuePair<TKey, TValue>, bool>.

Вы можете реализовать свой метод следующим образом:

void CardStats findCard(Func<int, CardStats, bool> pred)
{
    return cardPool.Where(kv => pred(kv.Key, kv.Value))
                   .Select(kv => kv.Value)
                   .FirstOrDefault();
}

И используйте это так:

CardStats stats = myCards.findCard((id, stats) => id == 7);

or

CardStats stats = myCards.findCard((id, stats) => stats.Name == "Ace of Clubs");

Обратите внимание, что использование Where в словаре не использует возможности быстрого поиска словаря и в основном рассматривает его как линейный набор пар ключ-значение.


Еще один комментарий: я бы предложил предоставить метод, который возвращает IEnumerable найденных карт, если их несколько. Или вы можете предоставить тот, который делает это, и тот, который просто возвращает первое совпадение:

void IEnumerable<CardStats> findCards(Func<int, CardStats, bool> pred)
{
    return cardPool.Where(kv => pred(kv.Key, kv.Value))
                   .Select(kv => kv.Value);
}

void CardStats findCard(Func<int, CardStats, bool> pred)
{
    return findCards(pred).FirstOrDefault();
}
person JLRishe    schedule 23.12.2014
comment
Ах, извините, я должен был упомянуть больше: Cardstats - это значение, ключ имеет тип int. Я хотел бы просмотреть значения (cardStats) и проверить их свойства, такие как идентификатор (int) или имя (строка). - person Dahlvash; 24.12.2014
comment
Ключ-значение важно искать. Производительность нужно рассматривать в большом словаре. - person Chaturvedi Dewashish; 24.12.2014
comment
Вы также можете сделать cardPool.Where(pred).ToList(), чтобы получить все значения, соответствующие предварительному - person Chaturvedi Dewashish; 24.12.2014
comment
@ChaturvediDewashish Вы не можете напрямую передать Func<int, CardStats, bool> методу Where(), потому что он ожидает Func<KeyValuePair<int, CardStats>, bool>. Я решил использовать первый вариант в сигнатуре метода, потому что он позволяет писать лямбда-выражения типа (id, stat) => id == 5 вместо kv => kv.Key == 5, что менее ясно по своему назначению. - person JLRishe; 24.12.2014
comment
@JLRishe Спасибо. Хорошо поймал. На самом деле я завернул его как Expression‹Func‹CardStats, bool››. Это помогло мне построить сложные предикаты. stackoverflow.com/questions/11490893/ - person Chaturvedi Dewashish; 24.12.2014
comment
Спасибо, JLRishe! Просто дал ему вихрь, и он делает именно то, что я искал. - person Dahlvash; 24.12.2014

Я бы использовал FirstOrDefault в качестве первого оператора, потому что он остановится, как только найдет соответствующий элемент. Другое дело, что я рассмотрю возможность использования чего-то другого, кроме словаря, потому что при таком использовании это злоупотребление, если его индексированная цель.

в любом случае, это код, который я буду использовать:

public CardStats Find(Func<CardStats, bool> predicate)
{
    KeyValuePair<int, Roster> kvCard = cardPool.FirstOrDefault(kvp => predicate(kvp.Value));
    if (kvCard.Equals(default(KeyValuePair<int, Roster>)))
        return null;
    return kvCard.Value;
}
person EladTal    schedule 19.04.2020