Create combinations of items in multiple line lists

I am trying to automate a nested foreach, provided that there is a main list containing a list of strings as elements for the following scenario.

Here, for example, I have 5 lists of strings stored in the main list of lstMaster

List<string> lst1 = new List<string> { "1", "2" }; List<string> lst2 = new List<string> { "-" }; List<string> lst3 = new List<string> { "Jan", "Feb" }; List<string> lst4 = new List<string> { "-" }; List<string> lst5 = new List<string> { "2014", "2015" }; List<List<string>> lstMaster = new List<List<string>> { lst1, lst2, lst3, lst4, lst5 }; List<string> lstRes = new List<string>(); foreach (var item1 in lst1) { foreach (var item2 in lst2) { foreach (var item3 in lst3) { foreach (var item4 in lst4) { foreach (var item5 in lst5) { lstRes.Add(item1 + item2 + item3 + item4 + item5); } } } } } 

I want to automate the loop below, regardless of the number of list items stored in the lstMaster main list

+5
source share
3 answers

Just cross-connect with each subsequent list:

  IEnumerable<string> lstRes = new List<string> {null}; foreach(var list in lstMaster) { // cross join the current result with each member of the next list lstRes = lstRes.SelectMany(o => list.Select(s => o + s)); } 

results:

 List<String> (8 items) ------------------------ 1-Jan-2014 1-Jan-2015 1-Feb-2014 1-Feb-2015 2-Jan-2014 2-Jan-2015 2-Feb-2014 2-Feb-2015 

Notes:

  • Declaring lstRes as an IEnumerable<string> prevents unnecessary creation of additional lists that will be thrown with each iteration

  • The malicious null used so that the first cross join will have something that can be built (with lines, null + s = s )

+4
source

To make this truly dynamic, you need two arrays of int loop variables (index and number):

 int numLoops = lstMaster.Count; int[] loopIndex = new int[numLoops]; int[] loopCnt = new int[numLoops]; 

Then you need the logic to iterate through all of these loopIndexes.

Initialize to start value (optional)

 for(int i = 0; i < numLoops; i++) loopIndex[i] = 0; for(int i = 0; i < numLoops; i++) loopCnt[i] = lstMaster[i].Count; 

Finally, a large loop that works through all combinations.

 bool finished = false; while(!finished) { // access current element string line = ""; for(int i = 0; i < numLoops; i++) { line += lstMaster[i][loopIndex[i]]; } llstRes.Add(line); int n = numLoops-1; for(;;) { // increment innermost loop loopIndex[n]++; // if at Cnt: reset, increment outer loop if(loopIndex[n] < loopCnt[n]) break; loopIndex[n] = 0; n--; if(n < 0) { finished=true; break; } } } 
+2
source
  var totalCombinations = 1; foreach (var l in lstMaster) { totalCombinations *= l.Count == 0 ? 1 : l.Count; } var res = new string[totalCombinations]; for (int i = 0; i < lstMaster.Count; ++i) { var numOfEntries = totalCombinations / lstMaster[i].Count; for (int j = 0; j < lstMaster[i].Count; ++j) { for (int k = numOfEntries * j; k < numOfEntries * (j + 1); ++k) { if (res[k] == null) { res[k] = lstMaster[i][j]; } else { res[k] += lstMaster[i][j]; } } } } 

The algorithm begins by calculating the number of combinations required for all subscriptions.

When we know that we are creating an array of results with so many records. Then the algorithm iterates through all the auxiliary lists, extracts the element from the auxiliary list and calculates how many times the element should occur as a result, and adds the element the specified number of times to the results. Go to the next item in the same list and add to the remaining fields (or as many as needed if there are more than two items in the list). And it continues through all the auxiliary lists and all the elements.

One area that needs to be improved is when the list is empty. There is a risk of a DivideByZeroException. I did not add this. I would rather focus on conveying the idea that underlies the calculations, and would not want to obfuscate it with additional checks.

0
source

Source: https://habr.com/ru/post/1213734/


All Articles