Sort numbers then strings C# - c#

I want to this list
A
B
C
111
11
123
1
42
5
To be sorted
1
5
11
42
111
123
A
B
C
By default, it sorts numbers like strings (So, it goes 1,11,111,123,42,5),
But I want to sort numbers like numbers, and than strings that are not numbers.
Is there clean solution to sort it like above?
It is a list of objects, and object has several properties, one of which is a this string.

This will work for most use cases, but may have odd results if the string starts with control characters, string like "\tabc" will come before the integers:
list.OrderBy(x=>int.TryParse(x, out var dummy) ? dummy.ToString("D10") : x);
or for versions of C# prior to 7:
list.OrderBy(x=> { int dummy; return int.TryParse(x, out dummy) ? dummy.ToString("D10") : x;} );

What you want is called Natural sort.
I once wrote some code for that:
public static class NaturalCompare
{
public static int Compare(string first, string second, StringComparison comparison = StringComparison.Ordinal)
{
if (string.Compare(first, second, comparison) == 0)
{
return 0;
}
if (first == null)
{
return -1;
}
if (second == null)
{
return 1;
}
DateTime d1, d2;
if (DateTime.TryParse(first, out d1) && DateTime.TryParse(second, out d2))
{
return d1.CompareTo(d2);
}
var pos1 = 0;
var pos2 = 0;
int result;
do
{
bool isNum1, isNum2;
var part1 = GetNext(first, ref pos1, out isNum1);
var part2 = GetNext(second, ref pos2, out isNum2);
if (isNum1 && isNum2)
{
result = long.Parse(part1).CompareTo(long.Parse(part2));
}
else
{
result = String.Compare(part1, part2, comparison);
}
} while (result == 0 && pos1 < first.Length && pos2 < second.Length);
return result;
}
public static int CompareToNatural(this string first, string second, StringComparison comparison = StringComparison.Ordinal)
{
return Compare(first, second, comparison);
}
public static IOrderedEnumerable<TSource> OrderByNatural<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
{
return source.OrderBy(keySelector, new NatComparer());
}
public static IOrderedEnumerable<TSource> OrderByNaturalDescending<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
{
return source.OrderByDescending(keySelector, new NatComparer());
}
private sealed class NatComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return NaturalCompare.Compare(x, y);
}
}
private static string GetNext(string s, ref int index, out bool isNumber)
{
if (index >= s.Length)
{
isNumber = false;
return "";
}
isNumber = char.IsDigit(s[index]);
var start = index;
while (index < s.Length && char.IsDigit(s[index]) == isNumber)
{
index++;
}
return s.Substring(start, index - start);
}
}

I wrote this IComparer implementation a few months back to handle something like this. I think it will do what you want by default, though it is built to handle more complex cases where number/letter groups are separated by delimiters that also need to be sorted atomically. You should be able to adjust it to your needs.
public class SemanticComparer : IComparer<string>
{
private static Regex _splitter = new Regex("\\W+");
public int Compare(string x, string y)
{
string[] partsX = _splitter.Split(x);
string[] partsY = _splitter.Split(y);
int shortest = Math.Min(partsX.Length, partsY.Length);
for (int index = 0; index < shortest; index++)
{
int intX, intY;
int result;
if (int.TryParse(partsX[index], out intX) && int.TryParse(partsY[index], out intY))
{
result = intX.CompareTo(intY);
}
else
{
result = string.Compare(partsX[index], partsY[index], StringComparison.Ordinal);
}
if (result != 0)
{
return result;
}
}
return 0;
}
}
You can sort your list with it like this:
MyList.Sort(new SemanticComparer());

You could loop through all the values once, and use int.TryParse to separate them into two separate lists: one for the values where int.TryParse returned true (aka the numbers), and another list for the ones where it returned false (the non-numbers). Then you could sort these two lists separately, and concatenate their sorted results together at the end.

I haven't tested this code for performance, but you can solve this with a Comparer
public class ArrayItemComparer : IComparer<string>
{
public int Compare(string x, string y)
{
int xInt = 0, yInt = 0;
bool parseX = int.TryParse(x, out xInt);
bool parseY = int.TryParse(y, out yInt);
if (parseX && parseY)
{
return xInt.CompareTo(yInt);
}
else if (parseX)
{
return -1;
}
else if (parseY)
{
return 1;
}
else
{
return x.CompareTo(y);
}
}
}

