Home > Enterprise >  Iterating through a heterogenous container?
Iterating through a heterogenous container?

Time:09-17

I am trying to iterate over a simple heterogenous container: Dictionary<string, object>.

It is constructed like this:

var d = new Dictionary<string, object>();
d.Add("key1", "value1");
d.Add("key2", 42);
var sub = new Dictionary<string, object>();
sub.Add("key3", new double[] { 0, 0 });
d.Add("key4", sub);

I assumed I could have written a simple function:

public IEnumerable<KeyValuePair<string, object>> flatten(IDictionary<string, object> dict)
{
    foreach(KeyValuePair<string, object> item in dict)
    {
        if (item.Value is IDictionary<string, object> subDict)
        {
            flatten(subDict);
        }
        else
        {
            yield return item;
        }
    }
}

How can I rewrite the above flatten function, so that:

var l = flatten(d).ToList();

returns a list of three items (with keys: key1, key2 and key3). I have not been able to use SelectMany from Linq package in my case so far...

CodePudding user response:

Try yield return the results of the flatten recursive call:

public IEnumerable<KeyValuePair<string, object>> flatten(IDictionary<string, object> dict)
{
    foreach (KeyValuePair<string, object> item in dict)
    {
        if (item.Value is IDictionary<string, object> subDict)
        {
            foreach (var item2 in flatten(subDict))
            {
                yield return item2;
            }
        }
        else
        {
            yield return item;
        }
    }
}

CodePudding user response:

If you want to use SelectMany, you actually need to project a non IDictionary<...> to an IEnumerable (so that the dict is always thought of as a collection of collections to be flattened):

public IEnumerable<KeyValuePair<string, object>> flatten(IDictionary<string, object> dict)
{
   return dict.SelectMany(kv => kv.Value switch {
      IDictionary<string, object> subdict => flatten(subdict),
                                        _ => Enumerable.Repeat(kv, 1)
   });
}

Live Demo

We don't need to say yield return because SelectMany is directly returning an IEnumerable

  • Related