string sorting in C# - c#

I have an array of strings like the following:
"access"
"Addition"
"account"
"base"
"Brick"
"zammer"
"Zilon"
I want them to sort them witht the following rules"
Capital letters for a given character should come first.
The capital and small letters should be sorted in their own groups.
Thus, the output should be:
"Addition"
"access"
"account"
"Brick"
"base"
"Zilon"
"zammer"
The language I am using is C# and .Net 4.0.

Proper set of OrderBy/ThenBy calls will do the trick.
Order by first letter lowercased, to get all as and As first, then bs and Bs, etc.
Then by IsLower(firstCharacter), which will get the uppercased items for each letter first.
Then by the entire string.
var sorted = source.OrderBy(s => char.ToLower(s[0]))
.ThenBy(s => char.IsLower(s[0]))
.ThenBy(s => s)
.ToList();

Try like this
List<string> list = new List<string>();
list.Add("access");
list.Add("Addition");
list.Add("account");
list.Add("base")
list.Add("Brick")
list.Add("zammer")
list.Add("Zilon")
list = list.Where(r => char.IsLower(r[0])).OrderBy(r => r)
.Concat(list.Where(r => char.IsUpper(r[0])).OrderBy(r => r)).ToList();
for (int i = 0; i < list.Count; i++)
Console.WriteLine(list[i]);

