Meshing IEnumerable<T> Lists Together - c#

I am looking for the most efficient way to mesh all the rows in IEnumerable<T> A before each row in IEnumerable<T> B.
For example:
A = {A, B}
B = {1, 2, 3}
After meshing:
B = {A, 1, B, 2, A, 3}

There is no easy solution, but something like this might work. The modulus operator is very important here, to repeat the results from the list with less items.
public static List<object> Mesh<T1, T2>(IEnumerable<T1> s1, IEnumerable<T2> s2)
{
T1[] array1 = s1.ToArray();
T2[] array2 = s2.ToArray();
int length1 = array1.Length;
int length2 = array2.Length;
int maxLength = Math.Max(length1, length2);
List<object> result = new List<object>();
for (int i = 0; i < maxLength; i++)
{
result.Add(array1[i % length1]);
result.Add(array2[i % length2]);
}
return result.
}

That solution uses IEnumerable parameters directly without initializing them and also returns an IEnumerable.
public IEnumerable<object> Mesh<S,T>(IEnumerable<S> items1, IEnumerable<T> items2){
bool items1Empty;
bool items2Empty;
bool items1Finished = items1Empty = ValidateParameter(items1, "items1");
bool items2Finished = items2Empty = ValidateParameter(items2, "items2");
using(var items1Enumerator = items1.GetEnumerator()){
using(var items2Enumerator = items2.GetEnumerator()){
while(true){
MoveNext(items1Enumerator, ref items1Finished);
MoveNext(items2Enumerator, ref items2Finished);
if(items1Finished && items2Finished)
break;
if(!items1Empty)
yield return items1Enumerator.Current;
if(!items2Empty)
yield return items2Enumerator.Current;
}
}
}
}
private bool ValidateParameter<T>(IEnumerable<T> parameter, string parameterName){
if(parameter == null)
throw new ArgumentNullException(parameterName);
return !parameter.Any();
}
private void MoveNext(IEnumerator enumerator, ref bool finished){
if(!enumerator.MoveNext()){
enumerator.Reset();
enumerator.MoveNext();
finished = true;
}
}

Try this, my first guess
for(int countA = 0, countB = 0; countA < A.Length; countA ++, countB++)
{
if(countB >= B.Length)
{
countB = 0;
}
newList.Add(A[countA]);
newList.Add(B[countB]);
}

I want to take a party too! Since I like LINQ very much, let's consider the following LINQ solution (sure it is not that efficient, but for fun):
public static void Main()
{
var a = new[] {1, 2, 3};
var b = new[] {'a', 'b'};
var joined = a.Join(
b,
x => Array.IndexOf(a, x) % b.Length,
y => Array.IndexOf(b, y) % a.Length,
(x, y) => new object[]{ x, y }
);
var flat = joined.SelectMany(x => x);
Console.Write(string.Join(", ", flat));
}

Another approach
public static IEnumerable<T> Mesh<T>(this IEnumerable<T> source, params IEnumerable<T>[] others)
{
var enumerators = new[] { source.GetEnumerator() }.Concat(others.Select(o => o.GetEnumerator())).ToList();
var finishes = new bool[enumerators.Count];
var allFinished = false;
while (!allFinished)
{
allFinsihed = true;
for (var i = 0; i < enumerators.Count; i++)
{
IEnumerator<T> enumerator = enumerators[i];
if (!enumerator.MoveNext())
{
finishes[i] = true;
enumerator.Reset();
enumerator.MoveNext(); // Not sure, if we need it here, Reset says: BEFORE the first element
}
yield return enumerator.Current;
if (!finishes[i])
{
allFinished = false;
}
}
}
}

