简体   繁体   中英

Linq to Dictionary<string, List<string>> with Null values

I do have a list a Culture and a list of Translation:

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);
}

The current code IActionResult return:

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

But i expect to add the Null values for the missing translation: { "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
  ]
}

Can it be done with only Linq ? Or maybe i should do a loop in the dictionary et repopulate the vales?

Why would you expect that in the first place? You're literally telling it to just select value . Neither cultureId nor your Languages array are even mentioned or utilized, so why would you even expect them to somehow play a part?

You'd need something like the following GroupBy :

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

You can do some join with your languages list, something like this (untested yet)

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));

Explanation :

We map the group on the list of languages. For each langage, if a matching element with cultureId is in the group, use that value, or instead use null.

(can't test right now, pardon me in advance for any error)

You try to do a "left join" in linq, that is possible using Join and DefaultIfEmpty operators. As long as I know this only can be made on a declarative syntax:

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());

I agree it's not the most readable query and maybe it would be simpler to maintain doing it on a procedural way.

Use below code

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());

And Correct last translation

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM