Unknown number for loops using Recursion - c#

I have done some research prior to and have found some great articles but I can't seem to tailor any of the solutions for my given problem. From the research done, I believe the best method of going about this problem would be to use recursion. I have made an example using some generic classes but essentially my problem is I have approximately 10 classes that I can have in a list. I might have only one of these classes and I might have all ten. I am ultimately finding the best combination of "items" (which all inherit from item) for a given problem. I think this would be fairly easy except for I have to deal with creating the combinations before each test.
Below is some sample code using only two classes. If recursion is not the best way to approach this problem then please correct as needed. How might I convert this to be used for any number of items that are needed to test with?
Edited: As some have pointed out my example code is the iterative solution however it is only useful if I have two items. Therefore, I need to define a recursive function to solve the problem based upon the number of for loops needed upon runtime.
-Chance
Research:
C#: N For Loops
Arbitrary number of nested-loops?
Number of nested loops at runtime
static void Main(string[] args)
{
List<Item> myItem = new List<Item>();
int numberItem1 = 0, numberItem2 = 0;
foreach (var item in myItem)
{
if (item.GetType() == typeof(Item1))
{
numberItem1++;
}
else if (item.GetType() == typeof(Item2))
{
numberItem2++;
}
}
List<Item> testingItems = new List<Item>();
//FirstItem
for (int a = 0; a < numberItem1; a++)
{
for (int b = 0; b <= a; b++)
{
testingItems.Add(new Item1 { });
}
//DoTest()
testingItems.Clear();
//Second Item
for (int c = 0; c < numberItem2; c++)
{
for (int d = 0; d <= a ; d++)
{
testingItems.Add(new Item1 { });
}
for (int e = 0; e <= c; e++)
{
testingItems.Add(new Item2 { });
}
//DoTest()
testingItems.Clear();
}
}
}

Non-recursive solution.
IEnumerable<List<Item>> TestLists(List<Item> fullList)
{
List<Type> types = fullList.Select(i => i.GetType()).Distinct().ToList();
List<Item> testList = new List<Item> { (Item)Activator.CreateInstance(types[0]) };
yield return testList;
bool finished = false;
while (!finished)
{
bool incremented = false;
int i = 0;
while (i < types.Count && !incremented)
{
if (testList.Where(t => t.GetType() == types[i]).Count() <
fullList.Where(t => t.GetType() == types[i]).Count())
{
testList.Add((Item)Activator.CreateInstance(types[i]));
incremented = true;
}
else
{
testList = testList.Where(t => t.GetType() != types[i]).ToList();
i++;
}
}
if (incremented)
{
yield return testList;
}
else
{
finished = true;
}
}
}
Usage:
foreach (var partList in TestLists(myListToTest))
{
DoTest(partList);
}

I think the following should work.
This requires you to build a stack of the item types you want to test, and a stack of the number of each present in the original list, with the two stacks in sync with each other.
The input List parameter should be an empty list.
void RecursiveTest(List<Item> testingItems, Stack<Type> itemTypes, Stack<int> itemCounts)
{
if (itemTypes.Count == 0) { return; }
Type thisType = itemTypes.Pop();
int thisCount = itemCounts.Pop();
List<Item> addedItems = new List<Item>();
for (int i = 0; i <= thisCount; i++)
{
if (i > 0)
{
Item thisItem = (Item)Activator.CreateInstance(thisType);
testingItems.Add(thisItem);
addedItems.Add(thisItem);
}
if (itemTypes.Count == 0)
{
DoTest(testingItems);
}
else
{
RecursiveTest(testingItems, itemTypes, itemCounts);
}
}
foreach(Item addedItem in addedItems)
{
testingItems.Remove(addedItem);
}
itemTypes.Push(thisType);
itemCounts.Push(thisCount);
}
Note: This code doesn't output/test lists that don't contain at least one of each item type.
Second note: This now includes the missing cases. It will, however, also test the empty list.