I've created a solution for this. I've divided the list into two part then sort and concat. Please check below:
public List<ListItem> getSortedList()
{
int dummy = 0;
List<ListItem> list = new List<ListItem>();
list.Add(new ListItem() { Item = "A" });
list.Add(new ListItem() { Item = "B" });
list.Add(new ListItem() { Item = "C" });
list.Add(new ListItem() { Item = "111" });
list.Add(new ListItem() { Item = "11" });
list.Add(new ListItem() { Item = "123" });
list.Add(new ListItem() { Item = "1" });
list.Add(new ListItem() { Item = "42" });
list.Add(new ListItem() { Item = "5" });
var listNumber = list.Where(m => int.TryParse(m.Item, out dummy)).ToList().OrderBy(m => Convert.ToInt16(m.Item)).ToList();
var listString = list.Where(m => !int.TryParse(m.Item, out dummy)).ToList().OrderBy(m => m.Item).ToList();
var sortedList = listNumber.Concat(listString).ToList();
return sortedList;
}
You can run this in DotNetFiddle.

Assuming you start with a collection of strings, a simple comparer should do the job:
public class Comparer : IComparer<string>
{
public int Compare(string a, string b)
{
int ia = 0;
int ib = 0;
var aIsInt = int.TryParse(a,out ia);
var bIsInt = int.TryParse(b,out ib);
if (aIsInt == bIsInt)
{
if (aIsInt)
{
return ia.CompareTo(ib);
}
else
{
return a.CompareTo(b);
}
}
return aIsInt ? -1 : 1;
}
}
Here's a fiddle

With Regex.Replace in the "OrderBy" it's one (fairly) simple line of code. And note that the number "3" just has to be a number equal-to or larger than your longest string, so for anyone else increase as needed.
using System.Text.RegularExpressions;
string[] yourStrings = new string[] { "A", "B", "C", "111", "11", "123", "1", "42", "5" };
foreach (var item in yourStrings.OrderBy(x => Regex.Replace(x, #"\d+", i =>
i.Value.PadLeft(3, '0'))))
{
Response.Write(item + "\n");
}

Related

C# - Finding key in an array by the value of its complex structure

is there a method in C# to find the key of the item in an array by its "subvalue"? Some hypothetical function "findKeyofCorrespondingItem()"?
struct Items
{
public string itemId;
public string itemName;
}
int len = 18;
Items[] items = new Items[len];
items[0].itemId = "684656";
items[1].itemId = "411666";
items[2].itemId = "125487";
items[3].itemId = "756562";
// ...
items[17].itemId = "256569";
int key = findKeyofCorrespondingItem(items,itemId,"125487"); // returns 2
You can use Array.FindIndex. See https://msdn.microsoft.com/en-us/library/03y7c6xy(v=vs.110).aspx
using System.Linq
...
Array.FindIndex(items, (e) => e.itemId == "125487"));
public static int findKeyofCorrespondingItem(Items[] items, string searchValue)
{
for (int i = 0; i < items.Length; i++)
{
if (items[i].itemId == searchValue)
{
return i;
}
}
return -1;
}
You can run a loop and check if itemId equal to the value you are searching for. Return -1 if no item matches with value.
Solution with Linq:
public static int findKeyofCorrespondingItem(Items[] items, string searchValue)
{
return Array.FindIndex(items, (e) => e.itemId == searchValue);
}

C# Calculate Combinations from Multiple Lists of Custom Objects with No Repetition

So I have been struggling to find a way to create all combinations with no repetition from multiple lists containing custom objects. There are some additional constraints that make it a bit more challenging, of course.
Basically I am parsing a bunch of data from a .csv file that contains part information. This data is then passed on to a custom object and then those objects are added to lists based on their "group." (See code below)
So once the information has been parsed I now have 6 lists containing any number of elements. Now I need to generate all combinations between those 6 lists following these rules:
One object from groupA
Two objects from groupB (no repetition)
Three objects from groupC (no repetition)
One object from groupD
One object from groupE
One object from groupF
These objects are then used to create a ModuleFull object, and my overall end result should be a List<ModuleFull> containing all the combinations generated from the part lists.
I was able to figure out a way to do this using LINQ although I did not test it using lists of custom objects because I realized my lists all contain different numbers of elements.
So any help that I could get in coming up with a method to solve this using recursion would be greatly appreciated.
Here is the code parsing the data:
using (TextFieldParser parser = new TextFieldParser(#"c:\temp\test.csv"))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
Part tempPart = new Part(fields[0], fields[2], fields[1], double.parse(fields[4]), long.parse(fields[3]));
allParts.Add(tempPart);
if (tempPart.group == "A")
{
aParts.Add(tempPart);
}
else if (tempPart.group == "B")
{
bParts.Add(tempPart);
}
else if (tempPart.group == "C")
{
cParts.Add(tempPart);
}
else if (tempPart.group == "D")
{
dParts.Add(tempPart);
}
else if (tempPart.group == "E")
{
eParts.Add(tempPart);
}
else if (tempPart.group == "F")
{
fParts.Add(tempPart);
}
}
Below are the two classes for the objects that fill the lists:
public class Part
{
public string idNum; //0 locations when being parsed
public string name; //2
public string group; //1
public double tolerance; //4
public long cost; //3
public Part(string id, string nm, string grp, double tol, long cst)
{
idNum = id;
name = nm;
group = grp;
tolerance = tol;
cost = cst;
}
}
public class ModuleFull
{
public Part groupA;
public Part groupBOne;
public Part groupBTwo;
public Part groupCOne;
public Part groupCTwo;
public Part groupCThree;
public Part groupD;
public Part groupE;
public Part groupF;
public ModuleFull(Part a, Part b1, Part b2, Part c1, Part c2, Part c3, Part d, Part e, Part f)
{
groupA = a;
groupBOne = b1;
groupBTwo = b2;
groupCOne = c1;
groupCTwo = c2;
groupCThree = c3;
groupD = d;
groupE = e;
groupF = f;
}
}
The code below uses a custom enumerator to get unique combinations. Very clean solution.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace IEnumerable_IEnumerator_Recursive
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Parser parser = new Parser(FILENAME);
int level = 0;
List<Part> root = new List<Part>();
Part.Recurse(level, root);
}
}
public class Parser
{
public Boolean EndOfData = false;
public Parser(string filename)
{
StreamReader reader = new StreamReader(filename);
string inputLine = "";
while ((inputLine = reader.ReadLine()) != null)
{
inputLine = inputLine.Trim();
if (inputLine.Length > 0)
{
string[] fields = inputLine.Split(new char[] { ',' });
Part tempPart = new Part(fields[0], fields[1], fields[2], fields[3], fields[4]);
Part.allParts.Add(tempPart);
}
}
Part.MakeDictionary();
}
}
public class PartEnumerator : IEnumerator<List<Part>>
{
List<Part> parts = null;
public static SortedDictionary<string, int> maxCount = new SortedDictionary<string, int>() {
{"A", 1},
{"B", 2},
{"C", 3},
{"D", 1},
{"E", 1},
{"F", 1}
};
public int size = 0;
List<int> enumerators = null;
public PartEnumerator(string name, List<Part> parts)
{
this.parts = parts;
size = maxCount[name];
enumerators = new List<int>(new int[size]);
Reset();
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public List<Part> Current
{
get
{
try
{
List<Part> returnParts = new List<Part>();
foreach (int enumerator in enumerators)
{
returnParts.Add(parts[enumerator]);
}
return returnParts;
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Reset()
{
for (int count = 0; count < enumerators.Count; count++)
{
enumerators[count] = count;
}
}
public Boolean MoveNext()
{
Boolean moved = true;
int listSize = parts.Count;
int enumNumbers = enumerators.Count;
//only use enumerators up to the size of list
if (listSize < enumNumbers)
{
enumNumbers = listSize;
}
Boolean ripple = true;
int enumCounter = enumNumbers;
if (enumCounter > 0)
{
while ((ripple == true) && (--enumCounter >= 0))
{
ripple = false;
int maxCount = listSize - (enumNumbers - enumCounter);
if (enumerators[enumCounter] >= maxCount)
{
ripple = true;
}
else
{
for (int i = enumCounter; i < enumNumbers; i++)
{
if (i == enumCounter)
{
enumerators[i] += 1;
}
else
{
enumerators[i] = enumerators[i - 1] + 1;
}
}
}
}
if ((enumCounter <= 0) && (ripple == true))
{
moved = false;
}
}
return moved;
}
public void Dispose()
{
}
}
public class Part
{
public static List<Part> allParts = new List<Part>();
public static Dictionary<string, PartEnumerator> partDict = new Dictionary<string, PartEnumerator>();
public string idNum; //0 locations when being parsed
public string name; //2
public string group; //1
public double tolerance; //4
public long cost; //3
public Part()
{
}
public Part(string id, string nm, string grp, string tol, string cst)
{
idNum = id;
name = nm;
group = grp;
tolerance = double.Parse(tol);
cost = long.Parse(cst);
}
public static void MakeDictionary()
{
var listPartEnum = Part.allParts.GroupBy(x => x.name)
.Select(x => new { Key = x.Key, List = new PartEnumerator(x.Key, x.ToList()) });
foreach (var partEnum in listPartEnum)
{
partDict.Add(partEnum.Key, partEnum.List);
}
}
public static string[] NAMES = { "A", "B", "C", "D", "E", "F" };
public static void Recurse(int level, List<Part> results)
{
Boolean moved = true;
if (level < PartEnumerator.maxCount.Keys.Count)
{
//level is equivalent to names in the Part Enumerator dictionary A to F
string name = NAMES[level];
PartEnumerator enumerator = partDict[name];
enumerator.Reset();
while ((enumerator != null) && moved)
{
List<Part> allParts = new List<Part>(results);
allParts.AddRange((List<Part>)enumerator.Current);
int currentLevel = level + 1;
Recurse(currentLevel, allParts);
moved = enumerator.MoveNext();
}
}
else
{
string message = string.Join(",", results.Select(x => string.Format("[id:{0},name:{1}]", x.name, x.idNum)).ToArray());
Console.WriteLine(message);
}
}
}
}
I used following input file
1,A,X,0,0
2,A,X,0,0
3,A,X,0,0
4,A,X,0,0
5,A,X,0,0
1,B,X,0,0
2,B,X,0,0
3,B,X,0,0
4,B,X,0,0
5,B,X,0,0
1,C,X,0,0
2,C,X,0,0
3,C,X,0,0
4,C,X,0,0
5,C,X,0,0
1,D,X,0,0
2,D,X,0,0
3,D,X,0,0
4,D,X,0,0
5,D,X,0,0
1,E,X,0,0
2,E,X,0,0
3,E,X,0,0
4,E,X,0,0
5,E,X,0,0
1,F,X,0,0
2,F,X,0,0
3,F,X,0,0
4,F,X,0,0
5,F,X,0,0
This can be solved with two related methods. One is a method to generate all combinations of items from a list. This deals with your cases where you want more than one from a set, like group B and C. The other is a method to get you all the ways to combine one element from each list, which is otherwise known as a Cartesian product and is, in a way, a special case of the first method.
I recently wrote a library of combinatorial functions that includes both of these, so I can share my implementation with you. My library is on Github if you want to look at the source code, and can be installed from NuGet if you'd like. (The examples below are slightly simplified to fit your situation; in my fuller versions, the combinations method has different modes that allow you to specify whether it matters what order the output items are in, and whether it's allowed to use source items more than once. Neither are needed here, so they've been omitted.)
So, the first of those methods looks something like this:
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> source, int combinationSize)
{
if (combinationSize > source.Count())
{
return new List<IEnumerable<T>>();
}
if (source.Count() == 1)
{
return new[] { source };
}
var indexedSource = source
.Select((x, i) => new
{
Item = x,
Index = i
})
.ToList();
return indexedSource
.SelectMany(x => indexedSource
.OrderBy(y => x.Index != y.Index)
.Skip(1)
.OrderBy(y => y.Index)
.Skip(x.Index)
.Combinations(combinationSize - 1)
.Select(y => new[] { x }.Concat(y).Select(z => z.Item))
);
}
The second method is from a blog post by Eric Lippert (which was actually inspired by another StackOverflow question), and looks like this:
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
if (sequences == null)
{
throw new ArgumentNullException(nameof(sequences));
}
IEnumerable<IEnumerable<T>> emptyProduct = new IEnumerable<T>[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item }))
.Where(x => x.Any());
}
The two methods can be combined like this:
var groupA = new[] { "a", "aa", "aaa", "aaaa", "aaaaa" };
var groupB = new[] { "b", "bb", "bbb", "bbbb", "bbbbb" };
var groupC = new[] { "c", "cc", "ccc", "cccc", "ccccc" };
var groupD = new[] { "d", "dd", "ddd", "dddd", "ddddd" };
var groupE = new[] { "e", "ee", "eee", "eeee", "eeeee" };
var groupF = new[] { "f", "ff", "fff", "ffff", "fffff" };
var options = new[]
{
groupA.Combinations(1), // One object from groupA
groupB.Combinations(2), // Two objects from groupB (no repetition)
groupC.Combinations(3), // Three objects from groupC (no repetition)
groupD.Combinations(1), // One object from groupD
groupE.Combinations(1), // One object from groupE
groupF.Combinations(1) // One object from groupF
};
return options.CartesianProduct();
So, we generate the various ways of satisfying each of your sub-conditions first: one from this group, two from that group, etc. Then, we look at all ways of putting those together to form a group of subgroups. The result is an IEnumerable<IEnumerable<T>> where T is the type of what you started with - in this case, string, but for you it could be something else. You can then iterate over this and use each set to build your result type.
Be aware that, like many combinatorial problems, this can scale quite fast. For example, with my test data this returns 62,500 possible combinations.

Sorting an Arraylist of strings on a portion of each string, delimited by a character

I'm trying to sort an ArrayList of strings (not in int).
Given:
f.e.
[0] 23,24
[1] 12,33
[2] 37,11
Arraylist.Sort should give back (sorted by the last number ascending):
[0] 37,11
[1] 23,24
[2] 12,33
Having this so far:
public class Point
{
public int i, j;
public Point(int i, int j)
{this.i = i; this.j = j;}
public string ToString()
{return i + "," + j;}
}
public class SList
{
public static void Main()
{
ArrayList Slist = new ArrayList();
Random Generate = new Random();
for (int i = 0; i < 3; i++)
{
Point p = new Point(Generate.Next(40), Generate.Next(40));
Slist.Add(p.ToString());
}
Slist.Sort();
}
}
I'm not 100% sure if you have an array of strings or if they are split already, but you can create a custom comparer and do something like this perhaps. Make clearer what you want so that people can help you better.
public class MyCustomComparer : IComparer<string>
{
public int Compare(string x, string y)
{
var xIntValues = x.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
var yIntValues = y.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();
for(var i = 0; i < Math.Min(xIntValues.Count(), yIntValues.Count()); ++i)
if (xIntValues[i] != yIntValues[i])
return xIntValues[i] - yIntValues[i];
return xIntValues.Count() - yIntValues.Count();
}
}
Then use it like this where your array elements contain strings like "37, 11"
Array.Sort(myArray, MyCustomComparer);
Sort of odd to be using an ArrayList after c# 1.0, but if you have one for legacy reasons, you can create your own non-generic custom comparer, like so:
public class CustomComparer : IComparer<string>, IComparer {
#region IComparer<string> Members
int? ExtractNumber(string str)
{
int index = str.LastIndexOf(',');
if (index < 0)
return null;
var subStr = str.Substring(index+1);
int result;
if (int.TryParse(subStr, out result))
return result;
return null;
}
// Return x - y
public int Compare(string x, string y)
{
#if false
Given: f.e.
[0] 23,24
[1] 12,33
[2] 37,11
Arraylist.Sort should give back (sorted by the last number ascending):
[0] 37,11
[1] 23,24
[2] 12,33
#endif
var xVal = ExtractNumber(x);
var yVal = ExtractNumber(y);
if (!xVal.HasValue && !yVal.HasValue)
return x.CompareTo(y);
else if (!xVal.HasValue)
return 1;
else if (!yVal.HasValue)
return -1;
int cmp = xVal.Value.CompareTo(yVal.Value);
if (cmp != 0)
return cmp;
return x.CompareTo(y);
}
#endregion
#region IComparer Members
public int Compare(object x, object y)
{
if (object.ReferenceEquals(x, y))
return 0;
string xStr = x as string;
string yStr = y as string;
if (xStr == null && yStr == null)
return 0; // NO IDEA
else if (xStr == null)
return 1;
else if (yStr == null)
return -1;
else return Compare(xStr, yStr);
}
#endregion
}
The following code implements IComparable on your class. It sorts on y in ascending order:
public class Point : IComparable<Point>
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public string ToString()
{
return x + "," + y;
}
public int CompareTo(Point other)
{
return this.y.CompareTo(other.y);
}
}
You can now use ArrayList.Sort() to achieve the order you need.
The easiest way to do this would be by stepping away from your non-generic list:
var sortedList = list.Cast<string>().OrderBy(x => x.Split(',')[1]);
The code is self-explanatory.
Using this as input:
var list = new ArrayList();
list.Add("23,24");
list.Add("12,33");
list.Add("37,11");
Yields this as output:
37,11
23,24
12,33

