Using linq to group/aggregate by string[] - c#

Suppose I have a list with a Tuple.First as a string[] (i.e. my key):
var lst = new List<Tuple<string[], int>> {
new Tuple<string[], int>(new string[] { "A", "B" }, 5),
new Tuple<string[], int>(new string[] { "A", "B" }, 10),
new Tuple<string[], int>(new string[] { "C", "B" }, 10),
}
I would like to aggregate (e.g. Sum) by Tuple.First (i.e. string[]) so expecting an output as follows:
var output = new List<Tuple<string[], int>> {
new Tuple<string[], int>(new string[] { "A", "B" }, 15),
new Tuple<string[], int>(new string[] { "C", "B" }, 10),
}
I did it this way but there must be a cleaner way to do it instead of forcing a pipe concatenation:
var output = lst
.GroupBy(x => string.Join("|", x.First))
.Select(x => new Tuple<string[], int>(
x.Key.Split('|'),
Sum(x => x.Second)));

You need to implement IEqualityComparer for string[]. My example using touples (You don't say what Pair is):
static void Main(string[] args)
{
var lst = new List<(string[] arr, int num)> {
(new string[] { "A", "B" }, 5),
(new string[] { "A", "B" }, 10),
(new string[] { "C", "B" }, 10),
};
var grouped = lst.GroupBy(pair => pair.arr, new EnumerableComparer());
var sums = grouped.Select(g => (arr: g.Key, num: g.Sum(p => p.num)));
}
class EnumerableComparer<TRecord> : IEqualityComparer<IEnumerable<TRecord>>
{
public bool Equals(IEnumerable<TRecord> x, IEnumerable<TRecord> y)
{
return ReferenceEquals(x, y) || x != null && x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<TRecord> lst)
{
unchecked
{
int hash = 387;
foreach (var elem in lst)
{
hash = hash * 31 + elem.GetHashCode();
}
return hash;
}
}
}

Related

Combining values of two dictionaries in C# with distinct keys

I am having difficulty combining two dictionaries into a dictionary with two values combined to a list for identical keys. For example, having D1 and D2
D1 = {2:"a",
3:"b",
4: "c"}
D2 = {1:"e",
2:"f",
4:"h",
5:"i"}
I would like to create D3.
D3= { 1:["", "e"]
2:["a", "f"]
3:["b", ""]
4:["c":"h"]
5:["", "i"]}
Thank you.
This can be done in a single Linq expression like so:
Flatten and concatenate both d1 and d2 to a single flat sequence of (Int32,String) value-tuples.
Re-group them by the Int32 key (this is the main step).
Then convert each group into a separate output dictionary entry.
Dictionary<Int32,String> d1 = new Dictionary<Int32,String>()
{
{ 2, "a" },
{ 3, "b" },
{ 4, "c" },
};
Dictionary<Int32,String> d2 = new Dictionary<Int32,String>()
{
{ 1, "e" },
{ 2, "f" },
{ 4, "h" },
{ 5, "i" },
};
Dictionary<Int32,List<String>> d3 = Array
.Empty<( Int32 k, String v )>()
// Step 1:
.Concat( d1.Select( kvp => ( k: kvp.Key, v: kvp.Value ) ) )
.Concat( d2.Select( kvp => ( k: kvp.Key, v: kvp.Value ) ) )
// Step 2:
.GroupBy( t => t.k )
// Step 3:
.ToDictionary(
grp => grp.Key,
grp => grp.Select( t => t.v ).OrderBy( v => v ).ToList()
);
An advantage of this approach is that it works for any number of duplicated values (not just two). Also, the use of ValueTuple means this approach should have fewer heap-allocations.
Screenshot proof of it working in LinqPad:
The expression can be made more succint - I use a more verbose style myself, but if you want to be cryptic about it by re-using KeyValuePair instead of ValueTuple, and if you don't care about ordering, then you can do this:
var d3 = d1
.Concat( d2 )
.GroupBy( kvp => kvp.Key )
.ToDictionary( g => g.Key, g => g.Select( kvp => kvp.Value ).ToList() );
Simplest solution would be with Dictionary.Keys
var D1 = new Dictionary<int,string>(){{2,"a"}, {3,"b"},{4,"c"}};
var D2 = new Dictionary<int,string>(){{1,"e"},{2,"f"}, {4,"h"},{5,"i"}};
var keys = D1.Keys.Union(D2.Keys).OrderBy(key => key);
var test = keys.Select(key => new {Key = key, Value= new string[] {D1.ContainsKey(key) ? D1[key] : "", D2.ContainsKey(key) ? D2[key] : ""} });
Console.WriteLine(test);
Interactive: https://rextester.com/UXQ51844
Alternatively, you could do something similar to this: LINQ - Full Outer Join
Sounds to be a job for LINQ. Here is one possibility to solve this issue:
public class Element
{
public int Index { get; set; }
public string Value { get; set; }
}
public class GroupedElement
{
public int Index { get; set; }
public IReadOnlyList<string> Values { get; set; }
}
public static class Program
{
public static void Main(string[] args)
{
var d1 = new[]
{
new Element { Index = 2, Value = "a" },
new Element { Index = 3, Value = "b" },
new Element { Index = 4, Value = "c" },
};
var d2 = new[]
{
new Element { Index = 1, Value = "e" },
new Element { Index = 2, Value = "f" },
new Element { Index = 4, Value = "h" },
new Element { Index = 5, Value = "i" },
};
var result = d1.Concat(d2)
.GroupBy(element => element.Index)
.Select(group => new GroupedElement { Index = group.Key, Values = group.Select(g => g.Value).ToList() })
.ToList();
foreach (var item in result)
{
Console.WriteLine($"{item.Index}: {string.Join(",", item.Values)}");
}
}
}
I think this is simpler without LINQ (LINQ is a hammer, not every problem is a nail)
Let's loop from 1 to 5, putting a new List for each int. The list is inited with d1's value and d2's value
var d3 = new Dictionary<int, List<string>>();
for(int x=1;x<6;x++)
d3[x] = new() { d1.GetValueOrDefault(x,""), d2.GetValueOrDefault(x,"") };
If your ints aren't always contiguous you could (use a bit of LINQ 😀 and..)
foreach(int x in d1.Keys.Union(d2.Keys))
d3[x] = new() { d1.GetValueOrDefault(x,""), d2.GetValueOrDefault(x,"") };
This doesn't need another answer the others answers are plenty good enough and well done, however here is another (convoluted) approach
Given
var d1 = new Dictionary<int, string> {{2, "a"}, {3, "b"}, {4, "c"}};
var d2 = new Dictionary<int, string> { { 1, "e" }, { 2, "f" }, { 4, "h" }, { 5, "i" } };
Usage
static string[] Stuff((string, string)[] v) =>
new[] {v[0].Item1 ?? v.ElementAtOrDefault(1).Item1 ?? "", v[0].Item2 ?? v.ElementAtOrDefault(1).Item2 ?? "" };
var result = d1
.Select(x => (x.Key, (x.Value,(string)null)))
.Concat(d2.Select(x => (x.Key, ((string)null, x.Value ))))
.GroupBy(element => element.Key)
.ToDictionary(x => x.Key, x => Stuff(x.Select(y =>y.Item2).ToArray()))
Output
foreach (var item in result.OrderBy(x => x.Key))
Console.WriteLine($"{item.Key}: {string.Join(",", item.Value)}");
---
1: ,e
2: a,f
3: b,
4: c,h
5: ,i
Please see, here is another approach for this query -
Input -
Dictionary<int, string> first_dict = new Dictionary<int, string>()
{
{ 2,"a" },
{ 3,"b" },
{ 4, "c"}
};
Dictionary<int, string> second_dict = new Dictionary<int, string>()
{
{ 1,"e" },
{ 2,"f" },
{ 4, "h"},
{ 5, "i"}
};
First, I got common keys from both dictionaries like this -
var allKeys = first_dict.Concat(second_dict).OrderBy(b => b.Key).Select(b => b.Key).Distinct().ToList();
and then I created two another dictionaries and inserted data into them like this -
Dictionary<int, string> first_dict_res = new Dictionary<int, string>();
Dictionary<int, string> second_dict_res = new Dictionary<int, string>();
foreach (var keyItem in allKeys)
{
var first_dict_res_value = (first_dict.ContainsKey(keyItem)) ? first_dict[keyItem] : null;
first_dict_res.Add(keyItem, first_dict_res_value);
var second_dict_res_value = (second_dict.ContainsKey(keyItem)) ? second_dict[keyItem] : null;
second_dict_res.Add(keyItem, second_dict_res_value);
}
and then I concatenated the result from both dictionaries to get the desired result-
var res_dict = first_dict_res.Concat(second_dict_res).GroupBy(b => b.Key)
.Select(c => new { key = c.Key, values = string.Join(",", c.Select(b => b.Value)) }).ToList();

Cross join of unknown number of string arrays using linq

Is it possible to do a cross join using linq where the number of joins is not known in advance?
I have this:
var arrays = new List<string[]>();
If I know I have three lists I can do:
var oQuery = from x in arrays[0]
from y in arrays[1]
from z in arrays[2]
select new {x, y, z};
Is it be possible to join n string arrays using linq?
Try this solution, where each item from result will not be look like {x:"A", y:"B", ... }, because number of properties is not predicted, so instead, it will be like ["A", "B", ... ]:
public static List<List<string>> CrossJoin(List<string[]> arrays)
{
var data = arrays.Select(x => x.ToList()).ToList();
List<List<string>> result = data[0].Select(x => new List<string> { x }).ToList();
for (var i = 1; i < data.Count; i++)
result = (from a in result
from b in data[i]
select new { a, b })
.Select(x => x.a.Concat(new List<string> { x.b }).ToList())
.ToList();
return result;
}
Usage:
var arr1 = new[] { "A", "B", "C" };
var arr2 = new[] { "D", "E" };
var arr3 = new[] { "F", "G" };
var result = CrossJoin(new List<string[]> { arr1, arr2, arr3 });
for(var i = 0; i < result.Count; i++)
Console.WriteLine(string.Format("{0}: {1}", i + 1, string.Join(",", result[i])));
Please try below solution to get Cartesian product for given "N" number of string arrays.
static void Main(string[] args)
{
var arrays = new List<string[]>();
var arr1 = new string[] { "A", "B", "C" };
var arr2 = new string[] { "D", "E", "F" };
var arr3 = new string[] { "G", "H", "I" };
arrays.Add(arr1);
arrays.Add(arr2);
arrays.Add(arr3);
IEnumerable<Tuple<string,string>> oQuery1 = null;
int count = arrays.Count;
var k = arrays[0].AsEnumerable();
for (int i =1; i< count; i++)
{
var l1 = arrays[i];
oQuery1 = k.SelectMany((x) => l1, (x, y) => Tuple.Create( x, y ));
k = oQuery1.Select(x=>x.ToString());
}
}
[Fact]
public void Test1()
{
var listOfStringList = new List<List<string>>();
var list1 = new List<string> { "A", "B", "C" };
var list2 = new List<string> { "AA", "BB", "CC" };
var list3 = new List<string> { "AAA", "BBB", "CCC" };
listOfStringList.Add(list1);
listOfStringList.Add(list2);
listOfStringList.Add(list3);
var resultData = new List<List<string>>();
listOfStringList.ForEach((stringList) =>
{
if (resultData.Count == 0)
{
resultData = stringList.Select(u => new List<string> { u }).ToList();
return;
}
resultData = resultData.SelectMany(u => stringList
.Select(v =>
{
var list = new List<string>();
u.ForEach(sv =>
{
list.Add(sv);
});
list.Add(v);
return list;
}).ToList()).ToList();
});
// Ignore variable resultantData it is just for highlight
var resultantData = resultData;
}

Sort a List and keep a particular element at end of list after sorting

I have a list of string containing "Others". I am getting this list for drop down. I am sorting this list alphabetically. But I need "Others" always at end of list. I don't want to add this element after sorting which is one solution. Is there any other way to do the same like by using custom comparer of .Sort() method. I tried like below but no solution.
public class EOComparer : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null)
{
if (y == null)
{
// If x is null and y is null, they're
// equal.
return 0;
}
else
{
// If x is null and y is not null, y
// is greater.
return -1;
}
}
else
{
// If x is not null...
//
if (y == null)
// ...and y is null, x is greater.
{
return 1;
}
else
{
if (x.ToLower().Contains("other"))
{
return -1;
}
else
{
// If the strings are of equal length,
// sort them with ordinary string comparison.
//
return x.CompareTo(y);
}
}
}
}
and calling it as :
EOComparer c = new EOComparer();
a.Sort((x, y) => c.Compare(x.OptionValue, y.OptionValue));
return a;
Please help if it is possible.
Use this logic
List<string> l = new List<string>{ "z", "y", "x", "other", "b", "a", "c" };
var result = l.OrderBy(i => i == "other").ThenBy(i => i).ToList();
result.ForEach(Console.WriteLine);
Output:
a b c x y z other
If you want other to be on top of the list make it
var result = l.OrderBy(i => i != "other").ThenBy(i => i).ToList();
Output:
other a b c x y z
This is a fine answer, but I thought I would fix your comparer:
Test:
[TestCase(new string[0], new string[0])]
[TestCase(new[] { "a" }, new[] { "a" })]
[TestCase(new[] { "a", "b" }, new[] { "a", "b" })]
[TestCase(new[] { "b", "a" }, new[] { "a", "b" })]
[TestCase(new[] {"others"}, new[] {"others"})]
[TestCase(new[] {"a", "others"}, new[] {"a", "others"})]
[TestCase(new[] {"others", "a"}, new[] {"a", "others"})]
[TestCase(new[] {"others", "x"}, new[] {"x", "others"})]
[TestCase(new[] {"Others", "x"}, new[] {"x", "Others"})]
[TestCase(new[] { "othersz", "others" }, new[] { "othersz", "others" })]
[TestCase(new[] {"z", "y", "x", "others", "b", "a", "c"},
new[] {"a", "b", "c", "x", "y", "z", "others"})]
public void CanSortWithOthersAtEnd(string[] input, string[] expectedSorted)
{
var a = new List<string>(input);
var c = new EOComparer();
a.Sort(c.Compare);
CollectionAssert.AreEqual(expectedSorted, a);
}
Comparer:
public sealed class EOComparer : IComparer<string>
{
public int Compare(string x, string y)
{
if (IsOthers(x)) return 1;
if (IsOthers(y)) return -1;
return string.Compare(x, y, StringComparison.Ordinal);
}
private static bool IsOthers(string str)
{
return string.Compare(str, "others", StringComparison.OrdinalIgnoreCase) == 0;
}
}
Note how my use of string.Compare avoids all == null checks. And how StringComparison.OrdinalIgnoreCase avoids .ToLower() and thus avoids creating copies of the strings.
Suppose we have objects with the following list items.
object.Text = "a"
object.Value = 1
object.Text = "b"
object.Value = 2
object.Text = "other"
object.Value = 3
object.Text = "c"
object.Value = 4
the following works for me:
var oList = objects.OrderBy(i => i.Text != "other").ThenBy(i => i.Text);