EDIT
This code should generate all the possible test permutations for the list of items + the maximum number of each item that should appear in each test.
EXAMPLE: myItem = Item1 Item1 Item2 Item2 Item3
tests = 1,0,0; 2,0,0; 0,1,0; 1,1,0; 2,1,0; 0,2,0; 1,2,0; 2,2,0; 0,0,1; 1,0,1; 2,0,1; 0,1,1; 1,1,1; 2,1,1; 0,2,1; 1,2,1; 2,2,1
List<Item> myItem = new List<Item>();
List<Type> myOrder = new List<Item>();
Dictionary<Type, int> myCount = new Dictionary<Type, int>();
foreach (var item in myItem)
{
if (myCount.ContainsKey(item.GetType()))
{
myCount[item.GetType()]++;
}
else
{
myOrder.Add(item.GetType());
myCount.Add(item.GetType(), 1);
}
}
List<Item> testingItems = new List<Item>();
int[] testingCounts = new int[myCount.Count];
while(IncrementCounts(testingCounts, myOrder, myCount)) {
for(int x=0; x<testingCounts.length; x++) {
AddInstances( testingItems, myOrder[x], testingCounts[x] );
}
// doTest()
testingItems.Clear();
}
// count permutations using the maxima
// EXAMPLE: maxima [2, 2, 2]
// 1,0,0; 2,0,0; 0,1,0; 1,1,0; 2,1,0; 0,2,0; 1,2,0; 2,2,0; 0,0,1; 1,0,1; 2,0,1 etc..
public static bool IncrementCounts(int[] counts, List<Type> order, Dictionary<Type, int> maxima) {
for(int x=0; x<counts.length; x++) {
if(counts[x] + 1 <= maxima[order[x]]) {
counts[x]++;
return true;
} else {
counts[x] = 0;
}
}
return false; // overflow, so we're finished
}
public static void AddIstances(List<Item> list, Type type, int count) {
for(int x=0; x<count; x++) {
list.Add( Convert.ChangeType( Activator.CreateInstance(type), type ) );
}
}
Please note the above code was written inside the browser window and is untested, so syntax errors may exist.

Related

How to iterate a collection between two items?

