Finding all combinations from sets of possibilities - c#

I have multiple sets of arrays that contain additional arrays that have values attached that I use for figuring out math. In order to find the best combination of these things, I need to mix and match from these arrays. I've seen "solutions" similar to this around, but they're usually 1 array deep with no real combinations/possibilities. So to give an example.
I have sets A, B, and C. Set A contains Aa, Ab, Ac, and Ad. Aa contains a set of values. Extrapolate that out for the others. Aa can only be compared with Ba and Ca. How do I go about writing a program to find all combinations(i.e. Aa, Ab, Cc, Bd compared with Ba, Cb, Ac, Bd and etc) so I can compare the math on each combination to find the best one? Note: this is just an example, I don't need it for specifically 3 sets of 4 sets of 4, it needs to be able to expand.
Now I know I didn't use very meaningful names for my variables, but I would appreciate if any code given does have meaningful names in it(I'd really rather not follow around variables of x and c around in code).

The accepted answer appears to be correct but is a very strange way to do a Cartesian product in C#. If you have a given number of sequences you can take their Cartesian product idiomatically like this:
var aList = new[] { "a1", "a2", "a3" };
var bList = new[] { "b1", "b2", "b3" };
var cList = new[] { "c1", "c2", "c3" };
var product = from a in aList
from b in bList
from c in cList
select new[] { a, b, c };
foreach (var p in product)
Console.WriteLine(string.Join(",", p));
If you have arbitrarily many sequences that you need to take their Cartesian product then you can do it like this:
static class Extensions
{
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
this 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}));
}
}
And then:
var aList = new[] { "a1", "a2", "a3" };
var bList = new[] { "b1", "b2", "b3" };
var cList = new[] { "c1", "c2", "c3" };
var lists = new[] { aList, bList, cList };
var product = lists.CartesianProduct();
foreach (var p in product)
Console.WriteLine(string.Join(",", p));
See
http://ericlippert.com/2010/06/28/computing-a-cartesian-product-with-linq/
and my answer to
Generating all Possible Combinations
for more discussion of this problem.

Assuming you are using a version of C# which supports LINQ:
static void Main(string[] args)
{
// declare some lists
var aList = new string[] { "a1", "a2", "a3" };
var bList = new string[] { "b1", "b2", "b3" };
var cList = new string[] { "c1", "c2", "c3" };
// do the equivalent of a SQL CROSS JOIN
var permutations = aList
.Join(bList, a => "", b => "", (a, b) => new string[] { a, b })
.Join(cList, ab => "", c => "", (ab, c) => new string[] { ab[0], ab[1], c });
// print the results
Console.WriteLine("Permutations:");
foreach (var p in permutations)
Console.WriteLine(string.Join(", ", p));
}
The Join calls with the lambda expressions pointing the strings to empty strings causes the Join function to treat the strings as equal, emulating a SQL CROSS JOIN.

Related

How can I create a list of each item in another list, and the count of how many times that item appears?

