Get First 6 character from string which is distinct - c#

string[] arr = { "abcdefXXX872358", "abcdef200X8XXX58", "abcdef200X872359", "6T1XXXXXXXXXXXX11", "7AbcdeHA30XXX541", "7AbcdeHA30XXX691" };
how can I get distinct numbers from above where first 6 character must be distinct
result would be
abcdefXXX872358
6T1XXXXXXXXXXXX11
7AbcdeHA30XXX541
I try something like this
var dist = (from c in arr
select c).Select(a => a.Substring(0, 5)).Distinct();
which gives me first 5 character but I want whole string

Group on the first characters, and get the first string in each group:
IEnumerable<string> firstInGroup =
arr
.GroupBy(s => s.Substring(0, 6))
.Select(g => g.First());

I think the best method would be to implement an IEqualityComparer, as is hinted by the overload on List.Distinct()
public class firstXCharsComparer : IEqualityComparer<string>
{
private int numChars;
public firstXCharsComparer(int numChars)
{
this.numChars = numChars;
}
public bool Equals(string x, string y)
{
return x.Substring(0, numChars) == y.Substring(0, numChars);
}
public int GetHashCode(string obj)
{
return obj.Substring(0, numChars).GetHashCode();
}
}
static void Main(string[] args)
{
string[] arr = { "abcdefXXX872358", "abcdef200X8XXX58", "abcdef200X872359", "6T1XXXXXXXXXXXX11", "7AbcdeHA30XXX541", "7AbcdeHA30XXX691" };
var result = arr.ToList().Distinct(new firstXCharsComparer(6));
result.Count();
}

Related

Sort objects by string propery, empty string last

I have an array of objects which all contain string property. I want to sort objects by string property alphabetically in a way that objects with empty string property come at the end of the list. Currently I have this:
switches = switches.OrderBy(n => n.GetCurrentUser()).ToArray();
The problem is that it puts empty strings at the top of the list. How do I put objects with strings with value (sorted alphabetically) at the top and objects with empty strings at the bottom?
You can use:
switches = switches
.Select(n => new { TheObject = n, User = n.GetCurrentUser() })
.OrderBy(x => String.IsNullOrEmpty(x.User) ? 1 : 0)
.ThenBy(x => x.User)
.Select(x => x.TheObject)
.ToArray();
This will first build two groups, the one with empty user and others. OrderBy will move them to the end because 1 is more than 0. If you want them at the top use OrderByDescending.
Then i use ThenBy to sort alphabetically which will only matter for the non-empty users.
You can also use inline Comparer creation:
switches.OrderBy(n => n.GetCurrentUser(),
Comparer<string>.Create((a, b) =>
string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b)? 1
: !string.IsNullOrEmpty(a) && string.IsNullOrEmpty(b) ? -1
: string.Compare(a, b)));
OrderBy has an overload that accepts an IComparer<>T. This allows you to define your own sorting rules. You can start with the generic Comparer class and override the Compare method, eg :
public class EmptyLastComparer: Comparer<string>
{
public override int Compare(string x, string y)
{
if (String.IsNullOrWhiteSpace(x) && !String.IsNullOrWhiteSpace(y))
{
return 1;
}
else if (String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return 0;
}
else if (!String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return -1;
}
else
{
return x.CompareTo(y);
}
}
}
To use it, creatre a new EmptyLastComparer() instance and pass it to OrderBy :
var myStrings = new[] { "c", "A","a", "A","b", " "," ",null };
var ordered=myStrings.OrderBy(x => x, new EmptyLastComparer());
String comparison is more complex than just comparing two strings. String.Compare has overloads that allow case-insensitive comparisons, using specific cultures etc. The custom comparer could accepts a StringComparison parameter in its constructor to allow something similar, eg :
public class EmptyLastComparer : Comparer<string>
{
private readonly StringComparison _comparison;
public EmptyLastComparer(StringComparison comparison=StringComparison.CurrentCulture)
{
_comparison = comparison;
}
public override int Compare(string x, string y)
{
if (String.IsNullOrWhiteSpace(x) && !String.IsNullOrWhiteSpace(y))
{
return 1;
}
else if (String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return 0;
}
else if (!String.IsNullOrWhiteSpace(x) && String.IsNullOrWhiteSpace(y))
{
return -1;
}
else
{
return String.Compare(x,y, _comparison);
}
}
}
Perhaps even add some predefined comparers, just like StringComparer does :
public static EmptyLastComparer CurrentCulture =>
new EmptyLastComparer();
public static EmptyLastComparer CurrentCultureIgnoreCase =>
new EmptyLastComparer(StringComparison.CurrentCultureIgnoreCase);
public static EmptyLastComparer InvariantCulture =>
new EmptyLastComparer(StringComparison.InvariantCulture);
public static EmptyLastComparer InvariantCultureIgnoreCase =>
new EmptyLastComparer(StringComparison.InvariantCultureIgnoreCase);
public static EmptyLastComparer Ordinal =>
new EmptyLastComparer(StringComparison.Ordinal);
public static EmptyLastComparer OrdinalIgnoreCase =>
new EmptyLastComparer(StringComparison.OrdinalIgnoreCase);
And use them the same way, without allocating a new comparer each time :
var ordered=myStrings.OrderBy(x => x, EmptyLastComparer.InvariantCultureIgnoreCase);