You could write you own extension to allow greater flexibility.
Note: that this answer doesn't allocate a lot of unnecessary memory and enumerates all sequences exactly once.
I've included parameter checking.
public static IEnumerable<T> Mesh<T>(
this IEnumerable<T> source,
params IEnumerable<T>[] others)
{
if (others.LongLength == 0L)
{
foreach (var t in source)
{
yield return t;
}
yield break;
}
var nullCheck = Array.FindIndex(others, e => e == null);
if (nullCheck >= 0)
{
throw new ArgumentNullException(string.Format(
"Parameter {0} is null, this is not supported.",
++nullCheck),
(Exception)null);
}
var enumerators = new[] { source.GetEnumerator() }
.Concat(others.Select(o => o.GetEnumerator())).ToList();
try
{
var finishes = new bool[enumerators.Count];
var allFinished = false;
while (!allFinished)
{
allFinsihed = true;
for (var i = 0; i < enumerators.Count; i++)
{
if (finishes[i])
{
continue;
}
if (enumerators[i].MoveNext())
{
yield return enumerators[i].Current;
allFinished = false;
continue;
}
finishes[i] = true;
}
}
}
finally
{
foreach (var enumerator in enumerators)
{
enumerator.Dispose();
}
}
}
This would allow you to "Mesh" any number of jagged sequences as long has the items have the same type. e.g.
var a = new[] { 'a', 'b', 'c' };
var b = new[] { '1', '2', '3' };
var c = new[] { 'x', 'y' };
var d = new[] { '7', '8', '9', '0' };
var meshed = a.Mesh(b, c, d);
would yield
{ 'a', '1', 'x', '7', 'b', '2', 'y', '8', 'c', '3', '9', '0' }
If you wanted to mix types you could do
var a = new[] { 'a', 'b', 'c' };
var b = new[] { 1, 2, 3 };
var meshed = a.Cast<object>().Mesh(b.Cast<object>);
yielding something like,
{ ('a'), (1), ('b'), (2), ('c'), (3) }

Related

Order alphanumeric string numerically, and then by prefix/suffix

