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