0

I have common subkeys (SubKey1/SubKey2, etc) for each set of BaseKey1, BaseKey2, .... etc, but occurrence of all subkeys for each basekey is NOT fixed. In below example "SubKey2" not present for "BaseKey2".

var model = new Dictionary<string, Dictionary<string, Model>>
        {
            {
                "BaseKey1",
                new Dictionary<string, Model>
                {
                    {"SubKey1", new Model { Time = new DateTime(2020, 09, 15)}},
                    {"SubKey2", new Model { Time = new DateTime(2020, 12, 15) }}
                }
            },

            {
                "BaseKey2",
                new Dictionary<string, Model>
                {
                    {"SubKey1", new Model { Time = new DateTime(2020, 11, 15) }},
                }
            }
        };

I need to pull Min Time for each subkey from all basekey and to do so I am doing below,

 var query = model.Values
            .SelectMany(d => d.Keys)
            .Distinct()
            .Select(key => new
            {
                Key = key,
                Time = model.Values.Min(v => v[key].Time)
            })
            .ToList();

But it's giving error "The given key was not present in the dictionary" due to "SubKey2" not present for "BaseKey2". What could be solution here? I need below output. Thanks!

enter image description here

user584018
  • 10,186
  • 15
  • 74
  • 160

2 Answers2

1

You're not going to be able to do this without some loops -- the fact that you're using a dictionary is more or less irrelevant.

My understanding is that you want something like this:

var result = new Dictionary<string, Model>();
// For each "BaseKey" dictionary...
foreach (var subDictionary in model.Values)
{
    // For each "SubKey" and its corresponding value    
    foreach (var (key, value) in subDictionary)
    {
        // If we haven't yet recorded a value for this SubKey, add this value
        // If we have, but it's higher than the value for this SubKey, replace it
        if (!result.TryGetValue(key, out var existingValue) || value.Time < existingValue.Time)
        {
            result[key] = value;
        }
    }
}

See it working here.

You can sprinkle in a bit of LINQ to remove one of the loops quite easily:

var result = new Dictionary<string, Model>();
foreach (var (key, value) in model.Values.SelectMany(x => x))
{
    if (!result.TryGetValue(key, out var existingValue) || value.Time < existingValue.Time)
    {
        result[key] = value;
    }
}

See it working here.

If you really want to LINQ it up, you'll want something like:

var result = model.Values
    .SelectMany(x => x)
    .GroupBy(x => x.Key)
    .ToDictionary(
        x => x.Key,
        x => x.Select(m => m.Value)
            .MinBy(m => m.Time));

... where MinBy is provided by e.g. this answer. That's probably going to be measurably slower though, and isn't any shorter.

See it working here.

canton7
  • 37,633
  • 3
  • 64
  • 77
0

Check this

Dictionary<string, Model> flatSubDict = new Dictionary<string, Model>();
        foreach(var key in model.Keys)
        {
            foreach(var mKey in model[key].Keys)
            {
                if(flatSubDict.TryGetValue(mKey, out var m))
                {
                    // if existing time greater than new time then replace
                    if(m.Time > model[key][mKey].Time)
                    {
                        flatSubDict[mKey] = m;
                    }
                }
                else
                      flatSubDict[mKey] = m;
            }
        }

return flatSubDict.Values;

RockWorld
  • 1,278
  • 2
  • 11
  • 24
  • This contains loads of completely unnecessary dictionary lookups. You lookup `model[key]` twice (when you don't need to do it at all), and the `mKey` lookup is also unnecessary. Keep improving that, and you'll get to my answer – canton7 May 18 '21 at 13:13
  • Updated else part – RockWorld May 18 '21 at 13:15