Issue with LINQ SequenceEquals extension in C# - c#

I was trying out possibilities to check a string to be an palindrome with the following logic
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Anagram solver");
Console.WriteLine(IsPalindrome("HIMA", "AMHI").ToString());
Console.ReadKey();
}
static bool IsPalindrome(string s1, string s2)
{
return s1.OrderBy(c => c).SequenceEqual(s2.OrderBy(c => c));
}
}
My idea was to get character literals in a string, and compare with that of characters from another string to deduce for a possible palindrome. Is such a thing possible with LINQ SequenceEqual method ?
Looking from the sample above,
'H' shall be compared with 'A' (default equality comparison)
'I' shall be compared with 'M'
'M' shall be compared with 'H'
'A' shall be compared with 'I'
Can any one guide me here.
Thanks and Cheers
Srivatsa

If you want palindrome then you should not order them, just reverse and match -
static bool IsPalindrome(string s1, string s2)
{
return s1.SequenceEqual(s2.Reverse());
}
for case-insensitivity try -
static bool IsPalindrome(string s1, string s2)
{
return s1.ToLower().SequenceEqual(s2.ToLower().Reverse());
}

In your case, "HIMA" and "AMHI" are sorted by the OrderBy LINQ function, which results in two collections containing the characters "AHIM". If you call SequenceEqual this returns true.
For SequenceEqual to return true, both collections have to have the same amount of elements in exactly the same order. No elements are allowed to be duplicated or stored at another position.
If you want to determine if two words are anagrams, that is exactly the functionality you want.
For palindromes, you could use the following:
public bool CheckPalindrome(string first, string second)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullExcpetion("second");
return first.Reverse().SequenceEquals(second);
}

You could use this method:
public static bool IsPalindromWith(this string str1, string str2)
{
if(str1 == null || str2 == null) return false;
return str1.SequenceEqual(str2.Reverse());
}
Usage: bool isPalindrom = "HIMA".IsPalindromWith("AMIH");
However, it is a very simple approach which ignores many edge cases.
Here is a better version that takes at least the case into account:
public static bool IsPalindromWith(this string str1, string str2, StringComparison comparison = StringComparison.CurrentCultureIgnoreCase)
{
if(str1 == null || str2 == null) return false;
char[] str2Chars = str2.ToCharArray();
Array.Reverse(str2Chars);
return str1.Equals(new String(str2Chars), comparison);
}

To elaburate on the existing (and i my opinion corrent) answer by #feO2x
Try looking at it like this:
static bool IsAnagram(string s1, string s2)
{
var lst1 = s1.OrderBy(c => c); //will result in { 'A','H','I', 'M' }
var lst2 = s2.OrderBy(c => c); //will *also* result in { 'A','H','I', 'M' }
return lst1.SequenceEqual(lst2);
}
The OrderBy(...) destroys the original order which you are trying to test.
Simply removing them will solve your problem:
static bool IsAnagram(string s1, string s2)
{
var lst1 = s1.AsEnumerable();
var lst2 = s2.AsEnumerable();
return lst1.SequenceEqual(lst2);
}

Related

How to check a string variable type