So I have a list of items -> A, B, C, D.
C and D are included more than once, and A and B, more than twice. This list can go on and on, so we do not know how many times an item will be included.
I need to create a new list that will have the item in one column and the number of instances of that item in another column, but I do not know how to do this. I may need to use a tuple or a class, but I am not fully sure how to implement either...
What you actually need is to Group the items of your list and perform a group operation, which is Count in your case to calculate how many times does it exist.
This is how you may initialize your list:
List<string> myList = new List<string>() { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B" };
and then you will group it using GroupBy function and apply the Count aggregate function on each group.
myList
.GroupBy(item => item)
.Select(g => new {Key = g.Key, Count = g.Count()})
.ToList();
This will result in the table you need.
You can try like this:
var myList = new List<String>() { "A","B", "C", "D","A","B", "C", "D", "A","B"};
var grp = myList.GroupBy( x => x );
foreach( var g in grp )
{
Console.WriteLine( "{0} {1}", g.Key, g.Count() );
}
DOTNET FIDDLE
char[] items = new[] { 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B' };
Dictionary<char, int> counts = new();
foreach(char c in items)
{
if (counts.TryGetValue(c, out int n))
{
counts[c] = n + 1;
}
else
{
counts.Add(c, 1);
}
}
While not a one liner, a simple and fast option.
I may need to use a tuple or a class, but I am not fully sure how to implement either...
Since you mentioned you may want to use a class, here is an example:
public class TextCount
{
public string Text { get; set; }
public int Count { get; set; }
}
public class Program
{
public static void Main()
{
// Initialize the list of strings
List<string> data = new List<string> { "A", "B", "C", "D", "A", "B", "C", "D", "A", "B" };
// Use LINQ to group the strings by their value and count the number of occurrences of each string
List<TextCount> result = data
.GroupBy(s => s)
.Select(g => new TextCount { Text = g.Key, Count = g.Count() })
.ToList();
// Print the results
foreach (TextCount sc in result)
{
Console.WriteLine("{0}: {1}", sc.Text, sc.Count);
}
}
}
Demo: https://dotnetfiddle.net/2FRBbK

Group array of string arrays with LINQ

I have array like this, values are string:
var arr1 = new [] { "H", "item1", "item2" };
var arr2 = new [] { "T", "thing1", "thing2" };
var arr3 = new [] { "T", "thing1", "thing2" };
var arr4 = new [] { "END", "something" };
var arr5 = new [] { "H", "item1", "item2" };
var arr6 = new [] { "T", "thing1", "thing2" };
var arr7 = new [] { "T", "thing1", "thing2" };
var arr8 = new [] { "END", "something" };
var allArrays = new [] { arr1, arr2, arr3, arr4, arr5, arr6, arr7, arr8 };
I need to group this in to a new array of arrays, so that one array has arrays that start with H or T. The END records (not included in the results) are the delimiters between each section; each new array starts after an END array.
In the end I would like to have somethng like this:
[
[ [H, item1, item2], [T, thing1, thing2], [T, thing1, thing2] ]
[ [H, item1, item2], [T, thing1, thing2], [T, thing1, thing2] ]
]
I know how I can do this with for each loop, but I'm looking for a cleaner way, possibly using linq. All suggestions are much valued, thank you!
you can try this
List<string[]> list = new List<string[]>();
var newArr = allArrays.Select(a => AddToArr(list, a)).Where(a => a != null);
and helper (this code can be put inline, but it easier to read this way)
private static string[][] AddToArr(List<string[]> list, string[] arr)
{
if (arr[0] != "END")
{
list.Add(arr);
return null;
}
var r = list.ToArray();
list.Clear();
return r;
}
result
[
[["H","item1","item2"],["T","thing1","thing2"],["T","thing1","thing2"]],
[["H","item3","item4"],["T","thing3","thing4"],["T","thing5","thing6"]]
]
So arr1, arr2, etc are string[].
allArrays is a string[][].
I hope you gave a meaningful example. From this example it seems that you want all string[] from allArrays, except the string[] that have a [0] that equals the word "END".
If this is what you want, your result is:
string[][] result = allArrays.Where(stringArray => stringArray[0] != "END");
I need to group this in to a new array of arrays, so that one array has arrays that start with H or T. The END records (not included in the results) are the delimiters between each section; each new array starts after an END array.
This is not exactly the same as I see in your example: what if one of the string arrays in allArrays is an empty array, or if it has the value null values. What if one of the the arrays of strings is empty (= length 0), and what if one of the string arrays doesn't start with "H", nor "T", nor "END"?
Literally you say that you only want the string arrays that start with "H" or "T", no other ones. You don't want string arrays that are null, nor empty string arrays. You also don't want string arrays that start with "END", nor the ones that start with String.Empty, or "A" or "B" or anything else than "H" or "T".
If I take your requirement literally, your code should be:
string[] requiredStringAtIndex0 = new string[] {"H", "T"};
string[][] result = allArrays.Where(stringArray => stringArray != null
&& stringArray.Length != 0
&& requiredStringAtIndex0.Contains(stringArray[0]));
In words: from allArrays, keep only those arrays of strings, that are not null, AND that have at least one element AND where the element at index 0 contains either "H" or "T"
Normally I would use an extension method for grouping runs of items based on a predicate, in this case GroupByEndingWith and then throw away the "END" record, like so:
var ans = allArrays.GroupByEndingWith(r => r[0] == "END")
.Select(g => g.Drop(1).ToArray())
.ToArray();
But, in general, you can use Aggregate to collect items based on a predicate at the expense of comprehension. It often helps to use a tuple to track an overall accumulator and a sub-accumulator. Unfortunately, there is no + operator or Append for List<T> that returns the original list (helpful for expression based accumulation) and since C# doesn't yet have a comma operator equivalent, you need an extension method again or you can use ImmutableList.
Using Aggregate and ImmutableList, you can do:
var ans = allArrays.Aggregate(
(ans: ImmutableList<ImmutableList<string[]>>.Empty, curr: ImmutableList<string[]>.Empty),
(ac, r) => r[0] == "END"
? (ac.ans.Add(ac.curr), ImmutableList<string[]>.Empty)
: (ac.ans, ac.curr.Add(r))
).ans
.Select(l => l.ToArray())
.ToArray();
NOTE: You can also do this with List if you are willing to create new Lists a lot:
var ans = allArrays.Aggregate(
(ans: new List<List<string[]>>(), curr: new List<string[]>()),
(ac, r) => r[0] == "END"
? (ac.ans.Concat(new[] { ac.curr }).ToList(), new List<string[]>())
: (ac.ans, ac.curr.Concat(new[] { r }).ToList())
).ans
.Select(l => l.ToArray())
.ToArray();
Here is a simple implementation.
public static void Main(string[] args)
{
var data = ConvertToArrayOfArray(arr1, arr2, arr3, arrr4, arr5, arr6, arr7, arr8);
}
private string[][] ConvertToArrayOfArray(params string[][] arrs)
{
List<string[]> yoList = new List<string[]>();
arrs.ToList().ForEach(x =>
{
if(!x[0] == "END") yoList.Add(x);
});
return yoList.ToArray();
}

How to split the elements ​of an array of strings if they are equal?

Good day,
I currently have a string array like this:
string[] array = {"aa","bb","cc","dd","aa","cc","ee","ff","aa","bb"}
I would like to be able to get the positions that are the same from the same string [], example:
string[] a = {"aa","aa","aa"}
string[] b = {"bb","bb"}
string[] c = {"cc","cc"}
string[] d = {"dd"}
string[] e = {"ee"}
string[] f = {"ff"}
It should be noted that the elements of the parent matrix always change and are not always the same.
I tried with linq, but I don't get what I'm looking for.
this was my attempt with linq:
array.Where(x => array.Contains(x)).ToArray();
Thanks for help me!
Despite seeing what you ask for, the result you want is pretty limited an not useful to work with later on. You should take advantage of using the GroupBy in linq and then when you need something find it in that collection.
// your array
string[] array = {"aa","bb","cc","dd","aa","cc","ee","ff","aa","bb"};
// group by value
var groupedValues = array.GroupBy(x => x).ToList();
// get the "aa" group if exist
var aa = groupedValues.FirstOrDefault(x => x.Key == "aa");
// check if the group was found
if(aa != null)
{
// get all "aa" values in that group. This return this collection based on your inpit{ "aa", "aa", "aa" }
var allaaValues = aa.ToList();
}
Hope this is a solution you were looking for, good luck!
string[] array = { "aa", "bb", "cc", "dd", "aa", "cc", "ee", "ff", "aa", "bb" };
var splittedArray = new List<string[]>();
foreach (var strItem in array)
{
//Don't iterating duplicates
if (splittedArray.Any(si => si.Contains(strItem))) continue;
//if more then one item exists in the array getting those identic items and adding to the array list
if (array.Count(si => si.Equals(strItem)) > 0)
{
var identicItems = array
.Where(i => i.Equals(strItem))
.ToArray();
splittedArray.Add(identicItems);
}
else // Adding single item as a new array with this item
{
splittedArray.Add(new string[] { strItem });
}
}

C# merge two lists into one list like Enumerable.ConvertAll

I want to convert two lists with the same index into a single list.
Given this:
List<double> ListA = new List<double> { 1, 2, 3, 4, 5 };
List<string> ListB = new List<string> { "A","B","C","D","E" };
until now, I used this:
List<string> UNHAPPY = new List<string>();
for (int ListIndex = 0; ListIndex < ListA.Count; ListIndex++)
{
UNHAPPY.Add(ListB[ListIndex]+ListA[ListIndex].ToString());
}
// such that UNHAPPY == new List<string> {"A1", "B2", "C3", "D4", "E5"}
But I really want to use short code as possible, like this (Similar to Enumerable.ConvertAll):
List<string> HAPPY = SOME_CONTAINER(ListA, ListB).SOME_SELECTOR((a,b) => b + a.ToString());
// such that HAPPY == new List<string> {"A1", "B2", "C3", "D4", "E5"}
Is there any quick method for this? Thank you so mush in advance!
LINQ has a method for this, it's called Zip:
var res = ListA.Zip(ListB, (a,b) => $"{b}{a}");
It takes two sequences and a Func delegate, and applies the delegate to pairs of items coming from the two sequences.
Note: call of ToString on a is redundant, C# will concatenate a string to anything, including an int, by calling ToString on the corresponding object. I prefer string interpolation, especially when you need to concatenate more than two items.
You are looking for Enumerable.Zip
first.Zip(second, (f, s) => f + s.ToString());
You can do this with a Zip:
ListA.Zip(ListB, (a,b) => b + a.ToString());
This produces:
csharp> List<double> ListA = new List<double> { 1, 2, 3, 4, 5 };
csharp> List<string> ListB = new List<string> { "A","B","C","D","E" };
csharp> ListA.Zip(ListB, (a,b) => b + a.ToString());
{ "A1", "B2", "C3", "D4", "E5" }
Zip itererates over the two iterables concurrently and produces the product constructed by the function. From the moment one of the iterables is exhausted, the Zip stops.

Linq query to get shared items in a sublist

I have a class that has a property which is a List, I will name this class A. Then I have a List<A>.
I need a LINQ for objects to get all the objects B that are present on ALL the items on the List<A>.
Example to clarify:
var list = new List<A>
{
new A { B = new List<B> { B1, B2, B3, B4 } }
new A { B = new List<B> { B3, B4, B5, B6 } }
new A { B = new List<B> { B2, B3, B4, B5, B6 } }
};
The query must return the objects B3 and B4 because are the only ones contained on all the List<A> objects.
If you have a list of lists and you want the elements that are in all the inner lists, you can use a combination of Aggregate and Intersect, like this:
IEnumerable<IEnumerable<string>> listOfLists = new string[][] {
new string[] { "B1", "B2", "B3", "B4" },
new string[] { "B3", "B4", "B5", "B6" },
new string[] { "B2", "B3", "B4", "B5", "B6" }
};
IEnumerable<string> commonElements = listOfLists.Aggregate(Enumerable.Intersect);
You can just use Intersect() provided there is at least one element and your class B has an adequate implementation of equality / GetHashCode():
IEnumerable<B> allBs = list[0].B;
foreach (var item in list.Skip(1))
{
allBs = allBs.Intersect(item.B);
}
I see no benefit of a "pure" Linq solution here besides adding complexity.
As long as the Equals/GetHashCode are properly defined for objects of type B, then this is actually somewhat simple:
_listToQuery.Aggregate(
_listToQuery.First().B,
(seed, nextItem) => { seed = seed.Intersect(nextItem.B); return seed; })
Or, another way:
_listToQuery.SelectMany(tr => tr.B)
.GroupBy(tr => tr)
.Where(tr => tr.Count() == _listToQuery.Count)
.Select(tr => tr.Key)
The way I would approach this is to create a list of all the B's then find out which ones occur more than once by grouping them by some unique identifier (or just the object if they are comparable). Once they are grouped, select those where the number of instances is greater than 1, using the first instance of each grouping as the canonical representative.
var selection = list.SelectMany( a => a.B )
.GroupBy( b => b.UniqueID, b => b )
.Where( b => b.Count() > 1 )
.Select( b => b.First() );

Categories