subkonstrukt
subkonstrukt

Reputation: 446

How to go from iterative approach to recursive approach

I have an iterative C# loop which fills out a checkboard pattern of up to 5 columns.

The values are paired, it's always a Headline and multiple Values for each column, and it's combining the values to a non-repetitative combination.

Starting with the simplest solution I could imagine, and after looking at it, I thought there must be a better approach to this problem by doing this recursively.

Below is an example of what I've tried so far:

 List<EtOfferVariant> variants = new List<EtOfferVariant>();

 _containers[0].Variant.ForEach(first =>
 {
     if (_containers.Count > 1)
     {
         _containers[1].Variant.ForEach(second =>
         {
             if (_containers.Count > 2)
             {
                 _containers[2].Variant.ForEach(third =>
                     {
                         EtOfferVariant va = new EtOfferVariant();
                         va.OfferVariant1Type = _containers[0].VariantKey;
                         va.OfferVariant1 = first;
                         va.OfferVariant2Type = _containers[1].VariantKey;
                         va.OfferVariant2 = second;
                         va.OfferVariant3Type = third;
                         va.OfferVariant3 = _containers[3].VariantKey;
                         variants.Add(va);
                     });
             }
             else
             {
                 EtOfferVariant va = new EtOfferVariant();
                 va.OfferVariant1Type = _containers[0].VariantKey;
                 va.OfferVariant1 = first;
                 va.OfferVariant2Type = second;
                 va.OfferVariant2 = _containers[1].VariantKey;
                 variants.Add(va);
             }
         });
     }
     else
     {
         EtOfferVariant va = new EtOfferVariant();
         va.OfferVariant1Type = _containers[0].VariantKey;
         va.OfferVariant1 = first;
         variants.Add(va);
     }
 });

The containers consist of a list of strings (the values) and a key (the headline).

It's a shortend version OfferVariant counts up to 5 in the real example.

I cant change the inital checkboard structure since its given by a existing database.

Below is an illustration of the data input and output for 2 containers consisting of:

Container 1:

Container 2:

The generated output would consist of 4 rows containing

edit due the fact its easyly missunderstood as its illustrated here

The Result will be a Row in a Database consisting of 4 columns

Column 1 | Column 2  | Column 3 | Column 4
Pie      | Raspberry | Drink    | Cola 
Pie      | Raspberry | Drink    | Coffee
Pie      | Strawberry| Drink    | Cola 
Pie      | Strawberry| Drink    | Coffee

EtOfferVariant is a ORM Poco containing those columns

Upvotes: 6

Views: 267

Answers (3)

Jonas Elfstr&#246;m
Jonas Elfstr&#246;m

Reputation: 31428

If you first convert the dictionaries in the containers it seems you can get the wanted result by cartesian product.

var containers = new List<Dictionary<string, IEnumerable<string>>>
                    {
                        new Dictionary<string, IEnumerable<string>>() {{"Pie", new [] {"Raspberry", "Strawbery"}}},
                        new Dictionary<string, IEnumerable<string>>() {{"Drink", new [] {"Cola", "Coffee"}}},
                        new Dictionary<string, IEnumerable<string>>() {{"Bread", new [] {"Bagel", "Pretzel", "Scone"}}},
                    };

var flatten = containers.Select(dict => dict.SelectMany(c => c.Value.Select(v => new {type = c.Key, name = v})));

foreach (var combo in CartesianProduct(flatten))
{
    Console.WriteLine(string.Join(", ", combo.Select(c => c.type + ": " + c.name)));
}

Cartesian product method from https://stackoverflow.com/a/3098381/44620

public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences)
{
    IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
    return sequences.Aggregate(
        emptyProduct,
        (accumulator, sequence) => 
            from accseq in accumulator 
            from item in sequence 
            select accseq.Concat(new[] {item})                         
        );
}

Output:

Pie: Raspberry, Drink: Cola, Bread: Bagel
Pie: Raspberry, Drink: Cola, Bread: Pretzel
Pie: Raspberry, Drink: Cola, Bread: Scone
Pie: Raspberry, Drink: Coffee, Bread: Bagel
Pie: Raspberry, Drink: Coffee, Bread: Pretzel
Pie: Raspberry, Drink: Coffee, Bread: Scone
Pie: Strawbery, Drink: Cola, Bread: Bagel
Pie: Strawbery, Drink: Cola, Bread: Pretzel
Pie: Strawbery, Drink: Cola, Bread: Scone
Pie: Strawbery, Drink: Coffee, Bread: Bagel
Pie: Strawbery, Drink: Coffee, Bread: Pretzel
Pie: Strawbery, Drink: Coffee, Bread: Scone