Below solution works for more than one Caps.
static void Main(string[] args)
{
var names = new List<String>() {
"access",
"Addition",
"ADDition",
"ADdition",
"account",
"base",
"Brick",
"zammer",
"Zilon"
};
names.Sort((one, two) =>
{
int result = 0;
var oneArray = one.ToCharArray();
var twoArray = two.ToCharArray();
var minLength = Math.Min(oneArray.Length, twoArray.Length) - 1;
var i = 0;
while (i < minLength)
{
//Diff Letter
if (Char.ToUpper(one[i]) != Char.ToUpper(two[i]))
{
result = Char.ToUpper(one[i]) - Char.ToUpper(two[i]);
break;
}
// Same Letter, same case
if (oneArray[i] == twoArray[i])
{
i++;
continue;
}
// Same Letter, diff case
result = one[i] - two[i];
break;
}
return result;
});
foreach (string s in names)
Console.WriteLine(s);
Console.WriteLine("done");

If you want to go beyond the first character, I should implement a comparer:
class MyComparer : IComparer<string>
{
public int Compare(string x, string y)
{
if ((x == null) && (y == null))
{
return 0;
}
if (x == null)
{
return 1;
}
if (y == null)
{
return -1;
}
var l = Math.Min(x.Length, y.Length);
for (var i = 0; i < l; i++)
{
var c = x[i];
var d = y[i];
if (c != d)
{
if (char.ToLowerInvariant(c) == char.ToLowerInvariant(d))
{
return StringComparer.Ordinal.Compare(new string(c, 1), new string(d, 1));
}
else
{
return StringComparer.OrdinalIgnoreCase.Compare(new string(c, 1), new string(d, 1));
}
}
}
return x.Length == y.Length ? 0 : x.Length > y.Length ? 1 : -1;
}
}
And then use it:
var myComparer = new MyComparer();
source.OrderBy(s => s, myComparer);

Related

How to sort descending a string list contains strings with alphabets & numbers in C# .net?

I have values in a string list like
AB1001_A
AB1001_B
AB1002_2
AB1002_C
AB1003_0
AB1003_
AB1003_B
AB1003_A
AB1001_0
AB1001_1
AB1001_2
AB1001_C
AB1002_B
AB1002_A
And I wanted to sort this by ascending order and the suffixes in descending order like below
AB1001_2
AB1001_1
AB1001_0
AB1001_C
AB1001_B
AB1001_A
AB1002_0
AB1002_B
AB1002_A
AB1003_0
AB1003_B
AB1003_A
AB1003_
How can I code it in C#.net?
It is quite strange sorting, but if you really need it, try something like this:
List<string> lItemsOfYourValues = new List<string>() {"AB1001_A","AB1001_B","AB1001_0" /*and next your values*/};
List<Tuple<string,string,string>> lItemsOfYourProcessedValues = new List<Tuple<string,string,string>>();
string[] arrSplitedValue;
for(int i = 0; i < lItemsOfYourValues.Count; i++)
{
arrSplitedValue = lItemsOfYourValues[i].Split("_");
lItemsOfYourProcessedValues.add(new Tuple<string,string,string>(lItemsOfYourValues[i], arrSplitedValue[0], arrSplitedValue[1]));
}
List<string> lSortedValues = lItemsOfYourProcessedValues.OrderBy(o => o.Item2).ThenByDescending(o => o.Item3).Select(o => o.Item1).ToList();
It looks like you have an error in your expected results, since AB1002_2 is in the input but not in the expected results.
Assuming that's just an error, and further assuming that the suffixes are limited to a single character or digit, you can solve the sorting by writing a special comparer like so:
static int compare(string x, string y)
{
var xParts = x.Split('_', StringSplitOptions.RemoveEmptyEntries);
var yParts = y.Split('_', StringSplitOptions.RemoveEmptyEntries);
if (xParts.Length != yParts.Length)
return yParts.Length - xParts.Length; // No suffix goes after suffix.
if (xParts.Length == 0) // Should never happen.
return 0;
int comp = string.Compare(xParts[0], yParts[0], StringComparison.Ordinal);
if (comp != 0 || xParts.Length == 1)
return comp;
if (char.IsDigit(xParts[1][0]) && !char.IsDigit(yParts[1][0]))
return -1; // Digits go before non-digit.
if (!char.IsDigit(xParts[1][0]) && char.IsDigit(yParts[1][0]))
return 1; // Digits go before non-digit.
return string.Compare(yParts[1], xParts[1], StringComparison.Ordinal);
}
Which you can then use to sort a string list, array or IEnumerable<string>, like so:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
static class Program
{
static void Main()
{
var strings = new []
{
"AB1001_A",
"AB1001_B",
"AB1002_2",
"AB1002_C",
"AB1003_0",
"AB1003_",
"AB1003_B",
"AB1003_A",
"AB1001_0",
"AB1001_1",
"AB1001_2",
"AB1001_C",
"AB1002_B",
"AB1002_A",
};
static int compare(string x, string y)
{
var xParts = x.Split('_', StringSplitOptions.RemoveEmptyEntries);
var yParts = y.Split('_', StringSplitOptions.RemoveEmptyEntries);
if (xParts.Length != yParts.Length)
return yParts.Length - xParts.Length;
if (xParts.Length == 0)
return 0;
int comp = string.Compare(xParts[0], yParts[0], StringComparison.Ordinal);
if (comp != 0 || xParts.Length == 1)
return comp;
if (char.IsDigit(xParts[1][0]) && !char.IsDigit(yParts[1][0]))
return -1; // Digits go before non-digit.
if (!char.IsDigit(xParts[1][0]) && char.IsDigit(yParts[1][0]))
return 1; // Digits go before non-digit.
return string.Compare(yParts[1], xParts[1], StringComparison.Ordinal);
}
var stringList = strings.ToList();
stringList.Sort(compare);
Console.WriteLine("Sorted list:");
Console.WriteLine(string.Join("\n", stringList));
var stringArray = strings.ToArray();
Array.Sort(stringArray, compare);
Console.WriteLine("\nSorted array:");
Console.WriteLine(string.Join("\n", stringArray));
var sequence = strings.Select(element => element);
var sortedSeq = sequence.OrderBy(element => element, Comparer<string>.Create(compare));
Console.WriteLine("\nSorted sequence:");
Console.WriteLine(string.Join("\n", sortedSeq));
}
}
}
Try it online on .Net Fiddle
Finally I got the soln by this
var mystrings = new []
{
"AB1001_A",
"AB1001_B",
"AB1002_2",
"AB1002_C",
"AB1003_0",
"AB1003_",
"AB1003_B",
"AB1003_A",
"AB1001_0",
"AB1001_1",
"AB1001_2",
"AB1001_C",
"AB1002_B",
"AB1002_A",
};
mystrings.Cast<string>().OrderBy(x => PadNumbers(x));
and then PadNumbers function as like below
public static string PadNumbers(string input)
{
return Regex.Replace(input, "[0-9]+", match => match.Value.PadLeft(10, '0'));
}

How to sort on 2 distinct rules in List<string>