I am programming a thing where I am asking a user to type in several ingridients, how much they want of it and what the kg/ml/liter/etc price is. So that I add all the cost values(after multiplying the cost per unit times the measure they want) later display for them the most cheap and expensive ones. This is done in Visual Studio and the language is in C#. So I am typing this:
static void Ingridiens()
{
string ingridiensen;
Console.Write("Vad för ingridiIngridiensens behöver du?\nIngridiens: ");
ingridiensen = Console.ReadLine();
listaformat.Add(ingridiensen);
PrisPerEnhetEtt(prisetPerEnhet);
}
Ignore the variable names since they are in swedish. What I want help in is to check whether the input of the user is letters or something else. If they are not letters like numbers or any other special characters I want to return an error. And I also want to check if they are typinh one letter or not. It is one letter I still wanna return an error. But if they both type in letters(i.e. at least 2 letters) than I want to move on to the next method which in my case is PrisPerEnhetEtt.
I am finding it hard to fix this. I tried a lot of stuff, if statements, switch statements but i seems i need to invoke boolean variables. I am not sure how to do it. I am quite new to programming.
Thanks for all the help! :D
Using foreach
You can parse the chars of the string and use char.IsLetter and char.IsNumber or char.IsDigit like that with or without extension methods:
static public class StringHelper
{
static public bool IsText(this string input)
{
foreach ( char c in input )
if ( c != ' ' && !char.IsLetter(c) )
return false;
return true;
}
static public bool IsNumber(this string input)
{
foreach ( char c in input )
if ( !char.IsNumber(c) )
return false;
return true;
}
}
Difference between Char.IsDigit() and Char.IsNumber() in C#
Using Linq
using System.Linq;
static public bool IsText(this string input)
{
return input.All(c => c == ' ' || char.IsLetter(c));
}
static public bool IsNumber(this string input)
{
return input.All(c => char.IsNumber(c));
}
Or:
static public bool IsText(this string input)
=> input.All(c => c == ' ' || char.IsLetter(c));
static public bool IsNumber(this string input)
=> input.All(char.IsNumber);
Linq can be as slow as it is faster than loops depending on the processing done on the nature and the complexity and the amount of data. But Linq provides simple, clean, small, maintainable, robust, and magical code once learned.
For vs. Linq - Performance vs. Future
Is a LINQ statement faster than a 'foreach' loop?
Is the LINQ version faster than the foreach one?
Usage
string ingridiensen;
Console.Write("Vad för ingridiIngridiensens behöver du?\nIngridiens: ");
ingridiensen = Console.ReadLine();
Console.WriteLine(ingridiensen.IsNumber());
Console.WriteLine(ingridiensen.IsText());
Remark
In fact we can write to check if integer or double:
return int.TryParse(input, out var _);
return double.TryParse(input, out var _);
Advanced conditions
You can adapt the test conditions and create as many methods to match your needs : lower or upper case, space or no space allowed, point, special chars, integers, decimals, and so on:
static public bool IsWhatYouNeed(this string input)
{
foreach ( char c in input )
if ( !match(c) )
return false;
return true;
void bool match(char c)
{
...
}
}
You can also use the char position if needed:
static public bool IsWhatYouNeed(this string input)
{
for ( int index = 0; index < input.Length; index++ )
if ( !match(input[index], index) )
return false;
return true;
void bool match(char c, int pos)
{
...
}
}
The code above is lousy but written to give you the idea if needed.

Unexpected results for checking if a character is a symbol

