寻找最小值时避免使用幻数

时间:2019-05-18 17:09:10

标签: c# linq

我需要按键在词典列表中找到min。这是我的方法: 公共int GetMin(字符串键,List > db)         {             如果(db == null || db.Count == 0)             {                 返回0;             }             int结果= Int32.MaxValue;             foreach(db中的var dic)             {                 如果(dic.TryGetValue(key,out int value)&& result> value)                 {                     结果=值;                 }             }             返回结果== Int32.MaxValue? 0:结果;         } 这不是理想的,因为它使用Int32.MaxValue作为基准。 这是我正在使用的词典的测试列表。 var db =新的List >()             {                 新的Dictionary (){{“ a”,1},{“ b”,5},{“ c”,3}},                 新的Dictionary (){{“ d”,5},{“ b”,3}},                 new Dictionary (){{“ a”,2},{“ c”,2}},             }; 预期产量: “ a”则结果为1,如果“ b”则为3,如果“ e”则为0,如果“ c”则为2 无论如何,有没有做得更好,更有效的方法? 我也尝试使用Linq进行此操作,但是看起来有点难看。也许更简单的方法可以使用linq做到这一点?   var result = db.SelectMany(d => d)?.哪里(kvp => kvp.Key == key)?. OrderBy(x => x.Value).FirstOrDefault()。Value;             返回结果0;

5 个答案:

答案 0 :(得分:3)

一种避免使用Int32.MaxValue作为基准并且避免在未找到结果的情况下返回0的方法是使用Nullable<int>或速记int?

public static int? GetMin(string key, List<Dictionary<string, int>> db)
{
    int? result = null;
    if (db != null) {
        foreach (var dic in db) {
            if (dic.TryGetValue(key, out int value) && (result == null || value < result)) {
                result = value;
            }
        }
    }
    return result;
}

如果返回null,则表示未找到匹配项。如果您更喜欢使用int作为返回类型并在这种情况下返回0,请写。

return result ?? 0;

在方法内部使用可为null的int仍然有用,因为它使我们避免滥用Int32.MaxValue。现在Int32.MaxValue可以是有效的输入和结果。

循环没有错。并非所有内容都需要使用LINQ。如果要使用LINQ,请确保使用Min重载与可为null配合使用。非空变量会为空输入引发异常。枚举字典(使用SelectMany)的LINQ变体没有利用快速字典查找的优点,并且效率不高。

public static int? GetMinLINQ(string key, List<Dictionary<string, int>> db)
{
    if (db == null) {
        return null;
    }
    return db
        .Select(d => (isMatch: d.TryGetValue(key, out int i), result:i))
        .Where(x => x.isMatch)
        .Select(x => (int?)x.result)
        .Min();
}

它将TryGetValue的返回值和out变量组合成一个值元组。但是最终的方法并不比使用循环的方法短。

答案 1 :(得分:0)

您可以编写一个函数,以检查key在词典中是否可用。

如果字典中包含键,则从中返回Min,如果不存在则返回0

实施:

   public static int GetMinFromDictionaryList(List<Dictionary<string, int>> parameter, string key)
    {
        var keyValuePairList = parameter.SelectMany(d => d).Select(x => x.Key);
        if (keyValuePairList.Contains(key))
        {
            return parameter.SelectMany(d => d).Where(x => x.Key == key).ToList().Min(x => x.Value);
        }
     return 0;
    }

POC:.NetFiddle

答案 2 :(得分:0)

我不明白问题出在哪里,这应该很简单。

请参见以下示例。

public static void Main()
{
   var db = new List<Dictionary<string, int>>()
        {
            new Dictionary<string, int>(){ {"a", 1 }, { "b", 5 }, { "c", 3 } },
            new Dictionary<string, int>(){ {"d", 5 }, { "b", 3 } },
            new Dictionary<string, int>(){ {"a", 2 }, { "c", 2 } },
        };
    // Result you will get is 1
    Console.Write(GetMin(db, "a").ToString());

}

public static int GetMin(List<Dictionary<string, int>> db, string key)
{
        return db == null || !db.Any(x=> x.ContainsKey(key)) ? 0 :
        db.SelectMany(x=> x).Where(x=> x.Key == key).Select(x=> x.Value).Min();

}

答案 3 :(得分:0)

由于LINQ查询中的唯一问题是在集合为空(未找到匹配键,因此引发异常)时评估最小值,因此将DefaultIfEmpty放在 {{1} } 允许在 Min() 过滤器未返回任何元素时简单地返回0

不需要使用可为空的变量或预先过滤:

Where()

答案 4 :(得分:0)

您可以将其编写为LINQ方法的一个序列。

因此,您有一个字符串key和一系列相似的字典,每个字典都是一个Dictionary<string, int>。并且您想要字典键等于“键”的最低整数值。如果所有项目都不包含Key,则要返回零。

很幸运,Dictionary<string, int>实现了IEnumerable<KeyValuePair<string, int>>,因此您可以使用SelectManyWhereSelectAggregate

string key = ...
List<Dictionary<string, int>> myListOfDictionaries = ...
// note: this implements IEnumerable<IEnumerable<KeyValuePair<string, int>>>
// so a sequence of sequences of KeyValuePairs

var smallestIntThatHasKey = myListOfDictionaries

    // make one list containing all keyValuePairs
    .SelectMany(dictionary => dictionary)
    // result: one sequence of KeyValuePairs

    // keep only those KeyValuePairs where the Key equals key
    .Where(keyValuePair => keyValuePair.Key == key)

从结果序列中,我想要一个值最小的序列。我可以按降序对所有值进行排序,并采用第一个值:

.Select(keyValuePair => keyValuePair.Value)
.OrderBy(value => value)
.FirstOrDefault();

如果没有带钥匙的物品,这将正确返回零。这也不是一个非常聪明的解决方案:如果您按排序顺序找到了第一个元素(最小的),那么,如果仅使用第一个,那么为什么还要对第二个,第三个等进行排序。更好地使用Aggregate

.Select(keyValuePair => keyValuePair.Value)
.Aggregate(0,(minValue, enumeratedValue) =>
    (enumeratedValue < minValue) ? enumeratedValue : minValue);

这将使汇总结果的种子为零。它将只传递一个序列:它将检查带有枚举值的minValue并将最小的一个保留为汇总值