Turn List into String with vowels and 'and' at the end [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm trying to make a string from a list. However, I want it to be arranged in a way that makes sense, so instead of the string being "I have apple, banana, Papa's citrus.", I want it to be "I have an apple, a banana, and Papa's citrus.". (Strings being "apple", "banana", and "Papa's citrus".)
I'd rather not change the strings making up my list; The thing I'm working on is a mod that changes the list depending on what other mods are enabled, so adding 'and' to the last string of each group won't work very well.
In all, the code I want would turn a list into a string, adding 'a' in front of words beginning with consonants, 'an' in front of words with vowels, and neither in front of words with apostrophes.
Thanks!
It's not clear what your input looks like, but it might be something like this console application. However, English is not my first language, so I might be terribly wrong:
static void Main(string[] args)
{
List<string> strings = new List<string>
{
"apple",
"banana",
"Papa's citrus"
};
var lastNdx = strings.Count - 1;
var sentence = "I have " + String.Join(", ",
strings.Select((s, ndx) =>
{
var ls = s.ToLower();
string ret = "";
if ("aeiou".Contains(ls[0]))
ret = "an " + s;
else if (ls.Contains("\'"))
ret = s;
else ret = "a " + s;
if (ndx == lastNdx)
ret = "and " + ret;
return ret;
}).ToArray() );
Console.WriteLine(sentence);
}
This answer is for #Stilgar. It is somewhat complicated, so I made it a second answer.
Download the latest version of the CMU Pronunciation Dictionary files and store them in a folder. Set the path in the CMUDictExt class to use that folder:
public static class CMUDictExt {
const string cmuFolder = #"D:\";
static IEnumerable<string> CMUFiles = Directory.EnumerateFiles(cmuFolder, "cmudict-*");
static Regex cmudictName = new Regex(#"cmudict-(?:\d+(?:\.\d+)?[a-z]?)+\.?(.*)$", RegexOptions.Compiled);
static string CMUFile(string ext) => CMUFiles.First(f => cmudictName.Match(f).Groups[1].Value == ext);
static Dictionary<string, string> phones;
static Dictionary<string, string[]> pronunciations;
public static ILookup<string, string> SymbolWords;
static HashSet<string> exceptions;
static CMUDictExt() {
phones = File.ReadLines(CMUFile("phones"))
.Select(l => l.Split('\t'))
.ToDictionary(pa => pa[0], pa => pa[1]);
pronunciations = File.ReadLines(CMUFile(""))
.Where(l => !l.StartsWith(";;;"))
.Where(l => Char.IsLetter(l[0]))
.Select(l => l.Split(" ").ToArray())
.ToDictionary(wg => wg[0].ToLowerInvariant(), wg => wg[1].Split(' '));
SymbolWords = pronunciations.SelectMany(wp => wp.Value.Select(s => (Word: wp.Key, s)))
.ToLookup(wp => wp.s, wp => wp.Word);
exceptions = pronunciations.Where(wp => (wp.Key.StartsWithVowel() ^ wp.Value[0].Phone() == "vowel")).Select(wp => wp.Key).ToHashSet();
}
public static string Phone(this string aSymbol) => phones.GetValueOrDefault(aSymbol.UpTo(ch => Char.IsDigit(ch)), String.Empty);
static string[] emptyStringArray = new string[] {};
public static string[] Pronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), emptyStringArray);
public static bool HasPronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), null) != null;
static readonly HashSet<char> vowels = "aeiou".ToHashSet<char>();
public static bool StartsWithVowel(this string w) => vowels.Contains(w[0]);
public static bool BeginsWithVowelSound(this string aWord) => exceptions.Contains(aWord) ? !aWord.StartsWithVowel() : aWord.StartsWithVowel(); // guess if not found
}
Using similar extension methods as before:
public static class IEnumerableExt {
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector, Comparer<TKey> keyComparer) => src.Aggregate((a, b) => keyComparer.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector) => src.Aggregate((a, b) => Comparer<TKey>.Default.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
}
public static class StringExt {
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
if (m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string UpTo(this string s, Func<char, bool> testfn) {
var m = s.Select((ch, Index) => new { ch, Index, Success = testfn(ch) }).FirstOrDefault(cit => cit.Success);
if (m != null && m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));
public static IEnumerable<string> Split(this string s, params string[] seps) => s.Split(StringSplitOptions.None, seps);
public static IEnumerable<string> Split(this string s, StringSplitOptions so, params string[] seps) {
int pos = 0;
do {
var sepPos = seps.Select(sep => new { pos = s.IndexOf(sep, pos) < 0 ? s.Length : s.IndexOf(sep, pos), len = sep.Length }).MinBy(pl => pl.pos);
if (sepPos.pos > pos || so == StringSplitOptions.None)
yield return s.Substring(pos, sepPos.pos - pos);
pos = sepPos.pos + sepPos.len;
} while (pos <= s.Length);
}
public static string FirstWord(this string phrase) => phrase.UpTo(ch => Char.IsWhiteSpace(ch));
public static bool IsAllLetters(this string s) => s.All(ch => Char.IsLetter(ch)); // faster than Regex
}
public static class DictionaryExt {
public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
}
You can now create some custom extensions to handle parts of the problem:
public static class FruitExt {
public static bool IsPossessive(this string phrase) => phrase.FirstWord().EndsWithOneOf("'s", "'");
public static string WithIndefiniteArticle(this string phrase) => (phrase.FirstWord().BeginsWithVowelSound() ? "an " : "a ") + phrase;
public static string ArticleOrPossessive(this string phrase) => phrase.IsPossessive() ? phrase : phrase.WithIndefiniteArticle();
}
Now the answer is calculated the same as before, but it handles many English language exceptions properly before defaulting to checking for a vowel:
var ans = ("I have " + src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ") + " and " + src.Last().ArticleOrPossessive() + ".");
Sample output:
I have an apple, a banana, Papa's citrus, an honest judge, a highchair, a university and an understanding.
Using some convenient extension methods:
public static class IEnumerableExt {
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
}
public static class StringExt {
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
if (m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));
}
You can then create some custom extension methods to break the problem down:
public static class FruitExt {
public static readonly HashSet<char> consonants = "bcdfghjklmnpqrstvwxyz".ToHashSet();
public static bool BeginsWithConsonant(this string w) => consonants.Contains(w[0]);
public static bool IsPossessive(this string w) => w.UpTo(new Regex(#"\s")).EndsWithOneOf("'s", "'");
public static string WithIndefiniteArticle(this string w) => (w.BeginsWithConsonant() ? "a " : "an ") + w;
public static string ArticleOrPossessive(this string w) => w.IsPossessive() ? w : w.WithIndefiniteArticle();
}
Note that BeginsWithConsonant fails for those (four?) words that begin with Y as a vowel - if it bothers you, you could add exceptions to the method.
Now the answer is fairly straight forward LINQ:
var ans = ("I have "+src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ")+" and "+src.Last().ArticleOrPossessive()+".");
Drop and Last aren't particularly efficient in the general case, but since we know we are using a List, they are fine (and the data is going to be short regardless).

Sort numbers then strings 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");
}

Entity Framework - How to convert from String to Integer in query

I have a legacy database with the same ID stored with multiple representations (string and integer). I need my query to join based on the key.
I know about SqlFunctions.StringConvert, but it doesn't work for my case, because the ID has 0-prefixes and the canonical representation of the number does not have string equivalence to other representations.
How can I convert my numeric string value into an integer from within my query?
Not sure if that what you are looking for but since your numeric string may have characters in it you can extract just the numbers from string
var getNumbers =Convert.ToInt32 (from t in stringToQuery
where char.IsDigit(t)
select t).ToArray().ToString());
May be you should try something like this:
//example
List<string> texts = new List<string>();
List<int> integers = new List<int>();
for (int j = 1; j <= 10; j++)
{
text.Add("00" + j.ToString());
integers.Add(j);
}
var a = from t in texts
join i in integers on Convert.ToInt32(t) equals i
select t;
Can't you just use TrimStart?
id.TrimStart('0');
(edit) Actually LINQ to Entities doesn't like that so you need to try this instead to strip the leading zeros before the comparison:
user trimstart in entity framework query
I would create a class to store your representation.
public sealed class CanonicalInt: IEquatable<int>, IEquatable<string>
{
private int _number;
private string _canonical
{
get
{
return ""; //logic to turn int into format
}
set
{
_number = 0; ////logic to turn string into format
}
}
public CanonicalInt(int number)
{
_number = number;
}
public CanonicalInt(string number)
{
_canonical = number;
}
public bool Equals(int other)
{
return _number.Equals(other);
}
public bool Equals(string other)
{
if(other == null)
return false;
return _canonical.Equals(other);
}
public static implicit operator int(CanonicalInt canonicalInt)
{
return canonicalInt._number;
}
public static implicit operator string(CanonicalInt canonicalInt)
{
return canonicalInt._canonical;
}
}
Usage:
var number = new CanonicalInt(23);
var result = number == 23; // True
if your string always ends with canonical number them may be something like a combination of patindex,datalength and stringconvert ? (please replace simulated SqlFunctions with real, it should run in 2entities context on tables then):
string [] Strings = new string [] {"0015","0-00152","00-0012"};
int[] Integers = new int[] { 15,12};
var MixedResult = Strings.Where(s => Integers.Any(i => (PatIndex(StringConvert(i),s) + DataLength(StringConvert(i))) == DataLength(s))).ToList();
these are just simulated SqlFunctions:
private string StringConvert(int x)
{
return x.ToString();
}
private int PatIndex(string pattern,string target)
{
return target.IndexOf(pattern);
}
private int DataLength(string x)
{
return x.Length;
}

How to remove duplicate combinations from a List<string> using LINQ

I'm having a List of String like
List<string> MyList = new List<string>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
};
I need to remove duplicate from the List i.e, if "A-B" and "B-A" exist then i need to keep only "A-B" (First entry)
So the result will be like
"A-B"
"C-D"
"C-E"
"D-E"
"F-G"
Is there any way to do this using LINQ?
Implement IEqualityComparer witch returns true on Equals("A-B", "B-A"). And use Enumerable.Distinct method
This returns the sequence you look for:
var result = MyList
.Select(s => s.Split('-').OrderBy(s1 => s1))
.Select(a => string.Join("-", a.ToArray()))
.Distinct();
foreach (var str in result)
{
Console.WriteLine(str);
}
In short: split each string on the - character into two-element arrays. Sort each array, and join them back together. Then you can simply use Distinct to get the unique values.
Update: when thinking a bit more, I realized that you can easily remove one of the Select calls:
var result = MyList
.Select(s => string.Join("-", s.Split('-').OrderBy(s1 => s1).ToArray()))
.Distinct();
Disclaimer: this solution will always keep the value "A-B" over "B-A", regardless of the order in which the appear in the original sequence.
You can use the Enumerable.Distinct(IEnumerable<TSource>, IEqualityComparer<TSource>) overload.
Now you just need to implement IEqualityComparer. Here's something for you to get started:
class Comparer : IEqualityComparer<String>
{
public bool Equals(String s1, String s2)
{
// will need to test for nullity
return Reverse(s1).Equals(s2);
}
public int GetHashCode(String s)
{
// will have to implement this
}
}
For a Reverse() implementation, see this question
You need to implement the IEqualityComparer like this:
public class CharComparer : IEqualityComparer<string>
{
#region IEqualityComparer<string> Members
public bool Equals(string x, string y)
{
if (x == y)
return true;
if (x.Length == 3 && y.Length == 3)
{
if (x[2] == y[0] && x[0] == y[2])
return true;
if (x[0] == y[2] && x[2] == y[0])
return true;
}
return false;
}
public int GetHashCode(string obj)
{
// return 0 to force the Equals to fire (otherwise it won't...!)
return 0;
}
#endregion
}
The sample program:
class Program
{
static void Main(string[] args)
{
List<string> MyList = new List<string>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
};
var distinct = MyList.Distinct(new CharComparer());
foreach (string s in distinct)
Console.WriteLine(s);
Console.ReadLine();
}
}
The result:
"A-B"
"C-D"
"C-E"
"D-E"
"F-G"
Very basic, but could be written better (but it's just working):
class Comparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return (x[0] == y[0] && x[2] == y[2]) || (x[0] == y[2] && x[2] == y[0]);
}
public int GetHashCode(string obj)
{
return 0;
}
}
var MyList = new List<String>
{
"A-B",
"B-A",
"C-D",
"C-E",
"D-C",
"D-E",
"E-C",
"E-D",
"F-G",
"G-F"
}
.Distinct(new Comparer());
foreach (var s in MyList)
{
Console.WriteLine(s);
}
int checkID = 0;
while (checkID < MyList.Count)
{
string szCheckItem = MyList[checkID];
string []Pairs = szCheckItem.Split("-".ToCharArray());
string szInvertItem = Pairs[1] + "-" + Pairs[0];
int i=checkID+1;
while (i < MyList.Count)
{
if((MyList[i] == szCheckItem) || (MyList[i] == szInvertItem))
{
MyList.RemoveAt(i);
continue;
}
i++;
}
checkID++;
}

Categories