I have a complicated sorting pattern to replicate, and my solution seems a bit hamfisted.
My input is a list of numbers that can have several letters as a suffix and prefix both are only letter ('aaa', 'aab', 'ac' etc).
I need to sort numerically, then sort by suffix (if there is one) and then by prefix (if there is one).
E.g.
"a1a",
"5ac",
"1",
"12",
"2",
"11",
"5aa",
"3",
"5ab",
"a2b",
"abb11ca",
"1b",
"aba11ca"
would be sorted as
1
a1a
1b
2
a2b
3
5aa
5ab
5ac
11
aba11ca
abb11ca
12
Here is the solution that I came up with using Linq.
static void Main(string[] args)
{
var arr = new []
{"b2","a1a","5ac","1","12","2","11","5aa","3","5ab","a1","a2b","abb11ca","1b","aba11ca"
};
var ordered = arr.Select(str => {
var parts = SplitIntoPrefixNumberSuffix(str);
var number = int.Parse(parts[1]);
return new { str, parts, number };
})
.OrderBy(x => x.number).ThenBy(x => x.parts[2]).ThenBy(x => x.parts[0])
.Select(x => x.str);
Console.WriteLine("sorted array: ");
foreach (var s in ordered)
{
Console.WriteLine("{0}", s);
}
Console.ReadLine();
}
public static string[] SplitIntoPrefixNumberSuffix(string str)
{
var numChar = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
var numLoc = str.IndexOfAny(numChar);
var nums = "";
foreach (var c in str)
{
if (char.IsDigit(c))
nums = nums + c;
}
Console.WriteLine("numLoc: {0}; nums: {1}", numLoc, nums.Count());
var prefix = str.Substring(0, numLoc);
var suffix = str.Substring(numLoc + nums.Count());
Console.WriteLine("prefix {0}; nums {1}; suffix {2}", prefix, nums, suffix);
return new[] { prefix, nums, suffix };
}
Here is a .netfiddle of it working: https://dotnetfiddle.net/C7ZA0b.
While it works, it feels like it's not a very good solution. I'm iterating over the collection several times, and I think I should be using a custom comparable.
I've never written a Comparable before; I looked at Dot Net Pearls Alphanumeric Sorting and can follow it but not well enough to modify it to suit my needs.
Is there an IComparable I can use to do the above job? Any suggestions on a good place to learn how to write one?
So you can use regex named groups to split out the various components of the string, then order by each component:
var regex = new Regex(#"^(?<pre>\D*)(?<num>\d+)(?<suff>\D*)$");
var ordered = data.Select(d => (match: regex.Match(d), value: d))
.Where(x => x.match.Success) //throw away anything that doesn't conform
.Select(x => (
x.value,
pre: x.match.Groups["pre"].Value,
num: int.Parse(x.match.Groups["num"].Value),
suff: x.match.Groups["suff"].Value))
.OrderBy(x => x.num)
.ThenBy(x => x.suff)
.ThenBy(x => x.pre)
.Select(x => x.value);
...but ultimately this isn't that different to your solution. I can't really see how a specialized IComparer will simplify this.
If you don't have tuples available ( < C#7.0), swap for anonymous classes:
data.Select(d => new { match = regex.Match(d), value = d})
.Where(x => x.match.Success)
.Select(x => new {
x.value,
pre = x.match.Groups["pre"].Value,
num = int.Parse(x.match.Groups["num"].Value),
suff = x.match.Groups["suff"].Value})
.OrderBy(x => x.num)
.ThenBy(x => x.suff)
.ThenBy(x => x.pre)
.Select(x => x.value)
As option you can implement your own Custom Comparer
public class CustomStringComparer : IComparer<string>
{
public int Compare(string first, string second)
{
var compareByCore = CompareCore(first, second);
var compareBySuffix = CompareSuffix(first, second);
var compareByPrefix = ComparePrefix(first, second);
return compareByCore != 0 ? compareByCore
: compareBySuffix != 0 ? compareBySuffix
: compareByPrefix;
}
private int CompareCore(string a, string b)
{
var firstCoreNumber = Regex.Match(a, #"\d+").Value;
var secondCoreNumber = Regex.Match(b, #"\d+").Value;
if (!string.IsNullOrEmpty(firstCoreNumber) && !string.IsNullOrEmpty(secondCoreNumber))
{
return int.Parse(firstCoreNumber).CompareTo(int.Parse(secondCoreNumber));
}
return 0;
}
private int CompareSuffix(string a, string b)
{
var firstSuffix = Regex.Match(a, #"\D+$").Value;
var secondSuffix = Regex.Match(b, #"\D+$").Value;
return firstSuffix.CompareTo(secondSuffix);
}
private int ComparePrefix(string a, string b)
{
var firstPrefix = Regex.Match(a, #"^\D+").Value;
var secondPrefix = Regex.Match(b, #"^\D+").Value;
return firstPrefix.CompareTo(secondPrefix);
}
}
And when you call order method just send an instance of this comparer:
var arr = new[]
{ "a1a", "5ac", "1", "12", "2", "11", "5aa", "3", "5ab", "a2b", "abb11ca", "1b", "aba11ca" };
var sortedArr = arr.OrderBy(x => x, new CustomStringComparer());
foreach (var s in sortedArr)
{
Console.Write($"{s} ");
}
As an alternative to my existing answer, I wrote a ComparerBuilder<T> helper a while ago that makes some nice client code.
Now you can:
var comparer = new ComparerBuilder<string>()
.SortKey(k => int.Parse(Regex.Match(k, #"\d+").Value))
.ThenKey(k => Regex.Match(k, #"\D*$").Value)
.ThenKey(k => Regex.Match(k, #"^\D*").Value)
.Build();
var ordered = data.OrderBy(x => x, comparer);
Here is the IComparer implementation of alphanumeric by Dave Koelle.
Edit: Adding code sample.
void Main()
{
var arr = new[] {"b2","a1a","5ac","1","12","2","11","5aa","3","5ab","a1","a2b","abb11ca","1b","aba11ca" };
var items = arr.OrderBy(x => x.ToString(), new AlphanumComparator()).ToList();
Console.WriteLine("sorted array: ");
foreach (var s in items)
{
Console.WriteLine("{0}", s);
}
}
public class AlphanumComparator : IComparer<object>
{
private enum ChunkType { Alphanumeric, Numeric };
private bool InChunk(char ch, char otherCh)
{
ChunkType type = ChunkType.Alphanumeric;
if (char.IsDigit(otherCh))
{
type = ChunkType.Numeric;
}
if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
|| (type == ChunkType.Numeric && !char.IsDigit(ch)))
{
return false;
}
return true;
}
public int Compare(object x, object y)
{
String s1 = x as string;
String s2 = y as string;
if (s1 == null || s2 == null)
{
return 0;
}
int thisMarker = 0, thisNumericChunk = 0;
int thatMarker = 0, thatNumericChunk = 0;
while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
{
if (thisMarker >= s1.Length)
{
return -1;
}
else if (thatMarker >= s2.Length)
{
return 1;
}
char thisCh = s1[thisMarker];
char thatCh = s2[thatMarker];
StringBuilder thisChunk = new StringBuilder();
StringBuilder thatChunk = new StringBuilder();
while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || InChunk(thisCh, thisChunk[0])))
{
thisChunk.Append(thisCh);
thisMarker++;
if (thisMarker < s1.Length)
{
thisCh = s1[thisMarker];
}
}
while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || InChunk(thatCh, thatChunk[0])))
{
thatChunk.Append(thatCh);
thatMarker++;
if (thatMarker < s2.Length)
{
thatCh = s2[thatMarker];
}
}
int result = 0;
// If both chunks contain numeric characters, sort them numerically
if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
{
thisNumericChunk = Convert.ToInt32(thisChunk.ToString());
thatNumericChunk = Convert.ToInt32(thatChunk.ToString());
if (thisNumericChunk < thatNumericChunk)
{
result = -1;
}
if (thisNumericChunk > thatNumericChunk)
{
result = 1;
}
}
else
{
result = thisChunk.ToString().CompareTo(thatChunk.ToString());
}
if (result != 0)
{
return result;
}
}
return 0;
}
}

Mapping a list of ints into a list of MinMax ranges with LINQ [duplicate]

When i have a list
IList<int> list = new List<int>();
list.Add(100);
list.Add(200);
list.Add(300);
list.Add(400);
list.Add(500);
What is the way to extract a pairs
Example : List elements {100,200,300,400,500}
Expected Pair : { {100,200} ,{200,300} ,{300,400} ,{400,500} }
The most elegant way with LINQ: list.Zip(list.Skip(1), Tuple.Create)
A real-life example: This extension method takes a collection of points (Vector2) and produces a collection of lines (PathSegment) needed to 'join the dots'.
static IEnumerable<PathSegment> JoinTheDots(this IEnumerable<Vector2> dots)
{
var segments = dots.Zip(dots.Skip(1), (a,b) => new PathSegment(a, b));
return segments;
}
This will give you an array of anonymous "pair" objects with A and B properties corresponding to the pair elements.
var pairs = list.Where( (e,i) => i < list.Count - 1 )
.Select( (e,i) => new { A = e, B = list[i+1] } );
You can use a for loop:
var pairs = new List<int[]>();
for(int i = 0; i < list.Length - 1; i++)
pairs.Add(new [] {list[i], list[i + 1]);
You can also use LINQ, but it's uglier:
var pairs = list.Take(list.Count - 1).Select((n, i) => new [] { n, list[i + 1] });
EDIT: You can even do it on a raw IEnumerable, but it's much uglier:
var count = list.Count();
var pairs = list
.SelectMany((n, i) => new [] { new { Index = i - 1, Value = n }, new { Index = i, Value = n } })
.Where(ivp => ivp.Index >= 0 && ivp.Index < count - 1) //We only want one copy of the first and last value
.GroupBy(ivp => ivp.Index, (i, ivps) => ivps.Select(ivp => ivp.Value));
More general would be:
public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> values, int count, Func<TSource[], TResult> pairCreator)
{
if (count < 1) throw new ArgumentOutOfRangeException("count");
if (values == null) throw new ArgumentNullException("values");
if (pairCreator == null) throw new ArgumentNullException("pairCreator");
int c = 0;
var data = new TSource[count];
foreach (var item in values)
{
if (c < count)
data[c++] = item;
if (c == count)
{
yield return pairCreator(data);
c = 0;
}
}
}
Following solution uses zip method. Zip originalList and originalList.Skip(1) so that one gets desired result.
var adjacents =
originalList.Zip(originalList.Skip(1),
(a,b) => new {N1 = a, N2 = b});
Using .Windowed() from MoreLINQ:
var source = new[] {100,200,300,400,500};
var result = source.Windowed(2).Select(x => Tuple.Create(x.First(),x.Last()));
Off the top of my head and completely untested:
public static T Pairwise<T>(this IEnumerable<T> list)
{
T last;
bool firstTime = true;
foreach(var item in list)
{
if(!firstTime)
return(Tuple.New(last, item));
else
firstTime = false;
last = item;
}
}

Cleaning List<double[]> within a given tolerance

I have a List where each double[] has a length of 3. I would like to clean this list by leaving only those double[] having unique elements within a given tolerance (round up). For instance, a list like the one below:
1059.17 0 446.542225842081
1059.17 0 446.542564789741
1059.17 0 446.541759880305
959.167 0 579.827860527898
959.167 0 579.827847296075
Should become this for a given tolerance=two:
1059.17 0 446.54,
959.17 0 579.83,
Is there a smart way to do this in a neat way?
This should work. It uses the build-in equality comparisons of anonymous types.
List<double[]> data = ...
int tolerance = 2;
var roundedData = data
.Select(x => new {
v1 = Math.Round(x[0], tolerance),
v2 = Math.Round(x[1], tolerance),
v3 = Math.Round(x[2], tolerance)
})
.Distinct()
.Select(x => new [] { x.v1, x.v2, x.v3 })
.ToList();
Providing that array elements are always in the same order you can create your own comparer that should know how to compare double arrays :
public class MyDoubleArrComparer : IEqualityComparer<double[]>
{
public bool Equals(double[] x, double[] y)
{
for (int i = 0; i < x.Length; i++)
{
if (x[i] != y[i]) return false;
}
return true;
}
public int GetHashCode(double[] obj)
{
return base.GetHashCode();
}
}
And you can create a helper method that will round numbers and remove duplicates :
public static class Helper
{
public static List<double[]> MyFilter(this List<double[]> list, int tolerance)
{
var result = list
.Select(arr =>
{
// rounds numbers with precision that is set in tolerance variable
arr = arr.Select(d => d = Math.Round(d, tolerance)).ToArray();
return arr;
}).Distinct(new MyDoubleArrComparer()) // here we use our custom comparer
.ToList();
return result;
}
}
Now we can start using our helper method :
var nums = new List<double[]>()
{
new[] {1059.17, 0, 446.542225842081},
new[] {1059.17, 0, 446.542564789741},
new[] {1059.17, 0, 446.541759880305},
new[] {959.167, 0, 579.827860527898},
new[] {959.167, 0, 579.827847296075},
};
var result = nums.MyFilter(2);
foreach (var arr in result)
{
foreach (var d in arr)
{
Console.Write(d + " ");
}
Console.WriteLine();
}
Output :
1059.17 0 446.54
959.17 0 579.83
Maybe this will work?
public List<double[]> CleanWithTolerance(List<double[]> doubleNumbersList, int tolerance)
{
var newDoublesNumbersList = new List<double[]>();
foreach(double[] doubleNumbers in doubleNumbersList)
{
var newDoublesNumbers = doubleNumbers.Select(doubleNumber => Math.Round(doubleNumber, tolerance)).ToArray();
if(newDoublesNumbersList.All(cleanDoubleNumbers => !Enumerable.SequenceEqual(cleanDoubleNumbers, newDoublesNumbers))
{
newDoublesNumbersList.Add(newDoublesNumbers);
}
}
return newDoublesNumbersList;
}

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) {_}));
}

Combine similar character in string in C#

I have a list of lists that contain integers (this list can be any length and can contain any number of integers:
{{1,2}, {3,4}, {2,4}, {9,10}, {9,12,13,14}}
What I want to do next is combine the lists where any integer matches any integer from any other list, in this case:
result = {{1,2,3,4}, {9,10,12,13,14}}
I have tried many different approaches but am stuck for an elegant solution.
If you just mean "combine when there's an intersection", then maybe something like below, with output:
{1,2,3,4}
{9,10,12}
noting that it also passes the test in your edit, with output:
{1,2,3,4}
{9,10,12,13,14}
Code:
static class Program {
static void Main()
{
var sets = new SetCombiner<int> {
{1,2},{3,4},{2,4},{9,10},{9,12}
};
sets.Combine();
foreach (var set in sets)
{
// edited for unity: original implementation
// Console.WriteLine("{" +
// string.Join(",", set.OrderBy(x => x)) + "}");
StringBuilder sb = new StringBuilder();
foreach(int i in set.OrderBy(x => x)) {
if(sb.Length != 0) sb.Append(',');
sb.Append(i);
}
Console.WriteLine("{" + sb + "}");
}
}
}
class SetCombiner<T> : List<HashSet<T>>
{
public void Add(params T[] values)
{
Add(new HashSet<T>(values));
}
public void Combine()
{
int priorCount;
do
{
priorCount = this.Count;
for (int i = Count - 1; i >= 0; i--)
{
if (i >= Count) continue; // watch we haven't removed
int formed = i;
for (int j = formed - 1; j >= 0; j--)
{
if (this[formed].Any(this[j].Contains))
{ // an intersection exists; merge and remove
this[j].UnionWith(this[formed]);
this.RemoveAt(formed);
formed = j;
}
}
}
} while (priorCount != this.Count); // making progress
}
}
Build custom comparer:
public class CusComparer : IComparer<int[]>
{
public int Compare(int[] x, int[] y)
{
x = x.OrderBy(i => i).ToArray();
y = y.OrderBy(i => i).ToArray();
for (int i = 0; i < Math.Min(x.Length, y.Length); i++ )
{
if (x[i] < y[i]) return -1;
if (x[i] > y[i]) return 1;
}
if (x.Length < y.Length) return -1;
if (x.Length > y.Length) return 1;
return 0;
}
}
Then, order by custom comparer first:
List<int[]> input = new List<int[]>()
{
new[] { 3, 4 }, new[] { 1, 2 }, new[] { 2, 4 },
new[] { 9, 10 }, new[] { 9, 12 }
};
var orderedInput = input.OrderBy(x => x, new CusComparer()).ToList();
Use Intersect.Any() to check:
List<int[]> output = new List<int[]>();
int[] temp = orderedInput[0];
foreach (var arr in orderedInput.Skip(1))
{
if (temp.Intersect(arr).Any())
temp = temp.Union(arr).ToArray();
else
{
output.Add(temp);
temp = arr;
}
}
output.Add(temp);
Here's a simple, flexible solution using LINQ's Aggregate:
void Main()
{
var ints = new []{new []{1,2},new []{3,4},new []{2,4},new []{9,10},new []{9,12}};
var grouped = ints.Aggregate(new List<HashSet<int>>(), Step);
foreach(var bucket in grouped)
Console.WriteLine(String.Join(",", bucket.OrderBy(b => b)));
}
static List<HashSet<T>> Step<T>(List<HashSet<T>> all, IEnumerable<T> current)
{
var bucket = new HashSet<T>();
foreach (var c in current)
bucket.Add(c);
foreach (var i in all.Where(b => b.Overlaps(bucket)).ToArray())
{
bucket.UnionWith(i);
all.Remove(i);
}
all.Add(bucket);
return all;
}
We maintain a list of resulting sets (1). For each source set, remove resulting sets that intersect it (2), and add a new resulting set (3) that is the union of the removed sets and the source set (4):
class Program {
static IEnumerable<IEnumerable<T>> CombineSets<T>(
IEnumerable<IEnumerable<T>> sets,
IEqualityComparer<T> eq
) {
var result_sets = new LinkedList<HashSet<T>>(); // 1
foreach (var set in sets) {
var result_set = new HashSet<T>(eq); // 3
foreach (var element in set) {
result_set.Add(element); // 4
var node = result_sets.First;
while (node != null) {
var next = node.Next;
if (node.Value.Contains(element)) { // 2
result_set.UnionWith(node.Value); // 4
result_sets.Remove(node); // 2
}
node = next;
}
}
result_sets.AddLast(result_set); // 3
}
return result_sets;
}
static IEnumerable<IEnumerable<T>> CombineSets<T>(
IEnumerable<IEnumerable<T>> src
) {
return CombineSets(src, EqualityComparer<T>.Default);
}
static void Main(string[] args) {
var sets = new[] {
new[] { 1, 2 },
new[] { 3, 4 },
new[] { 2, 4 },
new[] { 9, 10 },
new[] { 9, 12, 13, 14 }
};
foreach (var result in CombineSets(sets))
Console.WriteLine(
"{{{0}}}",
string.Join(",", result.OrderBy(x => x))
);
}
}
This prints:
{1,2,3,4}
{9,10,12,13,14}
Ok i LINQed this up! Hope this is what you wanted... crazy one ;)
void Main()
{
var matches = new List<List<ComparissonItem>> { /*Your Items*/ };
var overall =
from match in matches
let matchesOne =
(from searchItem in matches
where searchItem.Any(item => match.Any(val => val.Matches(item) && !val.Equals(item)))
select searchItem)
where matchesOne.Any()
select
matchesOne.Union(new List<List<ComparissonItem>> { match })
.SelectMany(item => item);
var result = overall.Select(item => item.ToHashSet());
}
static class Extensions
{
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> enumerable)
{
return new HashSet<T>(enumerable);
}
}
class ComparissonItem
{
public int Value { get; set; }
public bool Matches(ComparissonItem item)
{
/* Your matching logic*/
}
public override bool Equals(object obj)
{
var other = obj as ComparissonItem;
return other == null ? false : this.Value == other.Value;
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
}

Categories

Resources