Consider for example a List of objects:
List<MyClass> myList;
I have a method which passes two references to items within the list. I want to iterate all items within the given ones (note: I don't know which of the two items comes first within the list):
privat void MyFunction(MyClass listItem, MyClass anotherListItem)
{
foreach(var item in ????)
{
// do something
}
}
Currently I have solved this usecase as follows:
int listItemIdx = myList.IndexOf(listItem);
int anotherListItemIdx = myList.IndexOf(anotherListItem);
if(listItemIdx < anotherListItemIdx )
{
for(int i = listItemIdx ; i <= anotherListItemIdx ; i++)
{
// do stuff
}
}
else
{
for (int i = anotherListItemIdx ; i < listItemIdx ; i++)
{
// do stuff
}
}
I was wondering if there is a more elegant, efficient or built-in solution to this problem?
If you are looking for performance (IndexOf twice can be a bit slow) and generalization
(when myList is not necessary List<T> but IEnumerable<T> only) you can put it as
bool proceed = false;
MyClass lastItem = default;
foreach (var item in myList) {
if (!proceed) {
if (proceed = item == listItem)
lastItem = anotherListItem;
else if (proceed = item == anotherListItem)
lastItem = listItem;
}
if (proceed) {
//TODO: do staff here
if (item == lastItem)
break;
}
}
You iterate three times over the list: Two times in IndexOf, and then once again in your loop. You can make your code more efficient with this code, which iterates only once over the list.
privat void MyFunction(MyClass listItem, MyClass anotherListItem)
{
bool betweenTwoItems = false;
foreach(var item in myList)
{
if(item == listItem || item == anotherListItem)
{
betweenTwoItems = !betweenTwoItems;
if(!betweenTwoItems)
{
break;
}
}
if(betweenTwoItems )
{
// do stuff
}
}
}
We set a bool variable if we are between the two items. In the beginning, it is false. The we iterate over the list and check whether the current item is one of the two method parameters. If this is the case, we invert the value of the bool. If after the inversion of the bool the value is false, we can leave the list. After that, we check whether the bool is true. If so, we can do stuff.
Online demo: https://dotnetfiddle.net/xYcr7V
More generic version of the same idea. So this can be created as extension method for IEnumerable<,>
public static IEnumerable<T> RangeOf<T>(this IEnumerable<T> elements, T el1, T el2,
IEqualityComparer<T> comparer = null)
{
comparer ??= EqualityComparer<T>.Default;
var hasStarted = false;
var end = default;
foreach (T el in elements)
{
if (!hasStarted)
{
hasStarted = comparer.Equals(el, el1) || comparer.Equals(el, el2);
end = comparer.Equals(el, el1) ? el2 : el1;
}
if (hasStarted)
yield return el;
if (comparer.Equals(el, end))
yield break;
}
}
and version with the while loop supporting ranges from el to el. For example for [5, 0, 1, 2, 0, 6] the range [0, 0] will be [0, 1, 2, 0]:
public static IEnumerable<T> RangeOf<T>(this IEnumerable<T> elements, T el1, T el2,
IEqualityComparer<T> comparer = null)
{
comparer ??= EqualityComparer<T>.Default;
var hasStarted = false;
var end = default;
var it = elements.GetEnumerator();
while (!hasStarted && it.MoveNext())
{
T el = it.Current;
hasStarted = comparer.Equals(el , el1) || comparer.Equals(el , el2);
end = comparer.Equals(it.Current, el1) ? el2 : el1;
}
if (hasStarted)
yield return it.Current;
while (it.MoveNext())
{
yield return it.Current;
if (comparer.Equals(it.Current, end))
yield break;
}
}
both can be used like this
foreach (var el in list.RangeOf(listItem, anotherListItem))
// Do with el whatever you want to do
Is the list sorted? The you can use that fact to realize which item must be first. Nevertheless you can do with one for-loop, if you prefer:
private static void MyFunction(string item1, string item2)
{
List<string> input = new() {"A", "B", "C", "D", "E"};
int index1 = input.IndexOf(item1);
int index2 = input.IndexOf(item2);
int beginIndex = Math.Min(index1, index2);
int count = Math.Abs(index1 - index2) + 1;
foreach (string item in input.GetRange(beginIndex, count))
{
Console.Write(item);
}
}
Your existing solution can be improved:
int listItemIdx = myList.IndexOf(listItem);
int anotherListItemIdx = myList.IndexOf(anotherListItem);
int startIdx = Math.Min(listItemIdx, anotherListItemIdx);
int endIdx = Math.Max(listItemIdx, anotherListItemIdx);
for(int i = startIdx ; i <= endIdx ; i++)
{
// do stuff
}
Thus, the code duplication disappears and only a minor refactoring is required.
To create a range-loop version, you can create a subset using GetRange(), something like:
int listItemIdx = myList.IndexOf(listItem);
int anotherListItemIdx = myList.IndexOf(anotherListItem);
int startIdx = Math.Min(listItemIdx, anotherListItemIdx);
int endIdx = Math.Max(listItemIdx, anotherListItemIdx);
var subset = myList.GetRange(startIdx, endIdx - startIdx);
foreach(var item in subset)
{
// do stuff
}
Thus, filtering the list and processing the list can now be separated.

Is there a way to reuse for loops with different end arguments? [duplicate]

This question already has answers here:
Pass Method as Parameter using C#
(13 answers)
Closed 2 years ago.
Please do not get intimidated by the wall of code! My question simply put would be the following - can I somehow reuse the code and place whatever arguments I want in the place of // DoSomething? I do not want to copy and paste the same wall of code, I want to make it less repetitive and easier to maintain. I have wondered about this problem, but I do not see a straightforward solution and I am unable to find one.
for (int i = 0; i < treeListViewDatabase.SelectedObjects.Count; i++)
{
if (treeListViewDatabase.SelectedObjects[i] is NodeValueWithNodes rootNode)
{
for (int m = 0; m < rootNode.ChildTreeViewSets.Count; m++)
{
if (rootNode.ChildTreeViewSets[m] is NodeValueWithNodes childNodeWithNode)
{
for (int k = 0; k < childNodeWithNode.ChildTreeViewSets.Count; k++)
{
if (childNodeWithNode.ChildTreeViewSets[k] is NodeValueWithNodes secondChildNodeWithNode)
{
for (int p = 0; p < secondChildNodeWithNode.ChildTreeViewSets.Count; p++)
{
if (secondChildNodeWithNode.ChildTreeViewSets[p] is NodeValueWithDevices thirdChildNodeWithDevices)
{
for (int r = 0; r < thirdChildNodeWithDevices.ChildDeviceSet.Count; r++)
{
if (dataRows.Contains(thirdChildNodeWithDevices.ChildDeviceSet[r]))
{
dataRows.Remove(thirdChildNodeWithDevices.ChildDeviceSet[r]);
}
// DoSomething()
}
}
}
}
if (childNodeWithNode.ChildTreeViewSets[k] is NodeValueWithDevices secondChildNodeWithDevice)
{
for (int r = 0; r < secondChildNodeWithDevice.ChildDeviceSet.Count; r++)
{
if (dataRows.Contains(secondChildNodeWithDevice.ChildDeviceSet[r]))
{
dataRows.Remove(secondChildNodeWithDevice.ChildDeviceSet[r]);
}
// DoSomething();
}
}
}
}
if (rootNode.ChildTreeViewSets[m] is NodeValueWithDevices childNodeDevices)
{
for (int n = 0; n < childNodeDevices.ChildDeviceSet.Count; n++)
{
if (dataRows.Contains(childNodeDevices.ChildDeviceSet[n]))
{
dataRows.Remove(childNodeDevices.ChildDeviceSet[n]);
}
// DoSomething();
}
}
}
}
if (treeListViewDatabase.SelectedObjects[i] is NodeValueWithDevices rootNodeWithDevices)
{
for (int n = 0; n < rootNodeWithDevices.ChildDeviceSet.Count; n++)
{
if (dataRows.Contains(rootNodeWithDevices.ChildDeviceSet[n]))
{
dataRows.Remove(rootNodeWithDevices.ChildDeviceSet[n]);
}
// DoSomething();
}
}
}
for (int i = 0; i < dataRows.Count; i++)
{
// DoSomething();
}
*EDIT*
Following the advise of the people I have completely changed the code. The arrow-like look to the code was replaced with a simple recursion. This implementation made my issue non-existent as I can loop through the dataRows to perform the necessary action.
private void TraverseTreeView(IList selectedObjects, List<DataRow> dataRows)
{
if (selectedObjects == null)
{
return;
}
foreach (var selectedObject in selectedObjects)
{
if (selectedObject is NodeValueWithNodes withNodes)
{
TraverseTreeView(withNodes.ChildTreeViewSets, dataRows);
}
if (selectedObject is NodeValueWithDevices withDevices)
{
TraverseTreeView(withDevices.ChildDeviceSet, dataRows);
}
if (selectedObject is DataRow dataRow && !dataRows.Contains(dataRow))
{
dataRows.Add(dataRow);
}
}
}
You can use a delegate. It might look something like this:
public void TraverseTree(TreeListView root, Action<T> toDo)
{
//big set of loops goes in here
//DoSomething(); is replaced with:
toDo(rootNodeWithDevices.ChildDeviceSet[n]);
}
And then you can call it with a lambda expression, like this:
Traverse(treeListViewDatabase, (node) => {
//code for whatever you want to do goes in here
});
I would also tend to first re-write the initial set of loops like this:
var items = treeListViewDatabase.SelectedObjects.
Where(o => o is NodeValueWithNodes).
Select(o => o.ChildTreeViewSets).
Where(o => o is NodeValueWithNodes).
Select(o => o.ChildTreeViewSets).
Where(o => o is NodeValueWithNodes).
Select(o => o.ChildTreeViewSets).
Where(o => o is NodeValueWithDevices).
Select(o => o.ChildDeviceSet);
foreach(var item in items)
{
if (dataRows.Contains(item))
dataRows.Remove(item);
//DoSomething();
}
If I absolutely had to I'd add a ToList() call to avoid modifying this sequence while iterating.
Having done this much I would further look to build a recursive method to avoid even that much repetition. For example, I'm not sure exactly which third-party TreeListView control you have, but if it were a standard TreeView control I might do this:
public IEnumerable<TreeNode> Traverse(TreeView source)
{
// See Local Functions:
// https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions
IEnumerable<TreeNode> innerTraverse(TreeNodeCollection root)
{
foreach(var node in root)
{
yield return node;
foreach(var node in innerTraverse(root))
{
yield return node;
}
}
}
return innerTraverse(source.nodes));
}
and then use it like this:
var items = Traverse(treeListViewDatabase).
Where(o => o is NodeValueWithDevices).
Select(o => o.ChildDeviceSet);
foreach(var item in items)
{
if (dataRows.Contains(item))
dataRows.Remove(item);
//DoSomething();
}
Which isn't exactly equivalent — we haven't yet accounted for selected items and their children (which I would do by building a bool IsParentNodeSelected() method) — but it's definitely starting to get us to more functional and re-usable code.
Consider using a recursive function (a function that calls itself). For example, to iterate through a Treeview and return a list of ALL of the nodes and their children, and their children's children, etc...:
private List<TreeNode> GetAllNodes(TreeNodeCollection nodes)
{
List<TreeNode> retNodes = new List<TreeNode>();
foreach (TreeNode node in nodes)
{
retNodes.Add(node);
retNodes.AddRange(GetAllNodes(node.Nodes));
}
return retNodes;
}

