Linq to Dictionary<string, List<string>> 与 Null 值

Posted

技术标签:

【中文标题】Linq to Dictionary<string, List<string>> 与 Null 值【英文标题】:Linq to Dictionary<string, List<string>> with Null values 【发布时间】:2019-11-27 10:33:40 【问题描述】:

我确实有一个Culture 列表和一个翻译列表:

public class Translation

    public string key  get; set; 
    public string value  get; set; 
    public string cultureId  get; set; 


public async Task<IActionResult> ReactGetResources(string module = "shared")

    string[] Languages =  "en-US", "fr-FR", "it-IT", "es-ES" ;

    List<Translation> Translation = new List<Translation>();
    Translation.Add(new Translation  key = "hello", value = "Hello", cultureId = "en-US" );
    Translation.Add(new Translation  key = "hello", value = "Bonjour", cultureId = "fr-FR" );
    Translation.Add(new Translation  key = "hello", value = "Buongiorno", cultureId = "it-IT" );
    Translation.Add(new Translation  key = "goodbye", value = "Good Bye", cultureId = "en-US" );
    Translation.Add(new Translation  key = "goodbye", value = "Au revoir", cultureId = "fr-FR" );
    Translation.Add(new Translation  key = "goodbye", value = "adiós", cultureId = "es-ES" );

    Dictionary<string, List<string>> query = Translation
        .OrderBy(o => o.cultureId)
        .GroupBy(o => o.key)
        .ToDictionary(g => g.Key, g => g.Select(x => x.value).ToList());

    return Json(query);


当前代码IActionResult返回:


  "hello": [
    "Hello", //en
    "Bonjour", //fr
    "Buongiorno" //it
  ],
  "goodbye": [
    "Good Bye", //en
    "Au revoir", //fr
    "Adiós", //es
  ]


但我希望为缺少的翻译添加 Null 值: "en-US", "fr-FR", "it-IT", "es-ES"


  "hello": [
    "Hello", //en
    "Bonjour", //en
    "Buongiorno", //it
     null //es
  ],
  "goodbye": [
    "Good Bye", //en
    "Au revoir", //fr
    null, //it
    "Adiós" //es
  ]

可以只用Linq吗?或者也许我应该在字典里写一个loop 并重新填充山谷?

【问题讨论】:

多个翻译实例可以共享同一个key?我还以为是PK,还是和DB无关? 另一方面,您可以通过使用左连接来连接两个集合来完成您想要的操作。 Google 如何使用 LINQ 进行左连接。 是的,它不是 PK,要获得特定文化的特定翻译,我在 keycultureId 上搜索 【参考方案1】:

你为什么会期望这一点?您实际上是在告诉它只选择valuecultureId 和您的 Languages 数组都没有被提及或使用,那么您为什么还要期望它们以某种方式发挥作用呢?

你需要类似下面的GroupBy:

.GroupBy(g => g.key, (key, items) => Languages.Select(lang => items.FirstOrDefault(i => i.cultureId == lang)?.value))

【讨论】:

事实上这不是好的答案,hellogoodbye 的键没有返回【参考方案2】:

你可以加入你的语言列表,像这样(尚未测试)

Dictionary<string, List<string>> query = Translation
    .GroupBy(o => o.key)
    .ToDictionary(g => g.Key, g => 
        Languages.Select(l => g.FirstOrDefault(x => x.cultureId == l)?.value));

解释:

我们将组映射到语言列表中。对于每种语言,如果与cultureId 匹配的元素在组中,则使用该值,或者改为使用null。

(暂时无法测试,如有错误请见谅)

【讨论】:

编辑了我的答案以包含评论。不需要在 Aleks 的建议中使用 Any 或 Contains 是的,这是一个很好的答案:.ToDictionary(g =&gt; g.Key, g =&gt; Languages.Select(l =&gt; g.FirstOrDefault(x =&gt; x.cultureId == l)?.value).ToList()); 很高兴它有效。老实说,我更赞成克里斯的回答,因为它在分组中更早地处理了问题。【参考方案3】:

您尝试在 linq 中执行 "left join",这可以使用 JoinDefaultIfEmpty 运算符。 只要我知道这只能在声明性语法上进行:

var query = from trans in Translation
            group trans by trans.key into transGroup
            select new
            
                transGroup.Key,
                Values = from langId in Languages
                         join trans in transGroup on langId equals trans.cultureId 
                             into joint
                         from trans in joint.DefaultIfEmpty()
                         select trans?.value
            ;

var values = query.ToDictionary(x => x.Key, x => x.Values.ToList());

我同意这不是最易读的查询,也许以程序方式维护它会更简单。

【讨论】:

【参考方案4】:

使用下面的代码

Dictionary<string, List<string>> query = Translation
            .OrderBy(o => o.cultureId)
            .GroupBy(o => o.key)
            .ToDictionary(g => g.Key, g => Languages.GroupJoin(g.Select(x => x), f => f, s => s.cultureId, (f, s) => s).SelectMany(x => x.DefaultIfEmpty(),(x, y) => y != null? y.value :null).ToList());

并更正最后的翻译

Translation.Add(new Translation key = "goodbye", value = "adiós", cultureId = "es-ES" );

Translation.Add(new Translation key = "goodbye", value = "adiós",cultureId = "es-US" );

【讨论】:

以上是关于Linq to Dictionary<string, List<string>> 与 Null 值的主要内容,如果未能解决你的问题,请参考以下文章

使用 LINQ 选择 Dictionary<T1, T2>

Linq 到 Dictionary<string, string[]>。选择值包含条件字符串的键

为啥 Dictionary<TKey, TValue> 上的这个 Linq 查询不能作为数据源工作

嵌套字典LINQ中的过滤元素

如何在 C# 中使用 LINQ 过滤嵌套字典?

LINQ to OBJECT函数积累