【问题标题】:Extracting a Dictionary<TKey, double> from a Dictionary<TKey, string> instance从 Dictionary<TKey, string> 实例中提取 Dictionary<TKey, double>
【发布时间】:2012-01-26 15:32:53
【问题描述】:

我有一个通用字典,其中 TValue 的类型为 String (Dictionary&lt;int, string&gt;)。我选择使用字符串作为值类型,因为数据是从 Xml 文件中加载的,其中源值可以是字符或数字数据类型(我想 Object 也是可以接受的 TValue 类型,但即便如此这个问题将同样适用)。

字符数据类型也很重要,因此不能完全排除它们。

我想提取这个Dictionary&lt;int, double&gt; 的一个子集。换句话说,我想要值是数字的字典子集。

现在我正在这样做:

Dictionary<int, string> myDictionary;
// Do some loading.
var numericData = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp)       

这种方法非常丑陋,没有得到Dictionary&lt;int, double&gt; 的结果,谁能提供其他改进方法?

谢谢!

【问题讨论】:

  • 除了少了分号,我一点都不觉得难看。你有什么问题?
  • @RedFilter:我有一个正确性问题 - 那是行不通的 :)
  • 你的代码会在运行时惨遭轰炸。您不能将IEnumerable&lt;KeyValuePair&lt;int, string&gt;&gt; 转换为Dictionary&lt;int, double&gt;,即使您已限制为Value 可解析为double 的那些对。您至少需要致电ToDictionary(kvp =&gt; kvp.Key, kvp =&gt; Double.Parse(kvp.Value))
  • @JonSkeet 我觉得演员阵容看起来很有趣,但他说他目前正在使用它:)。我很懒。
  • 我意识到我在发布后设置了 InvalidCastException。将进行修改,以使问题的实际焦点不会被该错误分开。谢谢。

标签: c# .net linq generics


【解决方案1】:

您提供的代码不仅丑陋 - 它会在执行时以 InvalidCastException 失败。我怀疑你真的想要:

var numericData = myDictionary
        .Select(kvp => {
                    double value;
                    return new { kvp.Key,
                        Value = double.TryParse(kvp.Value, out value) 
                                   ? value : (double?) null
                    };
               })
        .Where(pair => pair.Value != null)
        .ToDictionary(pair => pair.Key, pair => pair.Value.Value);

是的,这很丑 - 但是:

  • 避免多次解析值
  • 避免在查询中产生副作用

如果你愿意解析两次,你可以让它更简洁但效率更低:

var numericData = myDictionary
       .Where(kvp => { double tmp; return double.TryParse(kvp.Value, out tmp); })
       .ToDictionary(pair => pair.Key, pair => double.Parse(pair.Value));

或者(更简洁)你可以创建一个单独的方法:

public static double? TryParseNullableDouble(string text)
{
    double value;
    return double.TryParse(text, out value) ? value : (double?) null;
}

那么第一个版本就变成了:

var numericData = myDictionary
        .Select(kvp => new { kvp.Key, TryParseNullableDouble(kvp.Value) })
        .Where(pair => pair.Value != null)
        .ToDictionary(pair => pair.Key, pair => pair.Value.Value);

【讨论】:

  • 但不是在与 TryParse (v2) 相同的版本中引入了可为空值吗?
  • @vc74: 是的...但他们可能是在 BCL 团队写了TryParse...之后才出现的。
  • 在编写第一种方法时,您可能已经在考虑第二种方法,因为它不像所写的那样工作。您必须将 : (double?) null 附加到第 5 行的末尾(就像您在 TryParseNullableDouble 方法中所做的那样)。除此之外它很棒。谢谢。
【解决方案2】:

您可以使用包含双精度值的temp 变量创建一个新字典 - 这利用了枚举和添加到字典是逐项完成的事实,因此 temp 包含正确的双精度值:

double temp = 0;
var numDict = myDictionary.Where(kvp => Double.TryParse(kvp.Value, out temp))
                          .ToDictionary( x=> x.Key, x=> temp);

【讨论】:

  • 只有 temp 而不是 x=&gt; temp 的项目值 lambda - 已修复