Create a list of all permutation from different lists [duplicate]

This one should not be too hard but my mind seems to be having a stack overflow (huehue). I have a series of Lists and I want to find all permutations they can be ordered in. All of the lists have different lengths.
For example:
List 1: 1
List 2: 1, 2
All permutations would be:
1, 1
1, 2
In my case I don't switch the numbers around. (For example 2, 1)
What is the easiest way to write this?
I can't say if the following is the easiest way, but IMO it's the most efficient way. It's basically a generalized version of the my answer to the Looking at each combination in jagged array:
public static class Algorithms
{
public static IEnumerable<T[]> GenerateCombinations<T>(this IReadOnlyList<IReadOnlyList<T>> input)
{
var result = new T[input.Count];
var indices = new int[input.Count];
for (int pos = 0, index = 0; ;)
{
for (; pos < result.Length; pos++, index = 0)
{
indices[pos] = index;
result[pos] = input[pos][index];
}
yield return result;
do
{
if (pos == 0) yield break;
index = indices[--pos] + 1;
}
while (index >= input[pos].Count);
}
}
}
You can see the explanation in the linked answer (shortly it's emulating nested loops). Also since for performace reasons it yields the internal buffer w/o cloning it, you need to clone it if you want store the result for later processing.
Sample usage:
var list1 = new List<int> { 1 };
var list2 = new List<int> { 1, 2 };
var lists = new[] { list1, list2 };
// Non caching usage
foreach (var combination in lists.GenerateCombinations())
{
// do something with the combination
}
// Caching usage
var combinations = lists.GenerateCombinations().Select(c => c.ToList()).ToList();
UPDATE: The GenerateCombinations is a standard C# iterator method, and the implementation basically emulates N nested loops (where N is the input.Count) like this (in pseudo code):
for (int i0 = 0; i0 < input[0].Count; i0++)
for (int i1 = 0; i1 < input[1].Count; i1++)
for (int i2 = 0; i2 < input[2].Count; i2++)
...
for (int iN-1 = 0; iN-1 < input[N-1].Count; iN-1++)
yield { input[0][i0], input[1][i1], input[2][i2], ..., input[N-1][iN-1] }
or showing it differently:
for (indices[0] = 0; indices[0] < input[0].Count; indices[0]++)
{
result[0] = input[0][indices[0]];
for (indices[1] = 0; indices[1] < input[1].Count; indices[1]++)
{
result[1] = input[1][indices[1]];
// ...
for (indices[N-1] = 0; indices[N-1] < input[N-1].Count; indices[N-1]++)
{
result[N-1] = input[N-1][indices[N-1]];
yield return result;
}
}
}
Nested loops:
List<int> listA = (whatever), listB = (whatever);
var answers = new List<Tuple<int,int>>;
for(int a in listA)
for(int b in listB)
answers.add(Tuple.create(a,b));
// do whatever with answers
Try this:
Func<IEnumerable<string>, IEnumerable<string>> combine = null;
combine = xs =>
xs.Skip(1).Any()
? xs.First().SelectMany(x => combine(xs.Skip(1)), (x, y) => String.Format("{0}{1}", x, y))
: xs.First().Select(x => x.ToString());
var strings = new [] { "AB", "12", "$%" };
foreach (var x in combine(strings))
{
Console.WriteLine(x);
}
That gives me:
A1$
A1%
A2$
A2%
B1$
B1%
B2$
B2%
I made the following IEnumerable<IEnumerable<TValue>> class to solve this problem which allows use of generic IEnumerable's and whose enumerator returns all permutations of the values, one from each inner list. It can be conventiently used directly in a foreach loop.
It's a variant of Michael Liu's answer to IEnumerable and Recursion using yield return
I've modified it to return lists with the permutations instead of the single values.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Permutation
{
public class ListOfListsPermuter<TValue> : IEnumerable<IEnumerable<TValue>>
{
private int count;
private IEnumerable<TValue>[] listOfLists;
public ListOfListsPermuter(IEnumerable<IEnumerable<TValue>> listOfLists_)
{
if (object.ReferenceEquals(listOfLists_, null))
{
throw new ArgumentNullException(nameof(listOfLists_));
}
listOfLists =listOfLists_.ToArray();
count = listOfLists.Count();
for (int i = 0; i < count; i++)
{
if (object.ReferenceEquals(listOfLists[i], null))
{
throw new NullReferenceException(string.Format("{0}[{1}] is null.", nameof(listOfLists_), i));
}
}
}
// A variant of Michael Liu's answer in StackOverflow
// https://stackoverflow.com/questions/2055927/ienumerable-and-recursion-using-yield-return
public IEnumerator<IEnumerable<TValue>> GetEnumerator()
{
TValue[] currentList = new TValue[count];
int level = 0;
var enumerators = new Stack<IEnumerator<TValue>>();
IEnumerator<TValue> enumerator = listOfLists[level].GetEnumerator();
try
{
while (true)
{
if (enumerator.MoveNext())
{
currentList[level] = enumerator.Current;
level++;
if (level >= count)
{
level--;
yield return currentList;
}
else
{
enumerators.Push(enumerator);
enumerator = listOfLists[level].GetEnumerator();
}
}
else
{
if (level == 0)
{
yield break;
}
else
{
enumerator.Dispose();
enumerator = enumerators.Pop();
level--;
}
}
}
}
finally
{
// Clean up in case of an exception.
enumerator?.Dispose();
while (enumerators.Count > 0)
{
enumerator = enumerators.Pop();
enumerator.Dispose();
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
You can use it directly in a foreach like this:
public static void Main(string[] args)
{
var listOfLists = new List<List<string>>()
{
{ new List<string>() { "A", "B" } },
{ new List<string>() { "C", "D" } }
};
var permuter = new ListOfListsPermuter<string>(listOfLists);
foreach (IEnumerable<string> item in permuter)
{
Console.WriteLine("{ \"" + string.Join("\", \"", item) + "\" }");
}
}
The output:
{ "A", "C" }
{ "A", "D" }
{ "B", "C" }
{ "B", "D" }

How to short text alphabetical whit giving value for each character

I was thinking if good way short strings whit character value. Like A = 0, B = 1...
I made some code, but best i get is wrong answer and i think i know where is problem but i dont know what is the problem.
So here is code:
void Start()
{
i = Inventory.instance;
List<Items> items = i.returnList("Food");
Debug.Log(items.Count); List<Items> sItems = GetListSetList(items);
foreach (Items item in sItems)
{
Debug.Log(item.name);
}
}
List items = i.returnList ("Food"); <-- This part is where i ask inventory return all "Food" taged items. Code is here:
public List<Items> returnList(string tag)
{
List<Items> classifiedItemSet = new List<Items>();
foreach (Items i in items)
{
if (i.tag == tag)
{
classifiedItemSet.Add(i);
}
}
return classifiedItemSet;
}
List sItems = GetListSetList (items); <-- This is part where i really try short list what i get from inventory. It looks like this :
private List<Items> GetListSetList(List<Items> itemS)
{
foreach (Items item in itemS)
{
GetAlphabetaValue(item);
}
List<Items> shorted = new List<Items>();
Items[] hold = new Items[itemS.Count];
foreach (Items item in itemS)
{
for (int x = 0; x < hold.Length; x++)
{
if (hold[x] == null)
{
hold[x] = item;
break;
}
else
{
bool PassIt = true;
for (int c_value = 0; c_value < item.Alphabet_value.Length; c_value++)
if (item.Alphabet_value[c_value] > hold[x].Alphabet_value[c_value])
PassIt = false;
if (PassIt)
{
for (int h_pos = hold.Length - 1; h_pos > x; h_pos--)
if (hold[h_pos] != null)
hold[h_pos] = hold[h_pos - 1];
hold[x] = item;
break; // If i use this break i get error ("NullReferenceException")
}
else continue;
}
}
}
for (int x = 0; x < hold.Length; x++)
shorted.Add(hold[x]);
return shorted;
}
Start of this void i give every character in every items name string some value. It is this part : GetAlphabetaValue (item); Oh and sorry naming it AlphaBeta :) Okey how i get this values is this :
private void GetAlphabetaValue(Items x)
{
x.Alphabet_value = new int[x.name.Length];
for (int c = 0; c < x.Alphabet_value.Length; c++)
{
string character = x.name.Substring(c, 1);
character.ToLower();
switch (character)
{
case "a":
x.Alphabet_value[c] = 0;
break;
case "b":
x.Alphabet_value[c] = 1;
break;
case "c":
x.Alphabet_value[c] = 2;
break;
case "d":
x.Alphabet_value[c] = 3;
break;
case "e":
x.Alphabet_value[c] = 4;
break;
case "f":
x.Alphabet_value[c] = 5;
break;
//To the end
}
}
}
I hope you understand what i try to talk :D Thank you :) And i try find some information in internet before i start doing this, but i dont find anything how really can short multiple string from array.
This part i think i got wrong but now i cant just see what wrong whit that :
for (int h_pos = hold.Length - 1; h_pos > x; h_pos--)
if (hold [h_pos] != null)
{
hold [h_pos] = hold [h_pos - 1];
hold [x] = item;
}
use this Code.
string str = "Tamil";
List<char> list = str.ToList ();
list = list.OrderBy(x => x.ToString()).ToList();
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.ReadLine();
To start with do you really think it's a good idea to have a +20 switch-case?
Characters are already numbered, characters a-z in enligsh alphabet correspond to 97-122 chars in Unicode.
This function:
void GetAlphabetaValue(Items x)
{
x.Alphabet_value = new int[x.name.Length];
for (int c = 0; c < x.Alphabet_value.Length; c++)
{
string character = x.name.Substring (c, 1);
character.ToLower ();
switch (character)
{
case "a":
x.Alphabet_value [c] = 0;
break;
///Etc... //Etc..... And end.
}
}
}
Becomes this:
void GetAlphabetaValue2(Items x)
{
var CharIntList = List<int>;
foreach (char ch in x.Name)
CharIntList.Alphabet_value.Add(ch - 97);
x.Alphabet_value = CharIntList.ToArray();
}
Much simpler. Also you code is messy, hard to understand and bad formatted. Probably you are new to C# so you should read about how you are supposed to write code, It's not like I do it well, but other pepole should be able to understand your code.
Then about you question, I think you mean to sort not to short (Completly different things). Also why is Items in plural? it's a singular thing, then it should be Item.
Well, I don't know about your problem, but you can replace your GetListSetList() function to this:
private List<Items> GetListSetList(List<Items> items)
{
foreach (Items item in items)
{
GetAlphabetaValue(item);
Array.Sort(item.Alphabet_value);
}
return items;
}
Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Items> items = Items.returnList("Food");
var groups = items.GroupBy(x => x.name.Substring(0,1)).ToList();
}
}
public class Items
{
public static List<Items> items = new List<Items>();
public string name { get; set; }
public string type { get; set; }
public static List<Items> returnList(string type)
{
return items.Where(x => x.type == type).ToList();
}
}
}

Generate combinations of elements held in multiple list of strings in C#

I'm trying to automate the nested foreach provided that there is a Master List holding List of strings as items for the following scenario.
Here for example I have 5 list of strings held by a master list 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 below for loop regardless of the number of list items held by the master list lstMaster
Just do a cross-join with each successive 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 the unnecessary creation of additional lists that will be thrown away
with each iteration
The instinctual null is used so that the first cross-join will have something to build on (with strings, null + s = s)
To make this truly dynamic you need two arrays of int loop variables (index and count):
int numLoops = lstMaster.Count;
int[] loopIndex = new int[numLoops];
int[] loopCnt = new int[numLoops];
Then you need the logic to iterate through all these loopIndexes.
Init 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 big 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;
}
}
}
public static IEnumerable<IEnumerable<T>> GetPermutations<T>(this IEnumerable<IEnumerable<T>> lists)
{
IEnumerable<IEnumerable<T>> result = new List<IEnumerable<T>> { new List<T>() };
return lists.Aggregate(result, (current, list) => current.SelectMany(o => list.Select(s => o.Union(new[] { s }))));
}
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 starts from calculating how many combinations we need for all the sub lists.
When we know that we create a result array with exactly this number of entries. Then the algorithm iterates through all the sub lists, extract item from a sub list and calculates how many times the item should occur in the result and adds the item the specified number of times to the results. Moves to next item in the same list and adds to remaining fields (or as many as required if there is more than two items in the list). And it continues through all the sub lists and all the items.
One area though that needs improvement is when the list is empty. There is a risk of DivideByZeroException. I didn't add that. I'd prefer to focus on conveying the idea behind the calculations and didn't want to obfuscate it with additional checks.

Categories