c# check if 2 string includes the same chars - c#

I created 3 strings:
string a = "abcdgfg";
string b = "agbfcd";
string c = "axcvn";
I want to create the following check but i can't find out how:
Need to check if in string a and string b there are the same latters (never mind the order or if a latter shown more than once, just need to check if the same latter appears on both strings).
Then i need to do the same check for string a and string c.
as you can see: string a and string b have the same latters, stringa a and string c don't.
after i do the checking i simply print a massage if the strings have the same latter or not
Can anyone show me how to do the cheking?
Edit:
after check "a" and "c", it o should print the first latter that came up and not match between "a" and "c"
Thanks

I would suggest to use a HashSet<T> and it's SetEquals:
var aSet = new HashSet<char>(a);
var bSet = new HashSet<char>(b);
bool abSame = aSet.SetEquals(b);
Edit
after check "a" and "c", it o should print the first latter that came
up and not match between "a" and "c"
Then you can use HashSet.SymmetricExceptWith:
if (!abSame)
{
aSet.SymmetricExceptWith(bSet);
Console.WriteLine("First letter that is either in a and not in b or in b and not in a: " + aSet.First());
}
By the way, this can also replace the SetEquals check:
aSet.SymmetricExceptWith(bSet); // removes all characters which are in both
if (aSet.Any()) // missing charaters in one of both strings
{
Console.WriteLine("First letter that is either in a and not in b or in b and not in a: " + aSet.First());
}
The original answer using Except + Any had a subtle bug. Checking the length is not sufficient if there are duplicates. So you need to check from both directions or use Distinct first. Both approaches are inefficient compared to the HashSet.SetEquals-method which is a O(n) operation.
bool abSame = !a.Except(b).Any() && !b.Except(a).Any();

private bool HaveSameLetters(string a, string b)
{
return a.All(x => b.Contains(x)) && b.All(x => a.Contains(x));
}

Need to check if in string a and string b there are the same latters (never mind the order or if a latter shown more than once, just need to check if the same latter appears on both strings).
You can do it like this:
bool same = a.Distinct().OrderBy(c => c)
.SequenceEqual(b.Distinct().OrderBy(c => c));
This simply sorts the characters of two strings and checks if the two ordered sequence are equal. You can use the same method for a and c.

Related

C# check if a string contains all characters in a list accounting for duplicates

