【问题标题】:Can't find the appropriate operators for a Linq query找不到适合 Linq 查询的运算符
【发布时间】:2013-01-08 17:09:49
【问题描述】:

我需要帮助来构建 Linq 查询。我有这本词典:

var dict = new Dictionary<string, IDictionary<int, double>>  
{  
    { "one", new Dictionary<int, double>  
        {  
            { 1, 2.0 },  
            { 2, 3.0 }  
        }},  
    { "two", new Dictionary<int, double>  
        {  
            { 1, 3.0 },  
            { 2, 4.0 },  
            { 3, 5.0 }  
        }},  
    { "three", new Dictionary<int, double>  
        {  
            { 1, 4.0 },  
            { 2, 5.0}  
        }}  
    };  

我想选择所有关联值为 3.0 的“字符串”/“整数”元组。使用 Foreach 循环,它看起来像:

var res = new Dictionary<string, int>();
foreach (var elem in dict.Select (d => new { S = d.Key, I = d.Value }))
{
    foreach (var val in elem.I)
    {
        if (val.Value == 3.0)
        {
            res.Add(elem.S, val.Key);
        }
    }
}

我正在尝试对单个 Linq 查询执行相同的操作,但没有成功(我不知道如何将键与子查询中的值“连接”)。你会怎么做呢?

提前谢谢你!

【问题讨论】:

  • 结果数据应该是什么样的?例如{ "one", new int[] { 2 } }{ "one", 2 }

标签: c# linq c#-4.0 linq-to-objects


【解决方案1】:

你可以这样做:

var result = dict
    // flatten to 3-value tuple
    .SelectMany(kvp => kvp.Value
        .Select(kvp2 => new // anonymous object
        {
            OuterKey = kvp.Key, // e.g. "one"
            InnerKey = kvp2.Key, // e.g. 1
            InnerValue = kvp2.Value // e.g. 2.0
        })
    ).Where(o => o.InnerValue == 3.0) // limit to the 3.0 value
    .ToDictionary(o => o.OuterKey, o => o.InnerKey)
    ;

SelectMany 将字典的字典扁平化为如下结构:

{ OuterKey = "one", InnerKey = 1, InnerValue = 2.0 },
{ OuterKey = "one", InnerKey = 2, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 1, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 2, InnerValue = 4.0 },
{ OuterKey = "two", InnerKey = 3, InnerValue = 5.0 },
{ OuterKey = "three", InnerKey = 1, InnerValue = 4.0 },
{ OuterKey = "three", InnerKey = 2, InnerValue = 5.0 }

Where() 将其限制为仅具有 InnerValue = 3.0 的对象:

{ OuterKey = "one", InnerKey = 2, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 1, InnerValue = 3.0 }

ToDictionary() 看起来像这样:

{ "one", 2 },
{ "two", 1 }

如果同一外键下可能有多个 3.0,则可以使用 ToLookup() 代替 ToDictionary()。

【讨论】:

  • 感谢您的解释!我没有想到 SelectMany 中的 Select(kvp2),所以只检索了 InnerKey 和 InnerValue。
  • @GwynnBlied 您的编辑确实提供了更高效的代码,但它使我对 LINQ 查询如何工作的解释无效。部分答案是指导性的。
【解决方案2】:

这是另一种丑陋的做法:

var results = from key1 in dict.Keys
              let valueDict = dict[key1]
              from key2 in valueDict.Keys
              where valueDict[key2] == 3d
              select new { Key1 = key1, Key2 = key2 };

【讨论】:

  • 只需要在末尾添加一个ToDictionary即可。
  • 谢谢:我不习惯用这种方式写查询,但它看起来很简单明了!
【解决方案3】:

这样就可以了:

var  res = dict
        .Where(outer => outer.Value.Any(inner => inner.Value == 3.0))
        .ToDictionary(outer => outer.Key, outer => outer.Value.First(x=>x.Value == 3.0).Key);

或者让代码更通用一点:

var predicate = new Func<KeyValuePair<int, double>, bool>(inner => inner.Value == 3.0); 
var  res = dict
        .Where(outer => outer.Value.Any(inner => predicate(inner)))
        .ToDictionary(outer => outer.Key, outer => outer.Value.First(inner => predicate(inner)).Key);

【讨论】:

  • 这将从内部字典中选择 int 值,而不是从外部字典中选择字符串值。
  • 看起来这需要在最后调用 ToDictionary 以使其与 OP 的代码保持一致。
  • @DStanley 在Where 之后还需要一个Select 来捕获该值。
  • 我把它省略了,因为如果内部字典中有两个相同的键,那将导致 ArgumentException...
  • @Spontifixus DictinctBy 来救援! (如果他们将它包含在 LINQ 本身中;它非常有用)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多