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>'
Что здесь дает?
Ответы
Ответ 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;
}