【问题标题】:C# LINQ query a List<Dicitonary<string, object>>C# LINQ 查询 List<Dictionary<string, object>>
【发布时间】:2014-05-01 14:49:22
【问题描述】:

我有一个List&lt;Dictionary&lt;string, object&gt;&gt; Dictionary&lt;string, object&gt; 就像:

我必须使用Dictionary&lt;string, object&gt;,因为我从 API 检索不同的对象并且指定每个对象是不可行的。

{
    [id, 12345],
    [action, SUBMIT],
    [comment, blah blah]
    ...
},
{
    [id, 98765],
    [action, DECLINE],
    [comment, yak yak]
    ...
}

'id' 总是一个整数。

如何使用 LINQ 从 List 中查询最大 id?在这种情况下返回 id=98765。

【问题讨论】:

  • 您自己尝试过吗?有什么尝试吗?
  • 我使用过几次 LINQ,但不熟悉如何查询 Dictionary 项目,并且在 Google 上找不到任何关于如何执行此操作的信息。
  • 我强烈建议将这些字典转换为实际对象,使用属性而不是键和这些属性的值而不是键对中的值。如果你真的必须这样做,那很好,但如果你可以避免它(你通常可以),你真的应该这样做。

标签: c# linq list dictionary


【解决方案1】:

让我们把问题分解成几个部分:

  1. 我需要将每个字典中的 ID 条目提取到一个集合中。
  2. 我需要将值转换为整数。
  3. 我需要获取该集合的最大值。

我们如何通过键访问字典条目的值? myDictionary["key"]。这很脆弱,因为它假设字典总是有一个带有该键的条目。如果没有,它会抛出异常。

我们如何使用 LINQ 从现有序列中生成新的对象序列? Select()。描述源中每个对象的输出对象。

我们使用int.Parse 从字符串中获取一个int。这很脆弱,通常是禁忌,因为如果该字符串值不是完美的整数,它将引发异常。但为简单起见,假设我们可以假设所有字符串都将始终是整数,就像你说的那样。

最后一点是最简单的 - Max() 适用于任何可比较项目的序列:

var maxId = theList.Select(dict => int.Parse(dict["id"].ToString())).Max();

现在更安全的版本将确保做两件事:

  1. 检查字典是否真的有一个“id”条目,如果没有,决定如何处理。
  2. 检查条目值是否真的可以转换为整数,如果不能,决定如何处理。

这是一种跳过不满足这两个要求的条目的方法。如果没有通过的条目,DefaultIfEmpty(-1) 会生成一个包含单个条目 -1 的序列。

int tryParse;
var onlyEntriesWithIdThatAreIntegers = theList.Where(
    dict => dict.ContainsKey("id")
        && dict["id"] != null
        && int.TryParse(dict["id"].ToString(), out tryParse))
    .DefaultIfEmpty(-1);

var max = onlyEntriesWithIdThatAreIntegers.Select(
    dict => int.Parse(dict["id"].ToString())).Max();

【讨论】:

  • 谢谢大家的回答。我只选择这个作为正确答案,因为它分解了示例,解释了查询的各个部分(非常有帮助!)。顺便说一句,我不得不使用 dict["id"].ToString() 因为它是一个对象(以防其他人遇到这个答案)。
  • @BrianKE 很高兴你发现它有帮助。如果您想接受此答案,请单击左侧投票按钮下方的复选标记。
  • 我试图接受,但显然必须等待 10 分钟。你只需要等待,这就是对你这么快又好老师的惩罚! :)
  • 顺便说一句,我如何将“@Rex M”添加到评论中(就像你对@BrianKE 所做的那样),它有什么作用?
  • @BrianKE 输入'@'和名字的前几个字母,它应该会自动完成。它必须是评论中的第一个字符。它的主要作用是确保您收到全站范围的警报,告知您有评论。
【解决方案2】:

这可能就是你要找的东西:

    var result = youList.Select(p=>int.Parse(p["id"])).Max();

【讨论】:

    【解决方案3】:

    就个人而言,我同意格兰特的观点。您应该使用对象而不是字典来表示您的对象。

    var values = new List<Dictionary<string, object>>();
    ... populate the list ...
    
    int maxIdValue = values.Where(v => v.Keys.Contains("id")).Select(v => (int) v["id"]).Max();
    

    您应该检查以确保字典包含 ID 属性,否则您将收到异常。此外,您应该真正使用不区分大小写的字典来执行您正在做的事情。比较速度更快,您不必担心 ("id, Id, ID") 对于您的目的来说都是一样的。此外,Int.Parse 很慢。如果对象是一个 Int,只需将其强制转换,bo 需要将其转换为字符串,然后对其进行解析以将其转换回原来的类型!但是,如果您必须使用 int.Parse,因为不能保证这些值是 int,那么请使用 TryParse。否则,再次出现未处理的异常

    编辑:

    如果你想使用 tryParse,这里有一个例子:

            var intValues = values.Where(v => v.Keys.Contains("id")).Select(v =>
            {
                int val;
                if (int.TryParse(v["id"].ToString(), out val))
                {
                    return val;
                }
                return int.MinValue;
            }).Where(v => v > int.MinValue);
    
            int? maxIdValue = null;
            if (intValues.Any())
                maxIdValue = intValues.Max();
    

    【讨论】:

    • 我同意使用 TryParse,但无法弄清楚如何在这种情况下应用它。 TryParse 返回一个布尔值,我不确定如何设置输出值,以便 LINQ 查询可以访问它。有什么想法吗?
    • 谢谢,没有意识到您可以使用 {} 标注。
    • 我再次修改它,以确保如果没有可解析的整数,您不会得到序列不包含元素错误。
    猜你喜欢
    • 1970-01-01
    • 2018-01-25
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    • 2017-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多