Combination Creator?

I would like to send to a method a list of numbers, and to get in return all the possible combinations of numbers I gen generate from this list by putting the digits next to each other.
For example, for the numbers {1, 2, 3} I would to give in return:
{1, 2, 3, 12, 13, 21, 23, 31, 32, 123, 132, 213, 231, 312, 321}
This code for example (which I haven't finished) only "Knows" to deal with lists that contain 3 numbers in them.
private static void M1()
{
var intList = new List<int>() { 1, 2, 3 };
var resultList = AllPossibleCombinations(intList);
}
private static object AllPossibleCombinations(List<int> List)
{
var result = new List<int>();
result.Add(List[0]);
result.Add(List[1]);
result.Add(List[2]);
result.Add(List[0] * 10 + List[1]);
result.Add(List[1] * 10 + List[2]);
result.Add(List[0] * 10 + List[2]);
result.Add(List[1] * 10 + List[0]);
result.Add(List[2] * 10 + List[1]);
result.Add(List[2] * 10 + List[0]);
return result;
}
How can I write something more generic? How can I get a list with different number of elements and give in return all the possible combinations?
This isn't necessarily the most efficient, but here's how you could do this using non-recursive methods returning IEnumerable<T>. Something like this will probably require the least possible memory, since it doesn't require building all the permutations in memory. Instead, it just allows you to iterate over the permutations one by one.
private static void Main()
{
var input1 = new[] {"1", "2", "3"};
foreach (var output in EnumeratePermutations(input1))
Debug.WriteLine(String.Join(",", output));
}
private static IEnumerable<T[]> EnumeratePermutations<T>(T[] input)
{
return from partCount in Enumerable.Range(1, input.Length)
let inputs = Enumerable.Repeat(input, partCount).ToArray()
from indices in EnumerateCrossjoinIndices(inputs)
where indices.Distinct().Count() == indices.Length
select indices.Select(n => input[n]).ToArray();
}
private static IEnumerable<int[]> EnumerateCrossjoinIndices(params Array[] arrays)
{
var arrayCount = arrays.Length;
if (arrayCount == 0)
yield break;
if (arrays.Any(a => a.Length == 0))
yield break;
var indexer = new int[arrayCount];
yield return (int[]) indexer.Clone();
for (var dimension = arrayCount - 1; dimension >= 0; --dimension)
{
++indexer[dimension];
if (indexer[dimension] == arrays[dimension].Length)
indexer[dimension] = 0;
else
{
yield return (int[]) indexer.Clone();
dimension = arrayCount;
}
}
}
EnumerateCrossjoinIndices takes n arrays of potentially different lengths and yields an enumeration of the indices that would be used in a crossjoin operation. For example, given {"1","2"} and {"A","B","C"}, it would yield 6 arrays {0,0}, {0,1}, {0,2}, {1,0}, {1,1}, {1,2}. Note that the arrays yielded by this method contain indices into the input arrays, not the elements at those indices.
EnumeratePermutations takes an array and yields the permutations of the items of that array. It does this by enumerating over the indices that would be used to crossjoin the array against itself x times (where x = 1 to n, where n is the number of items in the array). It then filters out any set of crossjoin indices where the set isn't a distinct set.
Try this sample code:
private static List<int> AllPossibleCombinations(IList<int> alphabet)
{
List<int[]> combinations = new List<int[]>();
MakeCombination(combinations, alphabet.Count, new int[0]); // Start recursion
combinations.RemoveAt(0); // Remove empty combination
return combinations.ConvertAll(c => c.Aggregate(0, (sum, index) => sum * 10 + alphabet[index]));
}
private static void MakeCombination(List<int[]> output, int length, int[] usedIndices)
{
output.Add(usedIndices);
for (int i = 0; i < length; i++)
if (Array.IndexOf(usedIndices, i) == -1) // If the index wasn't used earlier
{
// Add element to the array of indices by creating a new one:
int[] indices = new int[usedIndices.Length + 1];
usedIndices.CopyTo(indices, 0);
indices[usedIndices.Length] = i;
if (length + 1 != indices.Length)
MakeCombination(output, length, indices); // Recursion
}
}
It uses recursion to generate desired combinations.
Usage:
var t = AllPossibleCombinations(new[] { 1, 2, 3 });
Solution 1
The more generic and type independent way is to create tree-based algorithm which returns collection of combinations of input objects.
Code:
public static class IEnumerableExtensions
{
private class Node<T>
{
public Node()
{
Children = new List<Node<T>>();
}
public T Value
{
get;
set;
}
public bool IsRoot
{
get;
set;
}
public List<Node<T>> Children
{
get;
private set;
}
public Node<T> Parent
{
get;
set;
}
public List<Node<T>> Path
{
get
{
List<Node<T>> Result = new List<Node<T>>();
Result.Add(this);
if (this.Parent.IsRoot == false)
{
Result.AddRange(this.Parent.Path);
}
return Result;
}
}
}
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> enumerable)
{
List<Node<T>> AllNodes = new List<Node<T>>();
// Create tree.
Node<T> Root = new Node<T>() { IsRoot = true };
Queue<Node<T>> Queue = new Queue<Node<T>>();
Queue.Enqueue(Root);
int CurrentLevel = 0;
int LevelsToCreate = enumerable.Count();
while (Queue.Count > 0)
{
var CurrentLevelNodes = Queue.ToList();
Queue.Clear();
foreach (var LoopNode in CurrentLevelNodes)
{
if (LoopNode.Children.Count == 0)
{
foreach (var LoopValue in enumerable)
{
var NewNode = new Node<T>() { Value = LoopValue, Parent = LoopNode };
AllNodes.Add(NewNode);
LoopNode.Children.Add(NewNode);
Queue.Enqueue(NewNode);
}
}
}
CurrentLevel++;
if (CurrentLevel >= LevelsToCreate)
{
break;
}
}
// Return list of all paths (which are combinations).
List<List<T>> Result = new List<List<T>>();
foreach (var LoopNode in AllNodes)
{
if (!LoopNode.IsRoot)
{
List<T> Combination = LoopNode.Path.Select(Item => Item.Value).ToList();
Result.Add(Combination);
}
}
return Result;
}
}
Example with numbers:
class Program
{
static void Main(string[] args)
{
List<int> Input = new List<int>() { 1, 2, 3 };
var Combinations = Input.Combinations();
}
}
Example with strings:
static void Main(string[] args)
{
var Input = new List<string>() { "a", "b" };
var Combinations = Input.Combinations();
foreach (var LoopCombination in Combinations)
{
string Combination = string.Join(String.Empty, LoopCombination);
Console.WriteLine(Combination);
}
Console.ReadKey();
}
Solution 2
The second idea is to not to use tree-based algorithm and create combinations step-by-step - it may be faster.
Code:
public class Combinator<T>
{
private readonly Dictionary<int, T> _Pattern;
private readonly int _Min = 0;
private readonly int _Max;
private List<int> _CurrentCombination;
public Combinator(IEnumerable<T> pattern)
{
_Pattern = new Dictionary<int, T>();
for (int i = 0; i < pattern.Count(); i++)
{
_Pattern.Add(i, pattern.ElementAt(i));
}
_CurrentCombination = new List<int>();
_Max = pattern.Count() - 1;
}
public bool HasFinised
{
get;
private set;
}
public IEnumerable<T> Next()
{
// Initialize or increase.
if (_CurrentCombination.Count == 0)
{
_CurrentCombination.Add(_Min);
}
else
{
MyIncrease(_CurrentCombination.Count - 1);
}
if (_CurrentCombination.Count - 1 == _Max && _CurrentCombination.All(Key => Key == _Max))
{
HasFinised = true;
}
return _CurrentCombination.Select(Key => _Pattern[Key]).ToList();;
}
private void MyIncrease(int index)
{
if (index >= 0)
{
_CurrentCombination[index] = _CurrentCombination[index] + 1;
if (_CurrentCombination[index] > _Max)
{
_CurrentCombination[index] = _Min;
if (index - 1 < 0)
{
_CurrentCombination.Insert(0, -1);
index++;
}
MyIncrease(index - 1);
}
}
}
}
Example:
class Program
{
static void Main(string[] args)
{
var Pattern = new List<string>() { "a", "b", "c" };
Combinator<string> Combinator = new Combinator<string>(Pattern);
while (Combinator.HasFinised == false)
{
var Combined = Combinator.Next();
var Joined = string.Join("-", Combined);
Console.WriteLine(Joined);
}
Console.ReadKey();
}
}
If you want to combine items with others only:
static void Main(string[] args)
{
Combinator<int> Combinator = new Combinator<int>(new List<int>() { 1, 2, 3 });
while (Combinator.HasFinised == false)
{
var NextCombination = Combinator.Next();
var DistinctCheck = NextCombination.ToList().Distinct();
if (DistinctCheck.Count() == NextCombination.Count())
{
Console.WriteLine(string.Join(String.Empty, NextCombination.Select(Item => Item.ToString())));
}
}
Console.ReadKey();
}

