Подтвердить что ты не робот

Dictionary.FirstOrDefault(), как определить, был ли найден результат

Я (или хотел иметь) код вроде этого:

IDictionary<string,int> dict = new Dictionary<string,int>();
// ... Add some stuff to the dictionary.

// Try to find an entry by value (if multiple, don't care which one).
var entry = dict.FirstOrDefault(e => e.Value == 1);
if ( entry != null ) { 
   // ^^^ above gives a compile error:
   // Operator '!=' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,int>' and '<null>'
}

Я также попытался изменить строку нарушения следующим образом:

if ( entry != default(KeyValuePair<string,int>) ) 

Но это также дает ошибку компиляции:

Operator '!=' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,int>' and 'System.Collections.Generic.KeyValuePair<string,int>'

Что здесь дает?

4b9b3361

Ответ 1

Ответ на Jon будет работать с Dictionary<string, int>, поскольку в словаре не может быть нулевого значения ключа. Однако это не сработало бы с Dictionary<int, string>, так как это не означает нулевое значение ключа... в режиме "сбой" окажется ключ 0.

Два варианта:

Напишите метод TryFirstOrDefault, например:

public static bool TryFirstOrDefault<T>(this IEnumerable<T> source, out T value)
{
    value = default(T);
    using (var iterator = source.GetEnumerator())
    {
        if (iterator.MoveNext())
        {
            value = iterator.Current;
            return true;
        }
        return false;
    }
}

Альтернативно, проецируйте на нулевой тип:

var entry = dict.Where(e => e.Value == 1)
                .Select(e => (KeyValuePair<string,int>?) e)
                .FirstOrDefault();

if (entry != null)
{
    // Use entry.Value, which is the KeyValuePair<string,int>
}

Ответ 2

Сделайте это так:

if ( entry.Key != null )

Дело в том, что метод FirstOrDefault возвращает KeyValuePair<string, int>, который является типом значения, поэтому он никогда не может быть null. Вы должны определить, было ли найдено значение, проверяя, имеет ли значение по умолчанию хотя бы одно из его свойств Key, Value. Key имеет тип string, поэтому проверка того, что для null имеет смысл, учитывая, что словарь не может иметь элемент с ключом null.

Другие подходы, которые вы могли бы использовать:

var entry = dict.Where(e => e.Value == 1)
                .Select(p => (int?)p.Value)
                .FirstOrDefault();

Это отображает результаты в коллекцию нулевых ints, и если она пуста (нет результатов), вы получаете нуль - там вы не можете ошибаться, что для int, что приведет к успешному поиску.

Ответ 3

Независимо от типов ключа и значения, вы можете сделать что-то вроде этого:

    static void Main(string[] args)
    {
        var dict = new Dictionary<int, string>
        {
            {3, "ABC"},
            {7, "HHDHHGKD"}
        };

        bool found = false;
        var entry = dict.FirstOrDefault(d => d.Key == 3 && (found=true));
        if (found)
        {
            Console.WriteLine("found: " + entry.Value);
        }
        else
        {
            Console.WriteLine("not found");
        }
        Console.ReadLine();
    }

Ответ 4

public static TValue FirstOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, Func<KeyValuePair<TKey, TValue>, bool> where)
    {
        foreach (var kv in dictionary)
        {
            if (where(kv))
                return kv.Value;
        }
        return default;
    }

Ответ 5

Самый ясный код, который я думаю, состоит в следующем:

if (dict.ContainsValue(value))
  string key = dict.First(item => item.Value == value).Key;
else
  // do somehing else

Хотя с точки зрения скорости это не приятно, но лучшего решения нет. Это означает, что поиск по словарю будет выполняться с медленным поиском во второй раз. Класс Dictionary следует улучшить, предложив метод bool TryGetKey (value). Это выглядит немного странно - потому что словарь считается использованным в другом направлении - но иногда его неизбежно переводить в обратном направлении.

Ответ 6

для типов значений NULL, просто проверьте.

if ( entry.Value != null ) { 
     //do stuff
} 

для не-nullable типов проверяет значение значения по умолчанию для int, 0.

if ( entry.Value != 0) { 
   //do stuff
} 

Ответ 7

Если честно, приведение объекта или использование оператора select не является необходимым, я бы не стал полагаться на попытку, чтобы исправить проблему.

Так как вы все равно используете Linq, что не так с использованием .Any?

var entry;
if (dict.Any(e => e.Value == 1))
{
    // Entry was found, continue work...
    entry = dict.FirstOrDefault(e => e.Value == 1);
}
else
{
    // Entry was not found.
    entry = -1;
}

Очевидно, поиграйте с ним, чтобы соответствовать вашему решению, но это довольно быстрая проверка, которая останавливается, если он находит элемент в коллекции с этим значением. Таким образом, он не будет проверять все значения, если совпадение было найдено.

Документация MSDN: https://msdn.microsoft.com/en-us/library/bb534972(v=vs.110).aspx

Ответ 8

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

try
{
    var input = _dictionaryOfInputs.First(item => item.Key == interval);
    return input.Value.InvoiceAmount;
}
catch
{
    return 0;
}