Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I deal with multiple foreach loops when I'm working with multiple data types

Tags:

c#

loops

I have a problem that has been bothering me for a while now, it concerns the growth of loops in my program exponentially. I will let the code below do the talking and add comments within.

void Main()
{
    //Here we are just creating simple lists
    List<string> strings = new List<string>();
    strings.Add("a");
    strings.Add("b");
    strings.Add("c");

    List<int> integers = new List<int>();
    integers.Add(1);
    integers.Add(2);
    integers.Add(3);

    //Creating complex classes ( not really )
    ComplexClass cc1 = new ComplexClass();
    cc1.CCString = "A test";
    cc1.CCInt = 2;

    ComplexClass cc2 = new ComplexClass();
    cc2.CCString = "Another test";
    cc2.CCInt = 6;

    //Creating a list of these too
    List< ComplexClass > complexClasses = new List< ComplexClass >();
    complexClasses.Add(cc1);
    complexClasses.Add(cc2);


    //Here i want to create every possible combination using each of the lists 
    //and then add these to a testData class to do other things with, serialize, save, print etc.
    //The main question is here, the for loops will definitely increase exponentially with each
    //list added to. 
    foreach( int i in integers )
    {
        foreach( string s in strings )
        {
            foreach( ComplexClass compClass in complexClasses )
            {
                TestData data = new TestData();
                data.TestInteger = i;
                data.TestString = s;
                data.TestComplexClass = compClass;

                OutPutTestData( data );
            }
        }
    }
}

//Simply outputs the data as test but I will be keeping the object for later also
public void OutPutTestData( TestData testData )
{
    Console.WriteLine( testData.TestString + testData.TestInteger + testData.TestComplexClass.CCString );
}

//The "Complex class" again not that complex but an example of what im tring to achieve
public class ComplexClass
{
    public string CCString{ get; set; }
    public int CCInt { get; set; }
}

//The overall test object which holds multiple properties of different data types
public class TestData
{
    public string TestString { get; set; }
    public int TestInteger { get; set; }
    public ComplexClass TestComplexClass { get; set; }
}

Output

a1 Test1

a1 Test2

b1 Test1

b1 Test2

c1 Test1

c1 Test2

a2 Test1

a2 Test2

b2 Test1

b2 Test2

c2 Test1

c2 Test2

a3 Test1

a3 Test2

b3 Test1

b3 Test2

c3 Test1

c3 Test2

As you can see the loops work and give me every possible combination of the supplied data.

My problem is the exponential growth of the for loops as i add more lists. There could be a large number of lists.

I do understand that the number of iterations will increase as the combinations are discovered, thats not a problem as i plan to programmatically limit the amount of iterations that can occur based on the users input after estimating the total iterations possible.

e.g. Total iterations would be 234 so only iterate 120 times ( 120 combinations )

The code provided works great with nested foreach loops but as it grows exponentially it becomes hard to read, hard to manage and generally unsightly.

I have looked at Permutation algorithms like these:

Algorithm to generate all possible permutations of a list?

Understanding Recursion to generate permutations.

But they only allow the use of one specific datatype and not multiple.

I also looked into cartesian product but again the only examples i have found refer to only a single data type.

like image 894
Master Yoda Avatar asked Mar 16 '23 01:03

Master Yoda


2 Answers

Even though you selected an answer, I thought you may want to have a look at this... With the use of recursion, all you have to do is put all of your Lists in a List<IList>. All you have to do with this is just add any newly added Lists to the List<IList>.

I added a override ToString() to your ComplexClass for this to fit.

        public static void Test()
        {
            //Here we are just creating simple lists
            List<string> strings = new List<string>();
            strings.Add("a");
            strings.Add("b");
            strings.Add("c");

            List<int> integers = new List<int>();
            integers.Add(1);
            integers.Add(2);
            integers.Add(3);

            //Creating complex classes ( not really )
            ComplexClass cc1 = new ComplexClass();
            cc1.CCString = "A test";
            cc1.CCInt = 2;

            ComplexClass cc2 = new ComplexClass();
            cc2.CCString = "Another test";
            cc2.CCInt = 6;

            //Creating a list of these too
            List<ComplexClass> complexClasses = new List<ComplexClass>();
            complexClasses.Add(cc1);
            complexClasses.Add(cc2);

            // NEW LIST
            List<double> doubles = new List<double>();
            doubles.Add(99.99);
            doubles.Add(100.12);

            List<IList> myLists = new List<IList> {integers, strings, complexClasses, doubles};
            Permutate("", myLists, 0);

            Console.ReadLine();
        }

        public static void Permutate(string s, List<IList> list, int i)
        {
            if (i == list.Count)
            {
                Console.WriteLine(s);
            }
            else
            {
                foreach (object obj in list[i])
                {
                    Permutate(s + obj + " ", list, i + 1);
                }
            }
        }

        //The "Complex class" again not that complex but an example of what im tring to achieve
        public class ComplexClass
        {
            public string CCString { get; set; }
            public int CCInt { get; set; }

            // Added override
            public override string ToString()
            {
                return CCString + CCInt;
            }
        }

Results (Not all results were captured):

enter image description here

like image 199
Shar1er80 Avatar answered Apr 06 '23 04:04

Shar1er80


You could get rid of the for loops by doing a cross join in Linq:

var query = 
    from  i in integers
    from s in strings 
    from compClass in complexClasses
    select new TestData()
    {
        TestInteger = i,
        TestString = s,
        TestComplexClass = compClass
    };

foreach (var data in query)
    OutPutTestData( data );

If the lists were all of the same type then you could build a query that cross-joins a varying number of lists. In your case since the lists are of varying types it's not possible (without reflection, dynamic, or something uglier)

like image 28
D Stanley Avatar answered Apr 06 '23 02:04

D Stanley