If I start with a structure that can represent your range of values, like this:
public struct Range
{
public int Minimum { get; set; }
public int Maximum { get; set; }
}
...then I can represent your inputs like this:
var inputs = new Dictionary<string, Range>()
{
{ "a", new Range() { Minimum = 1, Maximum = 3 } },
{ "b", new Range() { Minimum = 1, Maximum = 2 } },
{ "c", new Range() { Minimum = 1, Maximum = 2 } },
};
...and then I can build the results like this:
Func<IEnumerable<KeyValuePair<string, Range>>, IEnumerable<Dictionary<string, int>>> build = null;
build =
kvps =>
{
if (kvps.Skip(1).Any())
{
return
from kvp in kvps.Take(1)
from n in Enumerable.Range(kvp.Value.Minimum, kvp.Value.Maximum - kvp.Value.Minimum + 1)
from r in build(kvps.Skip(1))
select new[] { new KeyValuePair<string, int>(kvp.Key, n) }.Concat(r).ToDictionary(x => x.Key, x => x.Value);
}
else
{
return
from kvp in kvps
from n in Enumerable.Range(kvp.Value.Minimum, kvp.Value.Maximum - kvp.Value.Minimum + 1)
select new[] { new KeyValuePair<string, int>(kvp.Key, n) }.ToDictionary(x => x.Key, x => x.Value);
}
};
This produces the following list of dictionaries:
a=1, b=1, c=1
a=1, b=1, c=2
a=1, b=2, c=1
a=1, b=2, c=2
a=2, b=1, c=1
a=2, b=1, c=2
a=2, b=2, c=1
a=2, b=2, c=2
a=3, b=1, c=1
a=3, b=1, c=2
a=3, b=2, c=1
a=3, b=2, c=2
Here's an explanation of the main query:
from kvp in kvps.Take(1)
Get the first element from the kvps
enumerable (this is the "head" of the enumerable)
from n in Enumerable.Range(kvp.Value.Minimum, kvp.Value.Maximum - kvp.Value.Minimum + 1)
Generate all of the values of n
from Minimum
to Maximum
.
from r in build(kvps.Skip(1))
Recursively call build
on the "tail" of the list to generate all of the possible tail dictionaries
select new[] { new KeyValuePair<string, int>(kvp.Key, n) }.Concat(r).ToDictionary(x => x.Key, x => x.Value);
Create a new KeyValuePair<string, int>[]
with the Key
and the value n
and concatenate each value from the tail (r
) at create a new dictionary.