Upvotes: 0

subkonstrukt
subkonstrukt

Reputation: 446

Thank you for your replies, Martin Liversage kinda guided me mental to it, tough it wasnt a pure cartesian product due the fact is was grouped to be flattend out latter and Cines approach gave me the final point in the right direction I solved this the following way now

in the first step I generate the inital rows for the first variant

    List<EtOfferVariant> row = new List<EtOfferVariant>();
    _containers.First().Variant.ForEach(o =>
    {
        row.Add(new EtOfferVariant() { OfferVariant1 = o, OfferVariant1Type = _containers.First().VariantKey });
    });
  return BuildVariants(row);

and then run it trough

  private List<EtOfferVariant> BuildVariants(List<EtOfferVariant> row, int containerIndex = 1)
    {
        List<EtOfferVariant> final = new List<EtOfferVariant>();
        row.ForEach(y =>
        {
            for (int i = 0; i < _containers[containerIndex].Variant.Count; i++)
            {
                var ret = MultiplyFromPrevious(y);
                FillByIndex(ret, _containers[containerIndex].Index, _containers[containerIndex].VariantKey, _containers[containerIndex].Variant[i]);
                final.Add(ret);
            }
        });
        containerIndex++;
        if (containerIndex < _containers.Count)
           return BuildVariants(final, containerIndex);
        return final;
    }

and thanks again to Cine

private EtOfferVariant MultiplyFromPrevious(EtOfferVariant variant)
{
    EtOfferVariant ret = new EtOfferVariant();
    ret.OfferVariant1 = variant.OfferVariant1;
    ret.OfferVariant2 = variant.OfferVariant2;
    ret.OfferVariant3 = variant.OfferVariant3;
    ret.OfferVariant4 = variant.OfferVariant4;
    ret.OfferVariant5 = variant.OfferVariant5;

    ret.OfferVariant1Type = variant.OfferVariant1Type;
    ret.OfferVariant2Type = variant.OfferVariant2Type;
    ret.OfferVariant3Type = variant.OfferVariant3Type;
    ret.OfferVariant4Type = variant.OfferVariant4Type;
    ret.OfferVariant5Type = variant.OfferVariant5Type;

    return ret;
}

I also seperated the column split into an own method, but it does nothing less then taking the index and map the values to the object, nothing special

thanks again everyone, this really loosend up the code

Upvotes: 1

Cine
Cine

Reputation: 4402

I presume you just want to cut down on the duplicate code. In that case, just add data as you know it and add the variant at the end once it is built up. Only thing is, is that you need a copy constructor that copies the values from the previous runs.

List<EtOfferVariant> variants = new List<EtOfferVariant>();

_containers[0].Variant.ForEach(first =>
{
    EtOfferVariant va = new EtOfferVariant();
    va.OfferVariant1Type = _containers[0].VariantKey;
    va.OfferVariant1 = first;
    if (_containers.Count > 1)
    {
        _containers[1].Variant.ForEach(second =>
        {
            va = new EtOfferVariant(va);
            va.OfferVariant2Type = _containers[1].VariantKey;
            va.OfferVariant2 = second;
            if (_containers.Count > 2)
            {
                _containers[2].Variant.ForEach(third =>
                {
                    va = new EtOfferVariant(va);
                    va.OfferVariant3Type = third;
                    va.OfferVariant3 = _containers[3].VariantKey;
                    variants.Add(va);
                });
            } else 
            variants.Add(va);
        });
    } else
    variants.Add(va);
});

...

public EtOfferVariant(EtOfferVariant va){
   this.OfferVariant1Type = va.OfferVariant1Type;
   this.OfferVariant2Type = va.OfferVariant2Type;
   this.OfferVariant3Type = va.OfferVariant3Type;
   this.OfferVariant1 = va.OfferVariant1;
   this.OfferVariant2 = va.OfferVariant2;
   this.OfferVariant3 = va.OfferVariant3;
}

Upvotes: 0

Related Questions