I need to sort a List<string> following 2 rules.
My string element will always be formatted as XXXnnnnE or XXXnnnnD where X are capitalized letters from A to Z and n are digit from 0 to 9.
I want to sort my list alphabetically, but I want E string to come before D string as shown below
DFG0001D
AWK0007E
ORK0127E
AWK0007D
DFG0001E
ORK0127D
need to be sorted as
AWK0007E
AWK0007D
DFG0001E
DFG0001D
ORK0127E
ORK0127D
How could I achieve this ?
Thanks for help
Here is snippet how you can do this with Linq OrderBy and ThenByDescending operations:
string[] arr = { "DFG0001D", "AWK0007E", "ORK0127E", "AWK0007D", "DFG0001E", "ORK0127D" };
arr = arr
.OrderBy(r => r.Substring(0, 7))
.ThenByDescending(s => s.Substring(7, 1))
.ToArray();
you can use a custom delegate and compare the 1st 3 chars and the last one:
List<string> x = new List<string>();
x.Add("DFG0001D");
x.Add("AWK0007E");
x.Add("ORK0127E");
x.Add("AWK0007D");
x.Add("DFG0001E");
x.Add("ORK0127D");
x.Sort(delegate(string c1, string c2) {
string a = c1.Substring(0, 3)+c1.Substring(c1.Length-1, 1);
string b = c2.Substring(0, 3)+c2.Substring(c2.Length-1, 1);
return (a.CompareTo(b));
});
Console.WriteLine("After sort...");
foreach (string i in x)
{
Console.WriteLine(i);
}
Fiddle example : https://dotnetfiddle.net/YAzvB4
var list = new List<string>{
"DFG0001D",
"AWK0007E",
"ORK0127E",
"AWK0007D",
"DFG0001E",
"ORK0127D"
};
list.Sort((str1, str2) => {
var eq = string.Compare(str1.Substring(0, str1.Length - 1), str2.Substring(0, str2.Length - 1));
if (eq == 0)
eq = string.Compare(str2[str2.Length - 1].ToString(), "E");
return eq;
});
foreach (var str in list)
Console.WriteLine(str);
Output:
AWK0007E
AWK0007D
DFG0001E
DFG0001D
ORK0127E
ORK0127D
just implement your own comparer like this:
class CustomStringComparer : IComparer<string>
{
private readonly IComparer<string> _baseComparer;
public CustomStringComparer(IComparer<string> baseComparer)
{
_baseComparer = baseComparer;
}
public int Compare(string x, string y)
{
// strings are completely same
if (_baseComparer.Compare(x, y) == 0)
{
return 0;
}
// strings are completely same except last char
if (_baseComparer.Compare(x.Substring(0, x.Length - 2), y.Substring(0, y.Length - 2)) == 0)
{
// if last char is E then win
return x.Last() == 'E' ? -1 : 1;
}
// defaut compare everything else
return _baseComparer.Compare(x, y);
}
}
Then you are able doing this:
static void Main(string[] args)
{
List<string> list = new List<string>()
{
"DFG0001D",
"AWK0007E",
"ORK0127E",
"AWK0007D",
"DFG0001E",
"ORK0127D"
};
list.Sort(new CustomStringComparer(StringComparer.CurrentCulture));
foreach (var item in list)
{
Console.WriteLine(item);
}
}
And output is this:
AWK0007E
AWK0007D
DFG0001E
DFG0001D
ORK0127E
ORK0127D

Icomparer c# List

