c# compare strings with a comparison function - c#

I may be missing the obvious but how do I provide a compare function to string.Equals() ?
I need to test for string equality but allow the first letter to be of a different case, so StringComparison is not useful, but I can't see a way to provide my own function to string.Equals ?
var s1="John";
var s2="john";
if (string.Equals(s1, s2, ????)) console.Write("Equal!!");

The Equals method does not have an overload that takes a custom compare function, but you could write one as an extension method:
public static class Extensions
{
public static bool EqualsCaseExceptFirst(this string input, string other)
{
if (input == null) throw new NullReferenceException();
if (ReferenceEquals(input, other)) return true;
if (input.Length != other.Length) return false;
if (input.Length == 0) return true;
if (!input.Substring(0, 1).Equals(other.Substring(0, 1),
StringComparison.OrdinalIgnoreCase)) return false;
return input.Length == 1 || input.Substring(1).Equals(other.Substring(1));
}
}
A sample test might look like:
private static void Main()
{
var testStrings = new List<string>
{
"hello", "Hello", "HELLO", "hELLO"
};
var sample = "Hello";
foreach (var testString in testStrings)
{
var result = sample.EqualsCaseExceptFirst(testString);
Console.WriteLine($"'{sample}' == '{testString}' : {result}");
}
Console.WriteLine("----------");
sample = "HELLO";
foreach (var testString in testStrings)
{
var result = sample.EqualsCaseExceptFirst(testString);
Console.WriteLine($"'{sample}' == '{testString}' : {result}");
}
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output
You mentioned in the comments that you want to create an IEqualityComparer class, so here's a sample that simply reuses this method:
class CustomComparer : IEqualityComparer<string>
{
public bool Equals(string first, string second)
{
return first?.EqualsCaseExceptFirst(second) ?? false;
}
public int GetHashCode(string obj)
{
return obj?.GetHashCode() ?? 0;
}
}

You could just roll your own, add pepper and salt to taste
public static bool MyStringEquals(this string str1, string str2, StringComparison comparison = StringComparison.CurrentCulture)
=> !(str1?.Length > 0) || !(str2?.Length > 0) || string.Equals(str1.Substring(1), str2.Substring(1),comparison);
Usage
var s1 = "asd";
var s2 = "bsd";
var result = s1.MyStringEquals(s2, StringComparison.Ordinal);
Obviously, you will want to write yourself a bunch of test cases, and work out if this is what you want

Related

Handle variable number of out parameters with less code duplication in C#

I'm trying to write a function that populates strings with the contents of an array, or sets them to null. The number of strings is can vary and I don't want to add requirements like them all being part of the same array or class.
In C# you cannot combine param and out. Therefore the only way to do this seems to be to overload the method like this:
public void ParseRemainders(string[] remainders, out string p1)
{
p1 = null;
if ((remainders != null) && (remainders.Length > 0))
p1 = remainders[0];
}
public void ParseRemainders(string[] remainders, out string p1, out string p2)
{
p1 = null;
p2 = null;
if (remainders != null)
{
ParseRemainders(remainders, out p1);
if (remainders.Length > 1)
p2 = remainders[1];
}
}
public void ParseRemainders(string[] remainders, out string p1, out string p2, out string p3)
{
p1 = null;
p2 = null;
p3 = null;
if (remainders != null)
{
ParseRemainders(remainders, out p1, out p2);
if (remainders.Length > 2)
p3 = remainders[2];
}
}
.... and on forever ....
How can I avoid all this code duplication, ideally accepting an arbitrary number of parameters?
Edit: This is useful because you could do, say, ParseRemainders(remainders, out inputFileName, out outputFileName, out configFileName) and then avoid having to manually do
if (remainder.Length > 0) inputFileName = remainder[0];
if (remainder.Length > 1) outputFileName = remainder[1];
if (remainder.Length > 2) configFileName = remainder[2];
...
Sorry if this wasn't clear, I had a specific goal in mind which I why I didn't simply return a List<>.
Conclusion: Thanks to Botond Balázs for the answer, particularly the hint that this is called "array destructuring". As they point out, and as this question confirms, it is not possible in the current version of C#: Destructuring assignment - object properties to variables in C#
I would take a different approach than any of the answers so far.
static class Extensions {
public static SafeArrayReader<T> MakeSafe<T>(this T[] items)
{
return new SafeArrayReader<T>(items);
}
}
struct SafeArrayReader<T>
{
private T[] items;
public SafeArrayReader(T[] items) { this.items = items; }
public T this[int index]
{
get
{
if (items == null || index < 0 || index >= items.Length)
return default(T);
return items[index];
}
}
}
There, now you have an array that gives you a default value instead of throwing:
var remainder = GetRemainders().MakeSafe();
var input = remainder[0];
var output = remainder[1];
var config = remainder[2];
Easy peasy. You have a problem with the semantics of a data type? Make a better data type that encapsulates the desired semantics.
If I understand you correctly, your use case would look like this:
var remainders = new[] { "a", "b", "c" };
string a, b, c;
ParseRemainders(remainders, a, b, c); // after this, a == "a", b == "b" and c == "c"
The feature you want to have in C# is called array destructuring, like in JavaScript:
var remainders = ["a", "b", "c"];
var [a, b, c] = remainders; // after this, a == "a", b == "b" and c == "c"
Unfortunately, as far as I know,
this cannot be solved in a general way using C#.
C# 7 will have tuple destructuring though.
Well, you can change your method to something like
public IEnumerable<string> ParseRemainders(string[] remainders)
{
var result = new List<string>();
///... your logic here, fill list with your strings according to your needs
return result;
}
Andys approach is fine but i'd return a string[] because it should have the same size as the input array and also return null if the input array was null:
public string[] ParseRemainders(string[] remainders)
{
if(remainders == null) return null;
var parsed = new string[remainders.Length];
for(int i = 0; i < remainders.Length; i++)
parsed[i] = ParseRemainder(remainders[i]);
return parsed;
}
To clarify what ParseRemainder(different method for a single string) does:
public string ParseRemainder(string remainder)
{
// parsing logic here...
return "the parsing result of remainder";
}
For completeness, this is how you can do this kind of thing in C#7 (Visual Studio 2017):
string[] test = { "One", "Two", "Three", "Four", "Five" };
var (a, b, c) = (test[0], test[2], test[4]);
Debug.Assert(a == "One");
Debug.Assert(b == "Three");
Debug.Assert(c == "Five");
The important line here is var (a, b, c) = (test[0], test[2], test[4]); which shows you the shorthand way of assigning several different variables from some elements of an array.
However, this doesn't help with the assigning of null if the array isn't long enough. You could get around that problem by writing a helper class:
public sealed class ElementsOrNull<T> where T: class
{
readonly IList<T> array;
public ElementsOrNull(IList<T> array)
{
this.array = array;
}
public T this[int index]
{
get
{
if (index < array.Count)
return array[index];
return null;
}
}
}
And then:
string[] test = { "One", "Two", "Three", "Four", "Five" };
var t = new ElementsOrNull<string>(test);
var (a, b, c) = (t[0], t[2], t[6]);
Debug.Assert(a == "One");
Debug.Assert(b == "Three");
Debug.Assert(c == null);
But I'm sure most people (myself included) will think that's more trouble than it's worth.
I think this gets pretty close to what you want. It doesn't need C# 7, works with any data element type, and isn't limited to arrays. You may want to pick better names than ValueReader/ReadValue, though.
static class Extensions
{
public static ValueReader<T> ReadValue<T>(this IEnumerable<T> source, out T value)
{
var result = new ValueReader<T>(source);
result.ReadValue(out value);
return result;
}
}
class ValueReader<T>
{
IEnumerator<T> _enumerator;
public ValueReader(IEnumerable<T> source)
{
if (source == null) source = new T[0];
_enumerator = source.GetEnumerator();
}
public ValueReader<T> ReadValue(out T value)
{
bool hasNext = _enumerator.MoveNext();
value = hasNext ? _enumerator.Current : default(T);
return this;
}
}
static class TestApp
{
public static void Main()
{
var remainders = new string[] { "test1", "test2", "test3" };
string inputFileName, outputFileName, configFileName, willBeSetToNull;
remainders
.ReadValue(out inputFileName)
.ReadValue(out outputFileName)
.ReadValue(out configFileName)
.ReadValue(out willBeSetToNull);
}
}
Just use an index in the array, eg:
remainers[0]; //same as p1
remainers[1]; //same as p2
remainers[2]; //same as p3
From your description I'm guessing your use case would be something similar to:
public void SomeMethod( ... )
{
string p1;
string p2;
....
ParseRemainders(string[] remainders, out string p1, out string p2);
...
}
public void SomeOtherMethod( ... )
{
string p1;
string p2;
string p3;
....
ParseRemainders(string[] remainders, out string p1, out string p2, out string p3);
...
}
You don't need to return strings this way. As already pointed out in other answers / comments, you can simply return an array of strings:
string[] ParseRemainders(string[] remainders)
{
var result = new string[remainder.Length];
result[0] = //whatever p1 would be
result[1] = //whatever p2 would be
//etc.
}
And you would use it like this:
public void SomeMethod( ... )
{
....
var parsed = ParseRemainders(string[] remainders);
string p1 = parsed[0];
string p2 = parsed[1];
....
}
That looks a lot better.
It feels like you're trying to over-complicate a simple null check, just go back to basics and keep it simple:
public string GetRemainder(string[] remainders, int index)
{
if ((remainders != null) && (remainders.Length > index))
return remainders[index];
return null;
}
Usage:
var inputFileName = GetRemainder(remainder, 0);
var outputFileName = GetRemainder(remainder, 1);
var configFileName = GetRemainder(remainder, 2);

How to override general comparison for two strings in C#

I've a question like the link below but much more complicated:
Ignoring accented letters in string comparison
I have a dictionary and some values inside like:
{[Ministère de l'économie, 139]}
{[Ministère des finances, 114]}
and for inserting new elements into the dictionary, as the sames codes, I wrote this:
if (!dict.ContainsKey(str))
{ dict.Add(str, Convert.ToDouble(number)); }
But when I want to check the existence of this value :
{[Ministère de l'economie, 139]} it returns it doesn't exist.
How can I implement this response to my code?
string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace);
On the other way, as the title of my question, how can I overwrite the general (or the main) string comparison method in my application?
So, combining with the linked answer for removal of accents and diacritics, you can make an IEqualityComparer<string> to supply to your dictionary:
public class IgnoreAccentsAndDiacriticsComparer:IEqualityComparer<string>
{
public bool Equals(string left, string right)
{
if(left == null && right == null){ return true; }
if(left == null || right == null){ return false; }
return string.Equals(RemoveDiacritics(left), RemoveDiacritics(right));
}
public int GetHashCode(string txt)
{
return RemoveDiacritics(txt).GetHashCode();
}
static string RemoveDiacritics(string text)
{
string formD = text.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
foreach (char ch in formD)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(ch);
if (uc != UnicodeCategory.NonSpacingMark)
{
sb.Append(ch);
}
}
return sb.ToString().Normalize(NormalizationForm.FormC);
}
}
Now, construct your dictionary using an instance of this comparer:
var myDic = new Dictionary<string, int>(new IgnoreAccentsAndDiacriticsComparer())
and try adding something accented:
myDic["Aimée"] = 1;
...and read it back out, without the accent:
Console.WriteLine(myDic["Aimee"]); //ŵôõ!
Starting from .NET 4.6 there are some interesting overloads of CompareInfo, so that the first solution suggested in https://stackoverflow.com/a/368850/613130 is usable in an IEqualityComparer<string>:
public class StringComparerIgnoreDiacritics : IEqualityComparer<string>
{
public static readonly StringComparerIgnoreDiacritics CurrentCulture = new StringComparerIgnoreDiacritics(CultureInfo.CurrentCulture.CompareInfo);
public readonly CompareInfo CompareInfo;
public StringComparerIgnoreDiacritics(CompareInfo compareInfo)
{
CompareInfo = compareInfo;
}
#region IEqualityComparer<string> Members
public bool Equals(string x, string y)
{
return CompareInfo.Compare(x, y, CompareOptions.IgnoreNonSpace) == 0;
}
public int GetHashCode(string obj)
{
return CompareInfo.GetHashCode(obj, CompareOptions.IgnoreNonSpace);
}
#endregion
}
and then
var myDic = new Dictionary<string, int>(var myDic = new Dictionary<string, int>(new IgnoreAccentsAndDiacriticsComparer()).CurrentCulture);

C# : Search the "v" which may or may not occur in the end of the string

So, I have a C# string input which can be either "ABCD12345678" or "ABCD1234" or "ABCD1233456v1" or "ABCD1233456v2" or "AVVV1233456v334" or "ABVV1233456V4".
I need to manipulate and remove the last may or may not be occurring "v"/"V" and get the result like :
"ABCD1233456"
"ABVV1233456"
"AVVV1233456"
Please help. If I use substring straightforward it works for only those where "v" occurs but throws exception for those where it doesn't occur.
It's a little confusing when you say at the end because the v isn't really at the end...
Anyway, what about Regex?
public static class VRemover
{
public static string Process(string input)
{
var regex = new Regex(#"(?'util'[a-z]+\d+)(v\d*)?", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
return regex.IsMatch(input)
? regex.Match(input).Groups["util"].Value
: input;
}
}
You can validate with this test (NUnit):
[TestFixture]
public class VRemoverTests
{
[Test]
public void Test()
{
var inputs = new[]
{
"ABCD12345678",
"ABCD1234",
"ABCD1233456v1",
"ABCD1233456v2",
"AVVV1233456v334",
"ABVV1233456V4"
};
var expecteds = new[]
{
"ABCD12345678",
"ABCD1234",
"ABCD1233456",
"ABCD1233456",
"AVVV1233456",
"ABVV1233456"
};
for (var i = 0; i < inputs.Length; i++)
{
var actual = VRemover.Process(inputs[i]);
Assert.AreEqual(expecteds[i], actual);
}
}
}
private static string RemoveString (string input)
{
var indexOf = input.LastIndexOf("v", StringComparison.OrdinalIgnoreCase);
if (indexOf < 0)
return input;
return input.Substring(0, indexOf);
}
private string ReplaceLastV(string input)
{
if (input.IndexOf("v", StringComparison.OrdinalIgnoreCase) > 0)
{
int lastIndexV = input.LastIndexOf("v", StringComparison.OrdinalIgnoreCase);
return input.Substring(0, lastIndexV);
}
return input;
}

Check String of chars

I'm looking for an fast method-solutions of my Problem to check a String, if this contains an min one char. IF String contains like any one of character in alphabet then return true, else false.
public bool checkString(String s)
{
return true || false;
}
For Example:
"1232133432454355467" return false
"134324239846c" return true
Try:
using System;
using System.Text.RegularExpressions;
class Program
{
static void Main()
{
var r = CheckString("112");
Console.WriteLine(r); // false
r = CheckString("112a");
Console.WriteLine(r); // true
}
public static bool CheckString(String input)
{
return Regex.Match(input, #"[a-zA-Z]").Success;
// or, as #Vlad L suggested
//return Regex.IsMatch(input, #"[a-zA-Z]");
}
}
If you want to verify against the "All Letters" Unicode character class, use this one instead:
return Regex.IsMatch(input, #"\p{L}");
Reference: Supported Unicode General Categories
If I understood the question correctly... This returns true if the string contains at least one letter.
public bool checkString(String s)
{
return s.Any(x => Char.IsLetter(x));
}
Try this with ToCharArray():
public bool checkString(String s)
{
bool retValue = s.ToCharArray()
.Any(c => ((int)c > 64 && (int)c < 91) ||
((int)c > 96 && (int)c < 123));
return retValue
}
Just for sake of completion.
// Regex to check the value consists of letters
// with atleast 1 character
private static Regex reg = new Regex(#"[a-zA-Z]+");
public bool checkString(String s)
{
return reg.Match(s).Success;
}
What about this?
if (Regex.IsMatch(yourString, "[a-zA-Z]"))
{
}
static void Main(string[] args)
{
Console.WriteLine(checkString("137563475634c756"));
}
static public bool checkString(String s)
{
return Regex.IsMatch(s, "[a-zA-Z]");
}
It Returns True.

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