Char/String comparison - c#

I'm trying to have a suggestion feature for the search function in my program eg I type janw doe in the search section and it will output NO MATCH - did you mean jane doe? I'm not sure what the problem is, maybe something to do with char/string comparison..I've tried comparing both variables as type char eg char temp -->temp.Contains ...etc but an error appears (char does not contain a definition for Contains). Would love any help on this! 8)
if (found == false)
{
Console.WriteLine("\n\nMATCH NOT FOUND");
int charMatch = 0, charCount = 0;
string[] checkArray = new string[26];
//construction site /////////////////////////////////////////////////////////////////////////////////////////////////////////////
for (int controlLoop = 0; controlLoop < contPeople.Length; controlLoop++)
{
foreach (char i in userContChange)
{
charCount = charCount + 1;
}
for (int i = 0; i < userContChange.Length; )
{
string temp = contPeople[controlLoop].name;
string check=Convert.ToString(userContChange[i]);
if (temp.Contains(check))
{
charMatch = charMatch + 1;
}
}
int half = charCount / 2;
if (charMatch >= half)
{
checkArray[controlLoop] = contPeople[controlLoop].name;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
Console.WriteLine("Did you mean: ");
for (int a = 0; a < checkArray.Length; a++)
{
Console.WriteLine(checkArray[a]);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

A string is made up of many characters. A character is a primitive, likewise, it doesn't "contain" any other items. A string is basically an array of characters.
For comparing string and characters:
char a = 'A';
String alan = "Alan";
Debug.Assert(alan[0] == a);
Or if you have a single digit string.. I suppose
char a = 'A';
String alan = "A";
Debug.Assert(alan == a.ToString());
All of these asserts are true
But, the main reason I wanted to comment on your question, is to suggest an alternative approach for suggesting "Did you mean?". There's an algorithm called Levenshtein Distance which calculates the "number of single character edits" required to convert one string to another. It can be used as a measure of how close two strings are. You may want to look into how this algorithm works because it could help you.
Here's an applet that I found which demonstrates: Approximate String Matching with k-differences
Also the wikipedia link Levenshtein distance

Char type cannot have .Contains() because is only 1 char value type.
In your case (if i understand), maybe you need to use .Equals() or the == operator.
Note: for compare String correctly, use .Equals(),
the == operator does not work good in this case because String is reference type.
Hope this help!

char type dosen't have the Contains() method, but you can use iit like this: 'a'.ToString().Contains(...)
if do not consider the performance, another simple way:
var input = "janw doe";
var people = new string[] { "abc", "123", "jane", "jane doe" };
var found = Array.BinarySearch<string>(people, input);//or use FirstOrDefault(), FindIndex, search engine...
if (found < 0)//not found
{
var i = input.ToArray();
var target = "";
//most similar
//target = people.OrderByDescending(p => p.ToArray().Intersect(i).Count()).FirstOrDefault();
//as you code:
foreach (var p in people)
{
var count = p.ToArray().Intersect(i).Count();
if (count > input.Length / 2)
{
target = p;
break;
}
}
if (!string.IsNullOrWhiteSpace(target))
{
Console.WriteLine(target);
}
}

Related

How to skip over characters already looped through in a string

I have a function findChar() that loops through a string to find occurrences of characters in a string Ex: "Cincinnati" (2 Cs, 2 i's, etc) but once it finds another 'C' or 'i' it will return the values again
public static int findChar(string name, char c)
{
int count = 0;
for (int i = 0; i < name.Length; i++)
{
if (name[i] == c || name[i] == Char.ToUpper(c) || name[i] == Char.ToLower(c))
{
count++;
}
}
return count;
}
static void Main(string[] args)
{
string name = "Cincinnati";
char c = ' ' ;
int count = 0;
for (int i = 0; i < name.Length; i++)
{
c = name[i];
count = findChar(name, c);
Console.WriteLine(count);
}
}
My Output looks like this:
2
3
3
2
3
3
3
1
1
3
And I need it to be like:
2
3
3
1
1
Option 1: keep track of the letters you already processed, and ignore it if you already did
Option 2: use System.Linq's GroupBy to get the count
public static void Main()
{
var name = "Cincinatti";
var letters = name.ToLower().GroupBy(letter => letter);
foreach (var group in letters) {
Console.WriteLine(group.Count());
}
}
There are many ways to solve a problem like this. First let's discuss a problem it looks like you've already run into, capitalization. Lower case and upper case versions of the same letter are classified as different characters. The easiest way to combat this is to either convert the string to lowercase or uppercase so each duplicate letter can also be classified as a duplicate character. You can do this either by using the String.ToLower() method or the String.ToUpper() method depending on which one you want to use.
The way to solve this that is the closest to what you have is to just create a list, add letters to it as you process them, then use the list to check what letters you've processed already. It would look something like this:
static void Main(string[] args)
{
string name = "Cincinnati";
char c = ' ' ;
int count = 0;
var countedLetters = new List<string>();
for (int i = 0; i < name.Length; i++)
{
c = name[i];
char cLower = char.ToLower(c);
if(countedLetters.Contains(cLower))
{
continue;
}
countedLetters.Add(cLower);
count = findChar(name, c);
Console.WriteLine(count);
}
}
Although, you can usually use System.Linq's Enumerable extension methods to do things like this pretty easily.
Not deviating too much from what you have, another solution using System.Linq would be to just get the distinct characters and loop through that instead of the entire string. When doing this, we need to convert the entire string to either upper or lower case in order for linq to return the expected result. This would like something like this:
static void Main(string[] args)
{
string name = "Cincinnati";
string nameLower = name.ToLower();
int count = 0;
foreach(char c in nameLower.Distinct())
{
count = findChar(name, c);
Console.WriteLine(count);
}
}
Then finally, you can simplify this a ton by leaning heavily into the linq route. GroupBy is very useful for this because it's entire purpose is to group duplicates together. There are many ways to implement this and two have already be provided, so I will just provide a third.
public static void Main()
{
string name = "Cincinatti";
int[] counts = name.ToLower()
.GroupBy(letter => letter)
.Select(group => group.Count())
.ToArray();
Console.WriteLine(string.Join("\n", counts));
}
you can do group by list, sort optional (i left it commented out) and then select count
var word="Cincinnati";
var groups = word.ToLower().GroupBy(n => {return n;})
.Select(n => new
{
CharachterName = n.Key,
CharachterCount = n.Count()
});
// .OrderBy(n => n.CharachterName);
Console.WriteLine(JsonConvert.SerializeObject(groups.Select(i=>i.CharachterCount)));

Converting random letters within a string to Upper/Lower Case

I have a bot for my discord that I am adding a command to post one of those spongebob memes that you may have seen on twitter recently. I basically have to convert a string that the user enters, for example This is the string they would enter and it would convert it to something like this THis iS ThE sTRinG thEy WOulD EnTEr
I need to basically randomly set each character in that string to and uppercase or a lowercase.
Here is what I have so for, it prints out the original string and not the converted one.
commands.CreateCommand("spongememe").Parameter("message", ParameterType.Multiple).Do(async (e) =>
{
string message = "";
for (int i = 0; i < e.Args.Length; i++)
{
message += e.Args[i].ToString() + " ";
}
char[] array = message.ToCharArray();
for(int i = 0; i < array.Length; i++)
{
if (rnd.Next(0, 2) == 1)
Char.ToUpper(array[i]);
else
{
Char.ToLower(array[i]);
}
}
string newMessage = String.Join("", array);
await e.Channel.SendMessage(newMessage);
});
Any help on how to randomly select which characters are set to upper and lower case would be appreciated.
Here is how to randomly uppercase letters from a sentence:
var someString = "This is the string they would enter";
var randomizer = new Random();
var final =
someString.Select(x => randomizer.Next() % 2 == 0 ?
(char.IsUpper(x) ? x.ToString().ToLower().First() : x.ToString().ToUpper().First()) : x);
var randomUpperLower = new string(final.ToArray());
Console.WriteLine(randomUpperLower);
<== Try Me ==>
Char.ToUpper(char c); and Char.ToLower(char c); takes the char argument, transforms it either to uppercase or lowercase, and returns the transformed result. It doesn't change the char itself (see "Value and Reference Types" https://msdn.microsoft.com/en-us/library/4d43ts61(v=vs.90).aspx).
You need to modify it to something like this: array[i] = Char.ToUpper(array[i]);

C# how can I check users input

I´m having a string with allowed chars. I´d like that user is only able to use this chars.
My idea was to loop through the unser inputs string and compare char for char. But the problem which I have is when the first char in string allowed is "A" and the first in the users input "B" is, it gives me an error...
Totally confused right now
string allowed = "abc";
string read= Console.ReadLine();
for (int i = 0; i < allowed.Length; i++ )
{
if (allowed[i] == read[i])
{
Console.WriteLine("Okay");
}
else
{
Console.WriteLine("Invalid char on" +index);
}
}
If you wanna check if the user input has any of not allowed characters you need a nested loop, because you wanna compare each char in the user input against the chars in the allowed:
foreach(var r in read)
{
bool isValid = false;
foreach(var c in allowed)
{
// if we found a valid char set isValid to true
if(c == r)
isValid = true;
}
// if it's still false then the current char
// doesn't match any of the allowed chars
// so it's invalid
if(!isValid)
{
Console.WriteLine("the string has invalid char(s)");
break;
}
}
Or, to simplify this you can use LINQ:
bool isInvalid = read.Any(c => !allowed.Contains(c));
If you want to know which chars are invalid, you can use Except method:
var invalidChars = read.Except(allowed);
foreach(var c in invalidChars)
{
Console.WriteLine(c);
}
You either need to search the char of user input within the allowed characters or you could use a regular expression.
Search approach:
private string allowed = "abc";
private string userInput = "some string entered";
bool stringIsValid = false;
for (int i = 0; i < userInput.Length; i++)
{
if (!allowed.IndexOf(userInput[i]))
{
stringIsValid = false;
break; // You can stop the loop upon the first occurance of an invalid char
}
}
Regular expression approach:
private string allowed = "abc";
private string userInput = "some string entered";
bool stringIsValid = Regex.IsMatch(allowed, userInput);
Please note that the regular expression approach is more flexible. If you learn about regular expressions, you will find it very powerful.
You need another loop in your first one:
string allowed = "abc";
string read= Console.ReadLine();
for (int i = 0; i < read.Length; i++ )
{
bool isValid = false;
for (int j = 0; j < allowed.Length; j++)
{
if (read[i] == allowed[j])
{
isValid = true;
break;
}
}
if (isValid)
{
Console.WriteLine("Okay");
}else{
Console.WriteLine("Invalid char on" +index);
}
}
Right now, what you're saying is "every character in read must be exactly the same as in allowed".
What you're trying to say (I think) is "every character in read must be present somewhere in allowed" – that's what the second loop does. It looks for the character in allowed and if it finds it, sets isValid to true. Otherwise the character wasn't found and it's incorrect.
As other answers here state, you can use LINQ or (preferrably) regular expressions (regex) for things like this. I assume this is homework, or you're new to C# or programming, so I provided a basic answer to (hopefully) help you understand what's not working currently with your code.
If this should indeed be a homerwok or studying-related question, then let me recommend you put that in your question next time, for it's not forbidden to ask about homework.
The "real world" solutions we would use are of no help to you if you're trying to figure out the basics, so if we know it's about learning stuff then we'll provide answers that are more useful for you.
When using a collection to store not allowed items (instead of a plain string) it opens a whole new spectrum of LINQ expressions you can use:
public static void Main(string[] args)
{
var allowed = new List<string> { "a", "b", "c" };
var read = Console.ReadLine().Select(c => c.ToString()).ToList();
if (read.All(allowed.Contains))
{
Console.WriteLine("Okay");
}
else
{
var firstNotAllowed = read.First(a => !allowed.Contains(a));
var firstIndex = read.FindIndex(a => !allowed.Contains(a));
Console.WriteLine("Invalid char: {0}, at index: {1}", firstNotAllowed, firstIndex);
}
}

Regex to find first capital letter occurrence in a string

I want to find the index of first capital letter occurrence in a string.
E.g. -
String x = "soHaM";
Index should return 2 for this string. The regex should ignore all other capital letters after the first one is found. If there are no capital letters found then it should return 0. Please help.
I'm pretty sure all you need is the regex A-Z \p{Lu}:
public static class Find
{
// Apparently the regex below works for non-ASCII uppercase
// characters (so, better than A-Z).
static readonly Regex CapitalLetter = new Regex(#"\p{Lu}");
public static int FirstCapitalLetter(string input)
{
Match match = CapitalLetter.Match(input);
// I would go with -1 here, personally.
return match.Success ? match.Index : 0;
}
}
Did you try this?
Just for fun, a LINQ solution:
string x = "soHaM";
var index = from ch in x.ToArray()
where Char.IsUpper(ch)
select x.IndexOf(ch);
This returns IEnumerable<Int32>. If you want the index of the first upper case character, simply call index.First() or retrieve only the first instance in the LINQ:
string x = "soHaM";
var index = (from ch in x.ToArray()
where Char.IsUpper(ch)
select x.IndexOf(ch)).First();
EDIT
As suggested in the comments, here is another LINQ method (possibly more performant than my initial suggestion):
string x = "soHaM";
x.Select((c, index) => new { Char = c, Index = index }).First(c => Char.IsUpper(c.Char)).Index;
No need for Regex:
int firstUpper = -1;
for(int i = 0; i < x.Length; i++)
{
if(Char.IsUpper(x[i]))
{
firstUpper = i;
break;
}
}
http://msdn.microsoft.com/en-us/library/system.char.isupper.aspx
For the sake of completeness, here's my LINQ approach(although it's not the right tool here even if OP could use it):
int firstUpperCharIndex = -1;
var upperChars = x.Select((c, index) => new { Char = c, Index = index })
.Where(c => Char.IsUpper(c.Char));
if(upperChars.Any())
firstUpperCharIndex = upperChars.First().Index;
First your logic fails, if the method returns 0 in your case it would mean the first char in that list was in upperCase, so I would recomend that -1 meens not found, or throw a exception.
Anyway just use regular expressions becasue you can is not always the best choise, plus they are pretty slow and hard to read in general, making yoru code much harder to work with.
Anyway here is my contribution
public static int FindFirstUpper(string text)
{
for (int i = 0; i < text.Length; i++)
if (Char.IsUpper(text[i]))
return i;
return -1;
}
Using Linq:
using System.Linq;
string word = "soHaMH";
var capChars = word.Where(c => char.IsUpper(c)).Select(c => c);
char capChar = capChars.FirstOrDefault();
int index = word.IndexOf(capChar);
Using C#:
using System.Text.RegularExpressions;
string word = "soHaMH";
Match match= Regex.Match(word, "[A-Z]");
index = word.IndexOf(match.ToString());
Using loop
int i = 0;
for(i = 0; i < mystring.Length; i++)
{
if(Char.IsUpper(mystring, i))
break;
}
i is the value u should be looking at;

Find the exact occurence of a string in HTML file

I would like to find the count of Exact match of string
Let suppose string is 'My Computer'. I want to find it,s occurrence in string
This is My computer,this is a good Computer,this is my Computer,this is my Computers
So at end I shall get Count 2 ,
I have tried the following formula with 'mykeyWord' as string to be found.
int strength = (innerDocument.DocumentNode.InnerText.Length - innerDocument.DocumentNode.InnerText.ToLower().Replace(mykeyWord.ToLower(), "").Length) / mykeyWord.Length;
But it will also count strings like 'my Computers' that is wrong.
This is a perfect place to use regular expressions, just as you tagged your post:
Regex re = new Regex("\\b" + Regex.Escape(mykeyWord) + "\\b", RegexOptions.IgnoreCase);
int count = re.Matches(innerDocument.DocumentNode.InnerText).Count;
You could use the regular expression [^A-z](my computer)[^A-z] This matches 'my computer' but not if it's before or after 'A to Z'. To make the regex search case insensitive use RegexOptions.IgnoreCase.
Edit
minitech's answer using word boundaries is better.
int FindCount(string keyword, string input)
{
if (input.Contains(keyword))
{
int count = 0;
int i = 0;
foreach (var c in input)
{
if (c == keyword[i])
i++;
else
i = 0;
if (i == keyword.Length)
{
i = 0;
count++;
}
}
return count;
}
return 0;
}

Categories

Resources