I have a list of image name like this {"1.jpg", "10.jpg", "2.jpg"}.
I would like to sort like this {"1.jpg", "2.jpg", "10.jpg"}.
I created this comparer. That means if x or y == "DSC_10.jpg", so if list is {"DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg", ...} don't sort and keep the list.
var comparer = new CompareImageName();
imageUrls.Sort(comparer);
return imageUrls;
public class CompareImageName : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null || y == null) return 0;
var l = x.Split('/');
var l1 = y.Split('/');
int a, b;
var rs = int.TryParse(l[l.Length - 1].Split('.')[0], out a);
var rs2 = int.TryParse(l1[l1.Length - 1].Split('.')[0], out b);
if (!rs || !rs2) return 0;
if (a == b || a == 0 && b == 0) return 0;
return a > b ? 1 : -1;
}
}
This sort correctly with name {"1.jpg", "10.jpg", "2.jpg"}, but incorrectly if list is {"DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg", ...}.
I read in MSDN:
What wrong with my code?
I think you're better off doing a bit of Regex for this. Try this solution:
public class CompareImageName : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null || y == null) return 0;
var regex = new Regex(#"/(((?<prefix>\w*)_)|)((?<number>\d+))\.jpg$");
var mx = regex.Match(x);
var my = regex.Match(y);
var r = mx.Groups["prefix"].Value.CompareTo(my.Groups["prefix"].Value);
if (r == 0)
{
r = int.Parse(mx.Groups["number"].Value).CompareTo(int.Parse(my.Groups["number"].Value));
}
return r;
}
}
Apart from the Regex string itself this is easier to follow the logic.
Here is your solution check this example, following class will do the comparison
public class NumericCompare : IComparer<string>
{
public int Compare(string x, string y)
{
int input1,input2;
input1=int.Parse(x.Substring(x.IndexOf('_')+1).Split('.')[0]);
input2= int.Parse(y.Substring(y.IndexOf('_')+1).Split('.')[0]);
return Comparer<int>.Default.Compare(input1,input2);
}
}
You can make use of this class like the following:
var imageUrls = new List<string>() { "DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg" };
var comparer = new NumericCompare();
imageUrls.Sort(comparer);
Console.WriteLine(String.Join("\n",imageUrls));
Try this with simple OrderBy
var SortedList = imageUrls.OrderBy(
x=>int.Parse(
x.Substring(x.IndexOf('_')+1).Split('.')[0])
).ToList();
Basically what you want to do is sort by the numeric part within the string. You are almost there. You just have to handle the part when you split a case like this DSC_2.jpg using a . then the first part is not all digits. So you need to get digits and then compare those. Here is the code. Please note I have made the assumption you will have backslash and if that is not the case then please handle it:
public int Compare(string x, string y)
{
if (x == null || y == null) return 0;
var nameX = x.Substring(x.LastIndexOf('/'));
var nameY = y.Substring(y.LastIndexOf('/'));
var nameXParts = nameX.Split('.');
var nameYParts = nameY.Split('.');
int a, b;
var rs = int.TryParse(nameXParts[0], out a);
var rs2 = int.TryParse(nameYParts[0], out b);
var nameXDigits = string.Empty;
if (!rs)
{
for (int i = 0; i < nameXParts[0].Length; i++)
{
if (Char.IsDigit(nameXParts[0][i]))
nameXDigits += nameXParts[0][i];
}
}
var nameYDigits = string.Empty;
if (!rs2)
{
for (int i = 0; i < nameYParts[0].Length; i++)
{
if (Char.IsDigit(nameYParts[0][i]))
nameYDigits += nameYParts[0][i];
}
}
int.TryParse(nameXDigits, out a);
int.TryParse(nameYDigits, out b);
if (a == b || a == 0 && b == 0) return 0;
return a > b ? 1 : -1;
}
Don't use imageUrls.Sort(comparer); on List because it doesn't accept 0 value as keeping the order of elements.
Reason:
The Sort performs an unstable sort; that is, if two elements are equal, their order might not be preserved. In contrast, a stable sort preserves the order of elements that are equal.
Link: https://msdn.microsoft.com/en-gb/library/w56d4y5z.aspx
Solution: Let's try to use OrderBy with your compare
var imageUrls1 = new List<string>() { "1.jpg", "10.jpg", "2.jpg" };
var imageUrls2 = new List<string>() { "DSC_1.jpg", "DSC_10.jpg", "DSC_2.jpg" };
var comparer = new CompareImageName();
//Sort normally
imageUrls1 = imageUrls1.OrderBy(p=>p, comparer).ToList();
//Keep the order as your expectation
imageUrls2 = imageUrls2.OrderBy(p=>p, comparer).ToList();
Maybe you can try doing this in a function instead of writing a comparator. I can't think of a good way to implement this logic as a comparator since there are different rules based on the contents (don't sort if the file name is not numeric).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace sortinglists
{
public class MainProgram
{
public static void Main()
{
var imageUrlsNumbers = new List<string>();
imageUrlsNumbers.Add("c:/a/b/1.jpg");
imageUrlsNumbers.Add("c:/a/b/10.jpg");
imageUrlsNumbers.Add("c:/a/b/2.jpg");
CustomSort(ref imageUrlsNumbers);
foreach (var imageUrl in imageUrlsNumbers)
{
Console.WriteLine(imageUrl);
}
var imageUrlsText = new List<string>();
imageUrlsText.Add("c:/a/b/DSC_1.jpg");
imageUrlsText.Add("c:/a/b/DSC_10.jpg");
imageUrlsText.Add("c:/a/b/DSC_2.jpg");
CustomSort(ref imageUrlsText);
foreach (var imageUrl in imageUrlsText)
{
Console.WriteLine(imageUrl);
}
}
public static void CustomSort(ref List<string> imageUrls)
{
if (imageUrls
.Select(s => s.Substring(s.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1))
.Select(t => t.Substring(0, t.IndexOf(".", StringComparison.OrdinalIgnoreCase)))
.Where(u => new Regex("[A-Za-z_]").Match(u).Success)
.Any())
{
imageUrls = imageUrls
.Select(x => x.Substring(x.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1))
.ToList();
}
else
{
imageUrls = imageUrls
.Select(v => v.Substring(v.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1))
.OrderBy(w => Convert.ToInt32(w.Substring(0, w.LastIndexOf(".", StringComparison.OrdinalIgnoreCase))))
.ToList();
}
}
}
}
The output for imageUrlsNumbers after sorting is:
1.jpg
2.jpg
10.jpg
And the output for imageUrlsText after sorting is:
DSC_1.jpg
DSC_10.jpg
DSC_2.jpg