Sort by numbers first

I have a list as under
var x = new List<string>() { "a", "1", "b", "2" };
var sortedResultByNumeric = x
.Select(s => new { OriginalString = s,
ExtractNumbers = String.Join("", s.Where(Char.IsDigit)) })
.OrderBy(o => o.ExtractNumbers).ToList();
The output is
a
b
1
2
But expected is
1
2
a
b
How can I do so?
The output that you see and expected is based on OriginalString because ExtractNumbers is "","","1","2" then you should OrderBy OriginalString:
var x = new List<string>() { "a", "1", "b", "2" };
var sortedResultByNumeric = x
.Select(s => new { OriginalString = s, ExtractNumbers = String.Join("", s.Where(Char.IsDigit)) })
.OrderBy(o => o.OriginalString).ToList();
The output:
1
2
a
b
You can try this:
var myList = new List<string>() { "a", "1", "b", "2", "123", "cd", "12346", "657" };
var nonNumericItems = myList.Where(item => !item.Any(i => Char.IsDigit(i)))
.OrderBy(item => item);
var numericItems = myList
.Select(item => String.Join("", item.Where(Char.IsDigit)))
.Where(item => !String.IsNullOrEmpty(item))
.Select(item => Convert.ToInt32(item))
.OrderBy(item => item)
.Select(item => item.ToString());
var result = numericItems
.Union(nonNumericItems);
result.ToList()
.ForEach(Console.WriteLine);
The output is:
1, 2, 123, 657, 12346, a, b, cd
P.S: You didn't tell us any additional explanation about your logic here String.Join("", item.Where(Char.IsDigit). So, I didn't ask any additional question about that.
If your list contains only 1-char strings you can order them by their char numerical values:
var x = new List<string>() { "a", "1", "b", "2" };
var sorted = x.OrderBy(c => Convert.ToChar(c))
.ToList();
foreach (var c in sorted)
Console.WriteLine(c);
// 1
// 2
// a
// b
This solution may seem complicated but it will work with any Net built in sort method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication63
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
List<AlphaNumberic> aN = new List<AlphaNumberic>() { "a", "1", "b", "2" };
aN.Sort((x, y) => x.CompareTo(y));
}
}
public class AlphaNumberic
{
int results = 0;
public string alphaNumeric { get; set; }
public AlphaNumberic(string value)
{
this.alphaNumeric = value;
}
public static implicit operator string(AlphaNumberic d)
{
return d.alphaNumeric;
}
public static implicit operator AlphaNumberic(string d)
{
return new AlphaNumberic(d);
}
public int CompareTo(AlphaNumberic compareItem)
{
int thisNum;
int num;
if (int.TryParse(compareItem.alphaNumeric, out num))
{
if (int.TryParse(this.alphaNumeric, out thisNum))
{
//alphaNumeric and compareItem both integers;
results = thisNum.CompareTo(num);
}
else
{
//alphaNumeric not an integer and compareItem integers;
results = 1;
}
}
else
{
if (int.TryParse(this.alphaNumeric, out thisNum))
{
//alphaNumeric is an integer and compareItem not an integers;
results = -1;
}
else
{
//alphaNumeric not an integer and compareItem not an integers;
results = alphaNumeric.CompareTo(compareItem);
}
}
return results;
}
}
}