How would one check to see if a string can be made up of the chars in a char list (or array), also accounting for duplicate letters.
So, say I have the string (or char array/list/whatever) "abcc" and the character list (or array/string/whatever, it can all be converted to whatever) "['c'],['b'],['a']". How would one go about checking those two values against each other but also account that 'c' exists only once in the char array and therefore it should fail.
I know I could easily do this with either ".Contains" and run through the entire array of chars deleting that certain character from the array as I go, or use "IndexOf" and do the same thing, but I'd like to know if there's any way to not delete any items from the char array.
var input = "abbc";
var validChars = new List<char>() { 'c', 'b', 'a' };
var invalidChars = validChars.Where(validChar => input.Count(inputChar => inputChar == validChar) > 1);
Then you can check if invalidChars has any entries.
Edit: I will leave my original response, but based on your feedback, I think this is the correct procedure to follow.
First, I think it is probably best to convert your character array to some sort of dictionary, with a char as the key and an int as the value (to tell you how many of that letter you have available).
var charPool = new Dictionary<char, int>()
{
{ 'a', 2 },
{ 'b', 5 },
{ 'c', 5 },
{ 'd', 5 },
{ 'f', 0 },
...
{ 'z', 5 }
};
From there, you can take your input string and apply some LINQ to filter the values based on a criteria. I have chosen !charPool.ContainsKey(inputChar) OR charPool[inputChar] < inputWord.Count(c => c == inputChar) as my match criteria. These basically state "for each character that this filter is applied to, if 1. the key does not exist in the dictionary, or 2. the value, or number of that char in the dictionary, is less than the occurrences of that char in the input string, then that char is invalid.
Given an input string of
var inputWord = "bananafone";
The following code should return an IEnumerable of the letters a, f, and e. a, because we need 3 and only have 2; f, because our entry is at 0 and we need 1; and e, because no entry exists for that letter. Also, a .Distinct() is used because without it, for this example, a would get entered 3 times as iterates through the input string, applying the filter.
var charsNeeded = inputWord.Where(inputChar => !charPool.ContainsKey(inputChar) || charPool[inputChar] < inputWord.Count(c => c == inputChar)).Distinct();
Note that one downside of this approach is that we do not count the difference between how many characters we have, and how many we need. However I don't think it would be very difficult to implement; you could easily create another dictionary for the input string, for example, and then compare the two.
If you're dealing with large lists of characters and if they will all be ASCII characters, then to keep it efficient I'd think about going through the two strings and counting how often each character appeared.
Using your Scrabble comment, how about this: Get the total count of each character in your input and compare that to the count of the same character in your allowed character list. If the input string ever contains more of a character than the allowed character list does, the input string isn't valid.
private static bool ValidateString(string input)
{
bool retValue = true;
char[] validChars = { 'a', 'b', 'd' };
foreach (var character in input)
{
//count the number of times the character occurs in the input string
var characterCount = input.Count(c => c == character);
//count the number of times the character occurs in the allowed char array
var allowedCharacterCount = validChars.Count(c => c == character);
//if the string contains more than the character array allows, immediately fail.
if (characterCount > allowedCharacterCount)
{
retValue = false;
break;
}
}
return retValue;
}
This probably isn't the most efficient way to do things, especially if the string is valid, since it goes through the entire string character by character. But I think it's functional.
I suppose you could go about this by creating a tally of each of the letters in the string by converting to a char array and if your case you would have 3 ints (a,b,c)or an int array with (0-25) where a=1 b=1 and c=2 then compare by doing the same thing with the other array and saying if the array your checking against the strings a value is >= to the one of the strings a value then you're good and you can move on to checking b until the point you check to see whether your c value 2 is >= to the other value of c=1 and you get false so it stops.
You can make your search list (that is, the list of characters you're searching FOR in the target string) be an array or list of objects rather than just characters. Then you can have a boolean associated with each character to indicate "IsFoundInTarget" - that way you can "mark" each character without deleting it. Lastly, do a .Find(x=> x.Character == TheCharacterBeingSearchedFor && x.IsFoundInTarget == False) or something similar to handle multiple occurrences of a character.
We can use default method contains
var str="Ramesh";
str.contains("s").Count();
Linq is not that difficult to understand you can break this down into two steps if you like
1st
//Check to see if the input values exist in the collection
var input = "abbc";
var collection = new List<char>() { 'c', 'b', 'a' };
bool doesContain = collection.Any(item => input.Contains((char)item));
doesContain will return true
2nd
//check for duplicates in the input variable
var duplicatesList = input.GroupBy(s => s)
.SelectMany(grp => grp.Skip(1)).ToList();
using the debugger it will return b

Method adds not a necessary line to a list when it should not do so

I'm a beginner in c# and I am working with text exercises. I made a method to filter vehicle's plate numbers. It should consist of 3 letters and 3 integers ( AAA:152 ). My method sends the wrong plate numbers to a file, but also it adds that bad number to a good ones list.
private static string[] InvalidPlates(string[] csvLines, int fieldToCorrect)
{
var toReturn = new List<string>();
var toSend = new List<string>();
int wrongCount = 0;
for (int i = 0; i < csvLines.Length; i++)
{
string[] stringFields = csvLines[i].Split(csvSeparator[0]);
string[] values = stringFields[fieldToCorrect].Split(':');
if(Regex.IsMatch(values[0], #"^[a-zA-Z]+$") && Regex.IsMatch(values[1], "^[0-9]+$"))
{
toReturn.Add(string.Join(csvSeparator, stringFields));
}
else
{
toSend.Add(string.Join(csvSeparator, stringFields));
wrongCount++;
}
}
WriteLinesToFile(OutputFile, toSend.ToArray(), wrongCount);
return toReturn.ToArray();
}
Can somebody help me to fix that?
You need to constrain the possible length using quantifiers:
^[a-zA-Z]{3}\:\d{3}$
which literally means the following, in the strict order:
the strings begins from exactly 3 lowercase or uppercase English alphabet letters, continues with semicolon (:), and ends with exactly three digits
Remember that \ should be escaped in C#.
Also, there is no need to join stringFields back into a string, when you can use non-splitted csvLines[i]:
if (Regex.IsMatch(stringFields, #"^[a-zA-Z]{3}\\:\\d{3}$"))
toReturn.Add(csvLines[i]);
}
else
{
toSend.Add(csvLines[i]);
wrongCount++;
}
Another important thing is that your code is incorrect in terms of OOP. It is pretty inobvious that your method called InvalidPlates will save something to a file. It may confuse you after some time or other developers. There should be no "hidden" functionality, and all methods should actually do only the one thing.
Here is how I would do this using LINQ:
private static bool IsACorrectPlate(string p) => Regex.IsMatch(p, #"^[a-zA-Z]{3}\:\d{3}$");
private static void SortPlatesOut(string[] csvLines, int column, out string[] correct, out string[] incorrect)
{
var isCorrect = csvLines
.GroupBy(l => IsACorrectPlate(l.Split(';')[column]))
.ToDictionary(g => g.Key, g => g.ToArray());
correct = isCorrect[true];
incorrect = isCorrect[false];
}
// Usage:
string[] incorrect, correct;
SortPlatesOut(csvLines, 1, out correct, out incorrect);
File.WriteAllLines("", incorrect);
// do whatever you need with correct
Now, SortPlatesOut method has an expectable behavior without side effects. The code has also become two times shorter. At the same time, it looks more readable for me. If it looks non-readable for you, you can unpack LINQ and split some things other things up.

Enumerable.Except with IEqualityComparer

I have two string arrays, newArray and oldArray, and I want to use Enumberable.Except method to remove all items that are in newArray that are also in oldArray and then write the result to a csv file.
However, I need to use a custom comparer in order to check for formatting similarities(if there is a new line character in one array and not the other, I don't want this item being written to the file).
My code as of now:
string newString = File.ReadAllText(csvOutputFile1);
string[] newArray = newString.Split(new string[] {sentinel}, StringSplitOptions.RemoveEmptyEntries);
string oldString = File.ReadAllText(csvOutputFile2);
string[] oldArray = oldString.Split(new string[] { sentinel }, StringSplitOptions.None);
IEnumerable<string> differnceQuery = newArray.Except(oldArray, new Comparer());
using (var wtr = new StreamWriter(diffFile))
{
foreach (var s in differnceQuery)
{
wtr.WriteLine(s.Trim() + "#!#");
}
}
and the custom comparer class:
class Comparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
x = x.ToString().Replace(" ", "").Replace("\n", "").Replace("\r", "");
y = y.ToString().Replace(" ", "").Replace("\n", "").Replace("\r", "");
if (x == y)
return true;
else
return false;
}
public int GetHashCode(string row)
{
int hCode = row.GetHashCode();
return hCode;
}
}
The resulting file is not omitting the formatting difference items between the two arrays. So although it catches items that are in the newArray but not in the oldArray(like it should), it is also putting in items that are only different because of a \n or something even though in my custom comparer I am removing them.
The thing I really don't understand is when I debug and step through my code, I can see each pair of items being analyzed in my custom comparer class, but only when they are equal terms. If for example the string "This is\nthe 1st term" is in newArray and the string "This is the first array" is in oldArray, the debugger doesn't even enter the comparer class and instead jumps straight to the writeline part of my code in the main class.
simply: your hash-code does not correctly mirror your equality method. Strings like "a b c" and "abc" would return different values from GetHashCode, so it would never get around to testing Equals. GetHashCode must return the same result for any two values that could be equal. It is not, however, necessary that two strings that are not equal return different hash-codes (although it is highly desirable, otherwise everything will go into the same hash-bucket).
I guess you could use:
// warning: probably not very efficient
return x.Replace(" ", "").Replace("\n", "").Replace("\r", "").GetHashCode();
but that looks pretty expensive (lots of potential for garbage strings to be generated all the time)

Determine if a string is greater than another string

I have two strings
string A = "1.0.0.0";
string B = "1.0.0.1";
I need to evaluate somehow that B is greater than A (version wise) either converting those two strings to integers or decimals or something.
I tried the following
Decimal S = Convert.ToDecimal(A);
int S = Convert.ToInt32(A);
but keep getting the following error, "Input string was not in a correct format."
Any help will be appreciated.
See the Version Class.
You're able to do something like this:
Version a = new Version("1.0.0.0");
Version b = new Version("1.0.0.1");
if (b>a) //evaluates to true
blah blah blah
I haven't personally tested this exact scenario, but the Version class allows you to use comparison operators like I've shown here.
If your string has at most 4 numeric parts (separated by .), you can use the Version class to get a strongly typed entity that corresponds to these strings. Version implements the different comparison operators (==, >, < etc...) in the expected manner, so you can find out which is greater:
var a = new Version(A);
var b = new Version(B);
if(a > b)
// a is larger
else if (a < b)
// b is larger
else
// they are identical
If there are more than 4 parts, you will need to split each string to its numeric components, convert each one to a numeric equivalent and compare the two resulting collections.
Something like:
var aParts = A.Split('.');
var bParts = B.Split('.');
// assumes the string have the same number of parts
for(int i = 0; i < aParts.Length; i++)
{
var currA = int.Parse(aParts[i]);
var currB = int.Parse(bParts[i]);
if(currA == currB)
continue;
if(currA > currB)
// A is greater than B
else
// B is greater than A
}
You can take a look at System.Version class.
If the strings are in this format or can be converted to a version.
Version have comparers
If you want to compare version strings in .NET, then you can use the Version class.
Version version = new Version("1.0.0.0");
Version otherVersion = new Version(""1.0.0.1");
The class provides operators to check if a version is greater or lesser than another.
Split on the ".". Then convert each part to an int. Starting from the left: if A's fragment is lower, then report that A is first. If B's fragment is lower, then report that B is first. Otherwise, move to the next fragment. If you're at the last fragment already, report that they are equal.
If your strings have at most four parts (like version numbers), then as others suggested it's easier to use the System.Version class.
Instead of using VersionClass a fast approach would be something like this.
string A = "1.0.0.0";
string B = "1.0.0.1";
int versionA = Convert.ToInt32(A.Replace(".", string.Empty));
int versionB = Convert.ToInt32(B.Replace(".", string.Empty));
if (b>a)
//something will happen here
Replace changes the first string to the second one in this case string.Empty equals to "".

Can you reverse order a string in one line with LINQ or a LAMBDA expression

Not that I would want to use this practically (for many reasons) but out of strict curiousity I would like to know if there is a way to reverse order a string using LINQ and/or LAMBDA expressions in one line of code, without utilising any framework "Reverse" methods.
e.g.
string value = "reverse me";
string reversedValue = (....);
and reversedValue will result in "em esrever"
EDIT
Clearly an impractical problem/solution I know this, so don't worry it's strictly a curiosity question around the LINQ/LAMBDA construct.
Well, I can do it in one very long line, even without using LINQ or a lambda:
string original = "reverse me"; char[] chars = original.ToCharArray(); char[] reversed = new char[chars.Length]; for (int i=0; i < chars.Length; i++) reversed[chars.Length-i-1] = chars[i]; string reversedValue = new string(reversed);
(Dear potential editors: do not unwrap this onto multiple lines. The whole point is that it's a single line, as per the sentence above it and the question.)
However, if I saw anyone avoiding using framework methods for the sake of it, I'd question their sanity.
Note that this doesn't use LINQ at all. A LINQ answer would be:
string reverseValue = new string(original.Reverse().ToArray());
Avoiding using Reverse, but using OrderByDescending instead:
string reverseValue = new string(original.Select((c, index) => new { c, index })
.OrderByDescending(x => x.index)
.Select(x => x.c)
.ToArray());
Blech. I like Mehrdad's answer though. Of course, all of these are far less efficient than the straightforward approach.
Oh, and they're all wrong, too. Reversing a string is more complex than reversing the order of the code points. Consider combining characters, surrogate pairs etc...
I don't see a practical use for this but just for the sake of fun:
new string(Enumerable.Range(1, input.Length).Select(i => input[input.Length - i]).ToArray())
new string(value.Reverse().ToArray())
var reversedValue = value.ToCharArray()
.Select(ch => ch.ToString())
.Aggregate<string>((xs, x) => x + xs);
Variant with recursive lambda:
var value = "reverse me";
Func<String, String> f = null; f = s => s.Length == 1 ? s : f(s.Substring(1)) + s[0];
var reverseValue = f(value);
LP,
Dejan
You can use Aggregate to prepend each Char to the reversed string:
"reverse me".Aggregate("", (acc, c) => c + acc);
var reversedValue= "reverse me".Reverse().ToArray();
In addition to one previous post here is a more performant solution.
var actual0 = "reverse me".Aggregate(new StringBuilder(), (x, y) => x.Insert(0, y)).ToString();
public static string Reverse(string word)
{
int index = word.Length - 1;
string reversal = "";
//for each char in word
for (int i = index; index >= 0; index--)
{
reversal = reversal + (word.Substring(index, 1));
Console.WriteLine(reversal);
}
return reversal;
}
Quite simple. So, from this point on, I have a single method that reverses a string, that doesn't use any built-in Reverse functions.
So in your main method, just go,
Console.WriteLine(Reverse("Some word"));
Technically that's your one liner :P
If we need to support combining characters and surrogate pairs:
// This method tries to handle:
// (1) Combining characters
// These are two or more Unicode characters that are combined into one glyph.
// For example, try reversing "Not nai\u0308ve.". The diaresis (ยจ) should stay over the i, not move to the v.
// (2) Surrogate pairs
// These are Unicode characters whose code points exceed U+FFFF (so are not in "plane 0").
// To be represented with 16-bit 'char' values (which are really UTF-16 code units), one character needs *two* char values, a so-called surrogate pair.
// For example, try "The sphere \U0001D54A and the torus \U0001D54B.". The ๐•Š and the ๐•‹ should be preserved, not corrupted.
var value = "reverse me"; // or "Not nai\u0308ve.", or "The sphere \U0001D54A and the torus \U0001D54B.".
var list = new List<string>(value.Length);
var enumerator = StringInfo.GetTextElementEnumerator(value);
while (enumerator.MoveNext())
{
list.Add(enumerator.GetTextElement());
}
list.Reverse();
var result = string.Concat(list);
Documentation: MSDN: System.Globalization.StringInfo Class
string str="a and b";
string t="";
char[] schar = str.Reverse().ToArray();
foreach (char c in schar )
{
test += c.ToString();
}

Categories