Array.Sort for strings with numbers [duplicate]

This question already has answers here:
Natural Sort Order in C#
(18 answers)
Closed 8 years ago.
I have sample codes below:
List<string> test = new List<string>();
test.Add("Hello2");
test.Add("Hello1");
test.Add("Welcome2");
test.Add("World");
test.Add("Hello11");
test.Add("Hello10");
test.Add("Welcome0");
test.Add("World3");
test.Add("Hello100");
test.Add("Hello20");
test.Add("Hello3");
test.Sort();
But what happen is, the test.Sort will sort the array to:
"Hello1",
"Hello10",
"Hello100",
"Hello11",
"Hello2",
"Hello20",
"Hello3",
"Welcome0",
"Welcome2",
"World",
"World3"
Is there any way to sort them so that the string will have the correct number order as well?
(If there is no number at the end of the string, that string will always go first - after the alphabetical order)
Expected output:
"Hello1",
"Hello2",
"Hello3",
"Hello10",
"Hello11",
"Hello20",
"Hello100",
"Welcome0",
"Welcome2",
"World",
"World3"
Here is a one possible way using LINQ:
var orderedList = test
.OrderBy(x => new string(x.Where(char.IsLetter).ToArray()))
.ThenBy(x =>
{
int number;
if (int.TryParse(new string(x.Where(char.IsDigit).ToArray()), out number))
return number;
return -1;
}).ToList();
Create an IComparer<string> implementation. The advantage of doing it this way over the LINQ suggestions is you now have a class that can be passed to anything that needs to sort in this fashion rather that recreating that linq query in other locations.
This is specific to your calling a sort from a LIST. If you want to call it as Array.Sort() please see version two:
List Version:
public class AlphaNumericComparer : IComparer<string>
{
public int Compare(string lhs, string rhs)
{
if (lhs == null)
{
return 0;
}
if (rhs == null)
{
return 0;
}
var s1Length = lhs.Length;
var s2Length = rhs.Length;
var s1Marker = 0;
var s2Marker = 0;
// Walk through two the strings with two markers.
while (s1Marker < s1Length && s2Marker < s2Length)
{
var ch1 = lhs[s1Marker];
var ch2 = rhs[s2Marker];
var s1Buffer = new char[s1Length];
var loc1 = 0;
var s2Buffer = new char[s2Length];
var loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
s1Buffer[loc1++] = ch1;
s1Marker++;
if (s1Marker < s1Length)
{
ch1 = lhs[s1Marker];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(s1Buffer[0]));
do
{
s2Buffer[loc2++] = ch2;
s2Marker++;
if (s2Marker < s2Length)
{
ch2 = rhs[s2Marker];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(s2Buffer[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(s1Buffer);
string str2 = new string(s2Buffer);
int result;
if (char.IsDigit(s1Buffer[0]) && char.IsDigit(s2Buffer[0]))
{
var thisNumericChunk = int.Parse(str1);
var thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}
if (result != 0)
{
return result;
}
}
return s1Length - s2Length;
}
}
call like so:
test.sort(new AlphaNumericComparer());
//RESULT
Hello1
Hello2
Hello3
Hello10
Hello11
Hello20
Hello100
Welcome0
Welcome2
World
World3
Array.sort version:
Create class:
public class AlphaNumericComparer : IComparer
{
public int Compare(object x, object y)
{
string s1 = x as string;
if (s1 == null)
{
return 0;
}
string s2 = y as string;
if (s2 == null)
{
return 0;
}
int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
var ch1 = s1[marker1];
var ch2 = s2[marker2];
// Some buffers we can build up characters in for each chunk.
var space1 = new char[len1];
var loc1 = 0;
var space2 = new char[len2];
var loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do
{
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
var str1 = new string(space1);
var str2 = new string(space2);
var result = 0;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
var thisNumericChunk = int.Parse(str1);
var thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}
if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}
Call like so:
This time test is an array instead of a list.
Array.sort(test, new AlphaNumericComparer())
You can use LINQ combined with regex to ensure that you use only numbers that occur at the end of the string for your secondary ordering
test
.Select(t => new{match = Regex.Match(t, #"\d+$"), val = t})
.Select(x => new{sortVal = x.match.Success
?int.Parse(x.match.Value)
:-1,
val = x.val})
.OrderBy(x => x.val)
.ThenBy(x => x.sortVal)
.Select(x => x.val)
.ToList()

Distinct list<string> of chars

I have this:
List<string> list = new List<string>();
list.Add("a-b-c>d");
list.Add("b>c");
list.Add("f>e");
list.Add("f>e-h");
list.Add("a-d>c-b");
I want to delete duplicates. In this case duplicates are "a-b-c>d" and "a-d>c-b". Both have same chars but in diferente order.
I tried with:
list.Distinct().ToList();
But didn't work!
It looks like you want:
var distinct = list
.Select((str, idx) => new { Str = str, Idx = idx })
.GroupBy(pair => new HashSet<char>(pair.Str), HashSet<char>.CreateSetComparer())
.Select(grp => grp.OrderBy(p => p.Idx).First())
.ToList();
This will keep the first element and remove any later strings in the sequence which contains the same characters.
You can also use Aggregate to track the character sets you've already seen:
var distinct = list
.Aggregate(new Dictionary<HashSet<char>, string>(HashSet<char>.CreateSetComparer()), (dict, str) =>
{
var set = new HashSet<char>(str);
if (!dict.ContainsKey(set))
dict.Add(set, str);
return dict;
})
.Values
.ToList();
You'll have to define a custom IEqualityComparer that allows the system to understand when you consider two strings to be "equal". For example:
List<string> list = new List<string>();
list.Add("a-b-c>d");
list.Add("b>c-d-f");
list.Add("c-d-f>e");
list.Add("a-d>c-b");
var distinctItems = list.Distinct(new KeyFuncEqualityComparer<string>(
s => new String(s.AsEnumerable().OrderBy(c => c).ToArray())));
Result:
a-b-c>d
b>c-d-f
c-d-f>e
... using this generic IEqualityComparer implementation:
public class KeyFuncEqualityComparer<T> :IEqualityComparer<T>
{
private readonly Func<T, object> _getKey;
public KeyFuncEqualityComparer(Func<T, object> getKey)
{
_getKey = getKey;
}
public bool Equals(T x, T y)
{
return _getKey(x).Equals(_getKey(y));
}
public int GetHashCode(T obj)
{
return _getKey(obj).GetHashCode();
}
}
untested
list<int> dups = new list<int>();
for(int i = 0; i < list.count - 1; i++)
{
for(int j = 1; j < list.count; j++)
{
int len = list[j].length;
bool b = false;
for(int k = 0; k < list[i].length; k++)
{
if(k < len && (list[i][k] != list[j][k]))
{
b = false;
break;
}
b = true;
}
if(b == true)
{
dups.add(i);
}
}
}
then remove all dups from list

Categories