How can I retrieve a set of unique arrays from a list of arrays using LINQ?

I have such construction
List<int[]> propIDs = new List<int[]>();
Can I get all unique value from propIDs with LINQ.For example I have list of
(1,2)
(4,5)
(1,5)
(1,2)
(1,5)
and I must get
(1,2)
(4,5)
(1,5)
You can use the overload of Enumerable.Distinct that takes an equality comparer.
class IntPairArrayComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] left, int[] right)
{
if (left.Length != 2) throw new ArgumentOutOfRangeException("left");
if (right.Length != 2) throw new ArgumentOutOfRangeException("right");
return left[0] == right[0] && left[1] == right[1];
}
public int GetHashCode(int[] arr)
{
unchecked
{
return (arr[0].GetHashCode() * 397) ^ arr[1].GetHashCode();
}
}
}
IEnumerable<int[]> distinctPairs = propIDs.Distinct(new IntPairArrayComparer());
If you want collections larger than pairs:
class IntArrayComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] left, int[] right)
{
if (left.Length != right.Length) return false;
return left.SequenceEquals(right);
}
public int GetHashCode(int[] arr)
{
unchecked
{
int hc = 1;
foreach (int val in arr) hc = hc * 397 ^ val.GetHashCode();
}
}
}
If all of your int arrays are two elements long, you could also use Tuples instead which will allow you to use the Distinct without a custom equality comparer:
IEnumerable<Tuple<int, int>> propIDs = [] { Tuple.Create(1,2), … };
IEnumerable<Tuple<int, int>> distinctPairs = propIDs.Distinct();
Below is a complete and working application of your need.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ListsAndArrays
{
class Program
{
static void Main(string[] args)
{
List<int[]> propIDs = new List<int[]>();
propIDs.Add(new[] { 1, 2 });
propIDs.Add(new[] { 4, 5 });
propIDs.Add(new[] { 1, 5 });
propIDs.Add(new[] { 1, 2 });
propIDs.Add(new[] { 1, 5 });
var distinct = propIDs.Distinct(new DistinctIntegerArrayComparer());
foreach (var item in distinct)
{
Console.WriteLine("{0}|{1}", item[0], item[1]);
}
if (Debugger.IsAttached)
{
Console.ReadLine();
}
}
private class DistinctIntegerArrayComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] x, int[] y)
{
if (x.Length != y.Length) { return false; }
else if (x.Length != 2 || y.Length != 2) { return false; }
return x[0] == y[0] && x[1] == y[1];
}
public int GetHashCode(int[] obj)
{
return -1;
}
}
}
}
This code works for any length arrays.
class MyEqualityComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] item1, int[] item2)
{
if (item1 == null && item2 == null)
return true;
if ((item1 != null && item2 == null) ||
(item1 == null && item2 != null))
return false;
return item1.SequenceEqual(item2);
}
public int GetHashCode(int[] item)
{
if(item == null)
{
return int.MinValue;
}
int hc = item.Length;
for (int i = 0; i < item.Length; ++i)
{
hc = unchecked(hc * 314159 + item[i]);
}
return hc;
}
}
and the code for distinct:
var result = propIDs.Distinct(new MyEqualityComparer());
Assuming that you can't use Tuple<T1, T2> which already provides equality you can instead create your own IEqualityComparer<T> that defines equality for arrays by simply requiring that all elements are equal sequentially:
class ArrayEqualityComparer<T> : IEqualityComparer<T[]> {
public Boolean Equals(T[] x, T[] y) {
if (x.Length != y.Length)
return false;
return x.Zip(y, (xx, yy) => Equals(xx, yy)).All(equal => equal);
}
public Int32 GetHashCode(T[] obj) {
return obj.Aggregate(0, (hash, value) => 31*hash + value.GetHashCode());
}
}
Then you can easily get the distinct values:
var distinctPropIDs = propIDs.Distinct(new ArrayEqualityComparer<Int32>());
public return List<Tuple<double, double>> uniquePairs(List<double[]> lst)
{
HashSet<Tuple<double, double>> hash = new HashSet<Tuple<double, double>>();
for (int i = 0; i < lst.count; i++)
{
hash.Add(new Tuple<double, double>(lst[i][0], lst[i][1]))
}
List<Tuple<double, double>> lstt = hash.Distinct().ToList();
}
For example:
List<double[]> lst = new List<double[]> {new double[] { 1, 2 }, new double[] { 2, 3 }, new double[] { 3, 4 }, new double[] { 1, 4 }, new double[] { 3, 4 }, new double[] { 2, 1 }}; // this list has 4 unique numbers, 5 unique pairs, the desired output would be the 5 unique pairs (count = 5)
List<Tuple<double, double>> lstt = uniquePairs(lst);
Console.WriteLine(lstt.Count().ToString());
the output is 5
HashSet, a set is a collection that contains no duplicate elements:
var propIDs = HashSet<Tuple<int,int>>

Categories