Generate all Combinations from Multiple (n) Lists

EDIT: I am completely redoing my questions as I have figured out the simplest way of asking it. Thanks to the commenters so far that got me thinking about the root problem.
public List<string> GetAllPossibleCombos(List<List<string>> strings)
{
List<string> PossibleCombos = new List<string>();
//????
{
string combo = string.Empty;
// ????
{
combo += ????
}
PossibleCombos.Add(combo);
}
return PossibleCombos;
}
I need to figure out how to recursively go through each List<string> and combine 1 string from each list into a combo string. Don't worry too much about formatting the string as the "live" code uses a custom object instead. Also, feel free to assume that every list will contain at least 1 string and that there are no null values.
Here is a simple non-recursive solution that just concatenates the elements of each combination:
public static List<string> GetAllPossibleCombos(List<List<string>> strings)
{
IEnumerable<string> combos = new [] { "" };
foreach (var inner in strings)
combos = from c in combos
from i in inner
select c + i;
return combos.ToList();
}
static void Main(string[] args)
{
var x = GetAllPossibleCombos(
new List<List<string>>{
new List<string> { "a", "b", "c" },
new List<string> { "x", "y" },
new List<string> { "1", "2", "3", "4" }});
}
You could generalize this to return an IEnumerable<IEnumerable<string>>, which allows the caller to apply any operation they like for transforming each combination into a string (such as the string.Join below). The combinations are enumerated using deferred execution.
public static IEnumerable<IEnumerable<string>> GetAllPossibleCombos(
IEnumerable<IEnumerable<string>> strings)
{
IEnumerable<IEnumerable<string>> combos = new string[][] { new string[0] };
foreach (var inner in strings)
combos = from c in combos
from i in inner
select c.Append(i);
return combos;
}
public static IEnumerable<TSource> Append<TSource>(
this IEnumerable<TSource> source, TSource item)
{
foreach (TSource element in source)
yield return element;
yield return item;
}
static void Main(string[] args)
{
var combos = GetAllPossibleCombos(
new List<List<string>>{
new List<string> { "a", "b", "c" },
new List<string> { "x", "y" },
new List<string> { "1", "2", "3", "4" }});
var result = combos.Select(c => string.Join(",", c)).ToList();
}
Hope this helps.
class NListBuilder
{
Dictionary<int, List<string>> tags = new Dictionary<int, List<string>>();
public NListBuilder()
{
tags.Add(1, new List<string>() { "A", "B", "C" });
tags.Add(2, new List<string>() { "+", "-", "*" });
tags.Add(3, new List<string>() { "1", "2", "3" });
}
public List<string> AllCombos
{
get
{
return GetCombos(tags);
}
}
List<string> GetCombos(IEnumerable<KeyValuePair<int, List<string>>> remainingTags)
{
if (remainingTags.Count() == 1)
{
return remainingTags.First().Value;
}
else
{
var current = remainingTags.First();
List<string> outputs = new List<string>();
List<string> combos = GetCombos(remainingTags.Where(tag => tag.Key != current.Key));
foreach (var tagPart in current.Value)
{
foreach (var combo in combos)
{
outputs.Add(tagPart + combo);
}
}
return outputs;
}
}
}
In case it helps anyone, here is the method syntax version for Douglas's GetAllPossibleCombos method.
public static List<string> GetAllPossibleCombos(List<List<string>> strings)
{
IEnumerable<string> combos = new[] { "" };
foreach (var inner in strings)
{
combos = combos.SelectMany(r => inner.Select(x => r + x));
}
return combos.ToList();
}
Here is a generic version that works with all object types:
public static List<List<T>> GetAllPossibleCombos<T>(List<List<T>> objects)
{
IEnumerable<List<T>> combos = new List<List<T>>() { new List<T>() };
foreach (var inner in objects)
{
combos = combos.SelectMany(r => inner
.Select(x => {
var n = r.DeepClone();
if (x != null)
{
n.Add(x);
}
return n;
}).ToList());
}
// Remove combinations were all items are empty
return combos.Where(c => c.Count > 0).ToList();
}
If you provide null values it will also give you empty combination. For example:
var list1 = new List<string>() { "1A", "1B", null };
var list2 = new List<string>() { "2A", "2B", null };
var output = GetAllPossibleCombos(allLists);
Will contain:
[["1A"], ["1B"], ["2A"], ["2B"], ["1A", "2A"], ["1A", "2B"], ["1B", "2A"], ["1B," "2B"]]
Rather than just:
var list1 = new List<string>() { "1A", "1B" };
var list2 = new List<string>() { "2A", "2B" };
var output = GetAllPossibleCombos(allLists);
[["1A", "2A"], ["1A", "2B"], ["1B", "2A"], ["1B," "2B"]]
Note: DeepClone is an extension method used for copying the list. This can be done in many ways
public static T DeepClone<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
Here is an answer that would work for any generic type, comes with a function to convert base-10 to base-n as well.
public static class IntExt
{
const string Symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static string ToString(this int value, int toBase)
{
switch (toBase)
{
case 2:
case 8:
case 10:
case 16:
return Convert.ToString(value, toBase);
case 64:
return Convert.ToBase64String(BitConverter.GetBytes(value));
default:
if (toBase < 2 || toBase > Symbols.Length)
throw new ArgumentOutOfRangeException(nameof(toBase));
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value));
int resultLength = 1 + (int)Math.Max(Math.Log(value, toBase), 0);
char[] results = new char[resultLength];
int num = value;
int index = resultLength - 1;
do
{
results[index--] = Symbols[num % toBase];
num /= toBase;
}
while (num != 0);
return new string(results);
}
}
}
public class UnitTest1
{
public static T[][] GetJoinCombinations<T>(T[][] arrs)
{
int maxLength = 0;
int total = 1;
for (int i = 0; i < arrs.Length; i++)
{
T[] arr = arrs[i];
maxLength = Math.Max(maxLength, arr.Length);
total *= arr.Length;
}
T[][] results = new T[total][];
int n = 0;
int count = (int)Math.Pow(maxLength, arrs.Length);
for (int i = 0; i < count; i++)
{
T[] combo = new T[arrs.Length];
string indices = i.ToString(maxLength).PadLeft(arrs.Length, '0');
bool skip = false;
for (int j = 0; j < indices.Length; j++)
{
T[] arr = arrs[j];
int index = int.Parse(indices[j].ToString());
if (index >= arr.Length)
{
skip = true;
break;
}
combo[j] = arr[index];
}
if (!skip)
results[n++] = combo;
}
return results;
}
[Fact]
public void Test1()
{
string[][] results = GetJoinCombinations(new string[][]
{
new string[] { "1", "2", "3" },
new string[] { "A", "B", "C" },
new string[] { "+", "-", "*", "/" },
});
}
}
public static IEnumerable<int[]> GetAllPossibleCombos(List<int[]> ints)
=> _getAllPossibleCombos(ints, 0, new List<int>());
private static IEnumerable<int[]> _getAllPossibleCombos(IReadOnlyList<int[]> ints, int index, IReadOnlyCollection<int> current)
{
return index == ints.Count - 1
? ints[index]
.Select(_ => new List<int>(current) {_}.ToArray())
: ints[index]
.SelectMany(_ => _getAllPossibleCombos(ints, index + 1, new List<int>(current) {_}));
}

Categories

Resources