I am creating a string extension to check if a string is all symbols or not however it is not working as I would expect it to, so far I have the following:
// Class for: String extensions
public static class StringExtension
{
// Method for: Determining if a string contains only symbols
public static bool ContainsOnlySymbols(this String inputString)
{
// Identifiers used are:
bool containsMore = false;
// Go through the characters of the input string checking for symbols
foreach (char character in inputString.ToCharArray())
{
// This line needs || Char.IsPunctuation(character) also
// Credit: #asantaballa
containsMore = Char.IsSymbol(character) ? false : true;
if (containsMore)
{
return containsMore;
}
}
// Return the results
return containsMore; // Edited after answer: <-- mistake here
}
}
Now if I use this extension on the following two strings I get the opposite of what I expect to see:
string testString = "!=";
I expect this to be all symbols, but
I expect: testString.ContainsOnlySymbols() => true
I get: testString.ContainsOnlySymbols() => false
Now if I use the next test string:
string testString = "Starts with";
I expect this to have no symbols
I expect: testString.ContainsOnlySymbols() => false
I get: testString.ContainsOnlySymbols() => true
A couple problems:
In your loop, you are really only getting the option related to the last character. And or clause should take care of it.
containsMore = containsMore || !(Char.IsSymbol(character) || Char.IsPunctuation(character));
Then, you need a not at the end. If it doesn't contain more, then its only symbols
return ! containsMore;
You might want a special case for how to handle empty strings too. Not sure how you want to handle that. That will be your choice if an empty string should return true or false.
You can accomplish this with a one-liner. See these examples.
string x = "##=";
string z = "1234";
string w = "1234#";
bool b = Array.TrueForAll(x.ToCharArray(), y => (Char.IsSymbol(y) || Char.IsPunctuation(y))); // true
bool c = Array.TrueForAll(z.ToCharArray(), y => (Char.IsSymbol(y) || Char.IsPunctuation(y))); // false
bool e = Array.TrueForAll(w.ToCharArray(), y => (Char.IsSymbol(y) || Char.IsPunctuation(y))); // false
Checking all chars if all isSymbol or Punctuation. we return true here.
public static bool ContainsOnlySymbols(this String inputString)
{
return inputString.ToCharArray().All(x => Char.IsSymbol(x) || Char.IsPunctuation(x));
}
Test:
string testString = "Starts with"; // => false
string testString = "!="; // => true
string testString = "##"; // => true
string testString = "!Starts with"; // => false
I believe the IsSymbol method checks for a very specific set of character. You may want to do:
containsMore = (Char.IsSymbol(character) || Char.IsPunctuation(character)) ? false : true;
Wrote a quick program to show results for character and does show symptom. Might even be that all you need for your app is IsPunctuation.
33/!: IsSymbol=False, IsPunctuation=True
Program
using System;
namespace csharptestchis
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i <= 255; i++)
{
char ch = (char)i;
bool isSymbol = Char.IsSymbol(ch);
bool isPunctuation = Char.IsPunctuation(ch);
Console.WriteLine($"{i}/{ch}: IsSymbol={isSymbol}, IsPunctuation={isPunctuation} ");
}
}
}
}
Firstly, the idea is simple: you loop your string, if you meet a character non-symbol, return false. Until the end of string and you don't meet a character non-symbol. Voilà, return true.
public static bool ContainsOnlySymbols(string inputString)
{
// Identifiers used are:
bool containsMore = false;
// Go through the characters of the input string checking for symbols
foreach (char character in inputString)
{
containsMore = Char.IsSymbol(character) ? false : true;
if(!containsMore)
return false;
}
// Return the results
return true;
}
Secondly, there is a problem with your code, IsSymbol returns true only if your character is in these groups
MathSymbol, CurrencySymbol, ModifierSymbol, and OtherSymbol.
And fortunately, ! don't be in these groups. That means "!=" returns false.
So you must include others conditions like:
public static bool ContainsOnlySymbols(string inputString)
{
// Go through the characters of the input string checking for symbols
return inputString.All(c => Char.IsSymbol(c) || Char.IsPunctuation(c));
}
Or you have to write your own method to determine what symbol is acceptable and what is not.
Or if a string doesn't contain digit and letter, it can be considered symbol. You can do
public static bool ContainsOnlySymbols(string inputString)
{
// Go through the characters of the input string checking for symbols
return !inputString.Any(c => Char.IsLetterOrDigit(c));
}

Efficient way to Compare 2 strings for NULL or value and trim to compare if it's not NULL in C#

static void Main(string[] args)
{
string string1 = " "; // it can be NULL and any word too
string string2 = null; // it can be NULL and any word too
if (string.IsNullOrEmpty(string1))
{
if (string.IsNullOrEmpty(string2))
{
Console.WriteLine("Both the string Null & equal");
}
}
else
{
if (!string.IsNullOrEmpty(string2))
{
if(string1.Trim().Equals(string2.Trim(),StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("Both the string has value& equal");
}
}
}
}
This code checks for NULL or value of both the string ultimately to confirm both the string is same. Importantly it has to trim the white space to make it comparable and same time if the string is NULL then it can't be trimmed.
Going through all possible conditions, I have written this block of code and still believing someone can make it more efficient in terms of efficiency.
Thank you!
Assuming that you really meant to check for null rather than null or empty (according to your console comment), I'd implement the following method...
private bool checkEqualityWithTrim(string string1, string string2)
{
bool bothNull = string1 == null && string2 == null;
bool bothNonNullAndEqualTrimmed = string1 != null && string2 != null && string1.Trim() == string2.Trim();
return bothNull || bothNonNullAndEqualTrimmed;
}
Then you can just do...
var areEqual = checkEqualityWithTrim(string1, string2);
If the IsNullOrEmpty() was intentional, then just replace the bothNull line with
bool bothNull = string.IsNullOrEmpty(string1) && string.IsNullOrEmpty(string2);
Yeah, you're doing more checks than you need to do. If the strings are equal, you only need to check that one of the strings is null or whitespace. If so, you know the value in both strings. This works assuming that for you NULL and whitespace are equivalent.
public static void Main(string[] args)
{
string string1 = ""; // it can be NULL and any word too
string string2 = ""; // it can be NULL and any word too
if (String.Equals((string1 ?? "").Trim(), (string2 ?? "").Trim(),StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrEmpty(string1)) //since the strings are equal, check one of the strings
{
Console.WriteLine("Both strings are null or empty & equal");
}
else
{
Console.WriteLine("Both strings have values & are equal");
}
}
}
Here is my attempt for you.
If this is something that is going to be used a lot then maybe using extension methods may be the way to go.
I have created two extension methods for you.
1 that performs a null and whitespace check (both conditions will be treated as a null
the second performs the logic you are after.
Here is my attempt for you:
public static bool IsNull(this string source)
{
return string.IsNullOrWhiteSpace(source);
}
public static string IsNullOrSame(this string source, string comparer)
{
//check for both values are null
if (source.IsNull() && comparer.IsNull())
{
return "Both Values Null or contain whitespace only";
}
//check if string 1 is null and string two has a value.
if (source.IsNull() && !comparer.IsNull())
{
return "I don't have a Value, but string 2 does";
}
//check if string 1 has a value and string two is null
if (!source.IsNull() && comparer.IsNull())
{
return "I have Value, but string 2 doesn't";
}
//if we make it this far then both have a value, check if the string value matches
if(source.Trim().Equals(comparer.Trim(), StringComparison.OrdinalIgnoreCase))
{
return "Both value match";
}
//if values don't match
return "strings both have values but don't match";
}
Once you have included these extension methods into your project you can do something simple like:
var string1 = "some value";
var string2 = null;
var result = string1.IsNullOrSame(string2);
Console.WriteLine(result);
this would then result in the message "I have Value, but string 2 doesn't"
The reason for the multiple return statements is one purely for readability. If we meet a "condition" then there is no point performing any more checking and the nesting of multiple if's can get a bit tiresome to debug.
Hopefully this gives you the desired functionality you are after and efficiency.
you are looking for simple and maintainable code not efficiency...
i would code it like that:
(edited: now with all possible conditions)
{
String string1 = "";
String string2 = "";
if (String.IsNullOrEmpty(string1.Trim()) && String.IsNullOrEmpty(string2.Trim()))
{
Console.WriteLine("Both the string Null & equal");
}
else if (!String.IsNullOrEmpty(string1.Trim()) && String.IsNullOrEmpty(string2.Trim()))
{
Console.WriteLine("String2 is null and string1 is not!");
}
else if (String.IsNullOrEmpty(string1.Trim()) && !String.IsNullOrEmpty(string2.Trim()))
{
Console.WriteLine("String1 is null and string2 is not!");
}
else {
if (string1.Trim().Equals( string2.Trim())) {
Console.WriteLine("both strings are not null and Equals!");
}
else {
Console.WriteLine("both strings are not null! and not Equals");
}
}
}
If you can use C# 6 I would definitely suggest you to use the Null Conditional Operator (called also the Elvis operator):
var test = "";
var test2 = "";
if (String.IsNullOrEmpty(test?.Trim()) && String.IsNullOrEmpty(test2?.Trim()))
{
Console.WriteLine("both strings are null or empty and equal");
}
else
{
Console.WriteLine("both strings have value and are equal");
}
Also depending on what the string " " (space) means for you (empty or value) use IsNullOrEmpty (in the case of value) or IsNullOrWhitespace (in the case of empty).

Avoiding calls to mscorlib when comparing strings?

Is there a possible way that let me compare strings without calling the op_Equality function is mscorlib?
For example:
string s1 = "hi";
if(s1 == "bye")
{
Console.Writeline("foo");
}
Compiles to:
IL_0003: call bool [mscorlib]System.String::op_Equality(string, string)
And looking at op_Equality at mscorlib from GAC it calls another method Equals(string, string)
Code:
public static bool Equals(string a, string b)
{
return a == b || (a != null && b != null && string.EqualsHelper(a, b));
}
it uses the op code bne.une.s to compare those strings.
Now back at my question, how can I compare 2 strings without calling any function from the mscorlib like the method Equals does.
Now back at my question, how can I compare 2 strings without calling any function from the mscorlib like the method Equals does.
You can't - at least not without writing your own string comparison method from scratch.
At some point, you'd have to at least call String.Equals(a,b) (in order to have the private EqualsHelper method called, which does the actual comparison), which is also defined in mscorlib.
You could call Equals directly if you wish to avoid the operator call, though:
string s1 = "hi";
if (string.Equals(s1, "bye"))
{
Console.WriteLine("foo");
}
This would avoid the call to op_Equality, bypassing it and calling Equals directly.
That being said, there is really no reason to avoid calling op_Equality on strings in the first place...
It seems like you are asking for a string comparison function. Strings are basically arrays of characters that you can index in a similar fashion. Here is a simple string comparison algorithm.
public static bool Equals(string left, string right)
{
if (ReferenceEquals(left, null)) return ReferenceEquals(right, null);
if (left.Length != right.Length) return false;
for (int i = 0; i < left.Length; i++)
{
if (left[i] != right[i])
return false;
}
return true;
}
Having shown you that, there is no reason to avoid the mscorlib implementation, since my example makes several calls to mscorlib.
This is another way to compare:
string s1 = "Hello";
bool same = s1.CompareTo("Hello") == 0;
bool different = s1.CompareTo("GoodBye") != 0;
Console.WriteLine(same);
Console.WriteLine(different);
In this case, both should report "True" to the console.
Another way:
private static bool CheckEqual(string s1, string s2)
{
char[] c1 = (s1 == null) ? new char[0] : s1.ToCharArray();
char[] c2 = (s2 == null) ? new char[0] : s2.ToCharArray();
if (c1.Length != c2.Length) { return false; }
for (int i = 0; i < c1.Length; i++)
{
if (!c1[i].Equals(c2[i])) { return false; }
}
return true;
}
Given that the String class is provided by mscorlib, there is absolutely no way to compare two strings without using mscorlib. Even just using the indexer to get each character calls into mscorlib.
The only way to avoid mscorlib is to not use the .Net runtime, but that's pretty difficult if you want to use C#.

Refactoring with Lambda's and Delegates

I've just installed VS2008 and have run into a problem that I'm sure can be solved with either lambda's or delegates (or a combination!).
private string ReadData(TcpClient s, string terminator)
{
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminator));
return sb.ToString();
}
The problem is, sometimes I need to check if the string contains either of two different values. Sometimes I may need to check it for three values.
So what I propose, is to change " !sb.ToString().Contains(terminator)" to a function that is passed into the method.
I could write my different functions such as:
private bool compare1(string s, string t) {
return s.contains(t)
}
private bool compare2(string s, string t1, string t2) {
return (s.compare(t1) or s.compare(t2)
}
// etc...
Then when I want to compare with 3 different values, create a delegate to one of these functions, then pass that to the ReadData() method.
I'm very clueless when it comes to delegates, and I'm not sure if this seems like the right place for a lambda but something is telling me it is.
The calling code is this:
// Enter username .
if (HasData(s,"login:"))
SendData(s, switchUser + TelnetHelper.CRLF);
HasData is identical to ReadData, but returns a bool instead of a string (which I'd also like to factor out into one method using some trickery - but that's a secondary question - feel free to answer that though.
Just for reference:
private bool HasData(TcpClient s, string terminator)
{
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminator));
return sb.ToString().Contains(terminator);
}
It sounds like you're looking for a predicate function. Instead of hard coding the check, take a delegate as a parameter than can do the check
private string ReadData(TcpClient s, Func<string,bool> predicate)
{
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !predicate(sb));
return sb.ToString();
}
Then you can create several wrappers which just create the appropriate delegate and pass it down
public bool HasData(TcpClient c, string terminator) {
return HasData(c, (s) => s.Contains(terminator));
}
public bool HasData(TcpClient c, string t1, string t2) {
return HasData(c, (s) => s.Contains(t1) || s.Contains(t2));
}
You can even build a delegate on the fly based on arbitrary number of terminators
public bool HasData(TcpClient c, params string[] terminatorList) {
return HasData(c, (s) => terminatorList.Where(x => s.Contains(x)).Any());
}
One option would be to overload the ReadData() method to take a string array containing the values that you are checking for. Using an extension method, you could extend Contains() to take a string array.
Your ReadData() method could be:
private string ReadData(TcpClient s, string[] terminators) {
// Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminators));
return sb.ToString();
}
The Contains() method extension could be:
public static bool Contains ( this String str , String[] testValues )
{
foreach ( var value in testValues )
{
if ( str.Contains( value ) )
return true;
}
return false;
}
This implementation eliminates the need to create a new predicate each time you have a different number of strings to test for.
Because the syntax of the lambdas is somewhat foreign to myself (and the rest of my team) I ended up going with a slightly different solution. I couldn't figure out the syntax of .All() when modified from the .Any() function above.
I needed an .All() function as well, to ensure all the terminators in the list were found. So I ended up going with something like the following:
delegate bool Predicate (string s, params [] string terminators);
bool HasAll(string s, params string [] terminators) {
foreach (var t in terminators) {
if (!s.contains(t)) return false;
}
return true;
}
bool HasAny(string s, params string [] terminators) {
foreach (var t in terminators) {
if (s.contains(t)) return true;
}
return false;
}
// Just looking now, I could also pass in a bool to switch between the two and remove one of these functions. But this is fairly clear
string ReadData(TcpClient sock, Function predicate, params [] string terminators) {
var sb = new StringBuilder();
do
{
var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
} while (s.GetStream().DataAvailable && !predicate(sb.ToString(), terminators);
return sb.ToString();
}
Then the calling code looks like:
private void someFunc()
{
Predicate any = new Predicate(HasAny);
Predicate all = new Predicate(HasAll);
String response;
// Check all strings exist
response = ReadData(this.sock, all, "(", ")", "->")
if (all(response, "(", ")", "->")
SendData(this.sock, ...);
// Check any string exists
response = ReadData(this.sock, any, "Hi", "Hey", "Hello");
if (any(response, "Hi", "Hey", "Hello"))
SendData(this.sock, ...);
}
I'll probably add null checks into the Has[Any|All] functions, reverse the do..while to a while, and just check response != null instead of duplicating the params. This solutions suits all my use cases and is fairly human readable I think. As long as I make the small changes I mentioned just above.
This whole thing highlights for me my need to learn lambda expressions though!

Categories

Resources