Separate words from a string in C# - c#

So i'm working on a program for a university degree. First requirement was to show the number of times each letter of the alphabet appears in a string. Now to develop this program further i would like to show all the words that are in the string, in a list. Here is the current code that i have.
public void occurances()
{
string sentence;
Console.WriteLine("\n");
Console.WriteLine("Please enter a random sentence and press enter");
Console.WriteLine("\n");
var occurances = new Dictionary<char, int>();
var words = occurances;
//a for each loop, and within it, the char variable is a assigned named "characters"
//The value "characters" will represent all the characters in the sentence string.
sentence = Console.ReadLine();
foreach (char characters in sentence)
{
//if the sentence contains characters
if (occurances.ContainsKey(characters))
//add 1 to the value of occurances
occurances[characters] = occurances[characters] + 1;
//otherwise keep the occurnaces value as 1
else
occurances[characters] = 1;
}
foreach (var entry in occurances)
{
//write onto the screen in position 0 and 1, where 0 will contain the entry key
// and 1 will contain the amount of times the entry has been entered
Console.WriteLine("{0}: {1}", entry.Key, entry.Value);
}
//Pause
Console.ReadLine();
}

For 1st Requirement:
var charGroups = sentence.GroupBy(x => x).OrderByDescending(x => x.Count());
For 2nd Requirement:
How to: Count Occurrences of a Word in a String (LINQ)

I thinks the easiest way would be this:
var WordList = YourString.Split(' ').toList(); // Making the list of words
var CharArray = YourString.toCharArray(); // Counting letters
var q = from x in CharArray
group x by x into g
let count = g.Count()
orderby count descending
select new {Value = g.Key, Count = count};

Related

C# looping through a list to find character counts

I'm trying to loop through a string to find the character, ASCII value, and the number of times the character occurs. So far, I have found each unique character and ASCII value using foreach statements, and finding if the value was already in the list, then don't add it, otherwise add it. However I'm struggling with the count portion. I was thinking the logic would be "if I am already in the list, don't count me again, however, increment my frequency"
I've tried a few different things, such as trying to find the index of the character it found and adding to that specific index, but i'm lost.
string String = "hello my name is lauren";
char[] String1 = String.ToCharArray();
// int [] frequency = new int[String1.Length]; //array of frequency counter
int length = 0;
List<char> letters = new List<char>();
List<int> ascii = new List<int>();
List<int> frequency = new List<int>();
foreach (int ASCII in String1)
{
bool exists = ascii.Contains(ASCII);
if (exists)
{
//add to frequency at same index
//ascii.Insert(1, ascii);
//get { ASCII[index]; }
}
else
{
ascii.Add(ASCII);
//add to frequency at new index
}
}
foreach (char letter in String1)
{
bool exists = letters.Contains(letter);
if (exists)
{
//add to frequency at same index
}
else
{
letters.Add(letter);
//add to frequency at new index
}
}
length = letters.Count;
for (int j = 0; j<length; ++j)
{
Console.WriteLine($"{letters[j].ToString(),3} {"(" + ascii[j] + ")"}\t");
}
Console.ReadLine();
}
}
}
I'm not sure if I understand your question but that what you are looking for may be Dictionary<T,T> instead of List<T>. Here are examples of solutions to problems i think you trying to solve.
Counting frequency of characters appearance
Dictionary<int, int> frequency = new Dictionary<int, int>();
foreach (int j in String)
{
if (frequency.ContainsKey(j))
{
frequency[j] += 1;
}
else
{
frequency.Add(j, 1);
}
}
Method to link characters to their ASCII
Dictionary<char, int> ASCIIofCharacters = new Dictionary<char, int>();
foreach (char i in String)
{
if (ASCIIofCharacters.ContainsKey(i))
{
}
else
{
ASCIIofCharacters.Add(i, (int)i);
}
}
A simple LINQ approach is to do this:
string String = "hello my name is lauren";
var results =
String
.GroupBy(x => x)
.Select(x => new { character = x.Key, ascii = (int)x.Key, frequency = x.Count() })
.ToArray();
That gives me:
If I understood your question, you want to map each char in the provided string to the count of times it appears in the string, right?
If that is the case, there are tons of ways to do that, and you also need to choose in which data structure you want to store the result.
Assuming you want to use linq and store the result in a Dictionary<char, int>, you could do something like this:
static IDictionary<char, int> getAsciiAndFrequencies(string str) {
return (
from c in str
group c by Convert.ToChar(c)
).ToDictionary(c => c.Key, c => c.Count());
}
And use if like this:
var f = getAsciiAndFrequencies("hello my name is lauren");
// result: { h: 1, e: 3, l: 3, o: 1, ... }
You are creating a histogram. But you should not use List.Contains as it gets ineffective as the list grows. You have to go through the list one item after another. Better use Dictionary which is based on hashing and you go directly to the item. The code may look like this
string str = "hello my name is lauren";
var dict = new Dictionary<char, int>();
foreach (char c in str)
{
dict.TryGetValue(c, out int count);
dict[c] = ++count;
}
foreach (var pair in dict.OrderBy(r => r.Key))
{
Console.WriteLine(pair.Value + "x " + pair.Key + " (" + (int)pair.Key + ")");
}
which gives
4x (32)
2x a (97)
3x e (101)
1x h (104)
1x i (105)
3x l (108)
2x m (109)
2x n (110)
1x o (111)
1x r (114)
1x s (115)
1x u (117)
1x y (121)

How to return the amount of duplicate letters in a string

I am trying to get a user's input so that I can return how many duplicate characters they have.
This is how I got the input
Console.WriteLine("Input a word to reveal duplicate letters");
string input = Console.ReadLine();
For example, the code should return something like:
List of duplicate characters in String 'Programming'
g : 2
r : 2
m : 2
How do I find duplicate letters and count them in a string?
Yes you can obtain this by using System.Linq GroupBy(), you going to group your string by character value and after filter the generated groups that have more than 1 values like so :
var word = "Hello World!";
var multipleChars = word.GroupBy(c => c).Where(group => group.Count() > 1);
foreach (var charGroup in multipleChars)
{
Console.WriteLine(charGroup .Key + " : " + charGroup .Count());
}
this will include case sensitivity as well as excluding non alphanumeric char
var sample = Console.ReadLine();
var letterCounter = sample.Where(char.IsLetterOrDigit)
.GroupBy(char.ToLower)
.Select(counter => new { Letter = counter.Key, Counter = counter.Count() })
.Where(c=>c.Counter>1);
foreach (var counter in letterCounter){
Console.WriteLine(String.Format("{0} = {1}", counter.Letter,counter.Counter));
}

Replacing data from a list with its position

I have made some code that allows the user to enter a sentence into a richtextbox, and then the data will be saved into a list and have the duplicates removed.
What I want to know is how I would make the overwrite the words in the list with their positions, and then replace the positions of the original sentence with those positions.
E.g: in the sentence Hello this is a test I hope this test works the sentence will be saved, removed of duplicates, and output hello, this, is, a, test, I, hope, works, the code replaces this with 1 2 3 4 5 6 7 8 (I think).
Now I need to make the program replace the actual words in the list with its position in the original so it will finally say 1 2 3 4 5 6 7 2 5 8 separated by commas.
This is my code:
string sentence = richTextBox1.Text;
list = sentence.Split(delimiterChars).ToList();
listoriginal = sentence.Split(delimiterChars).ToList();
listBox1.Items.Add("Full sentence: " + String.Join(" ", list));
list = list.Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();
listBox1.Items.Add("Words in the input: " + String.Join(", ", list));
for (int i = 0; i < list.Count; i++)
{
list[i] = list[i].ToString();
listoriginal[i] = listoriginal[i].ToString();
resultList = listoriginal.Select(x => x.Replace(listoriginal[i], list[i])).ToList();
i++;
}
listBox1.Items.Add("Final result: " + String.Join(", ", resultList));
Create a dictionary with the words that have already been addressed. Meaning that you would have the following:
Dictionary<int, string> words = new Dictionary<int, string>();
words.Items.Add(//number, //word);
(Hello, 1)
(this, 2)
(is, 3)
(a, 4)
.....and so on
You can then write a a method that will search to see if a word is already stored, for example:
foreach(KeyValuePair<int, string> set in words)
{
if(set.value == //whatever word is next)
{
//write the number in the dictionary here
//the corresponding number can be grabbed using: set.key
}
}
Basically you need to populate on the fly a Dictionary<string, int> holding the result position of the word. The result words then can be obtained from Keys property:
var originalWords = sentence.Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries);
var uniqueWordPositions = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
var originalWordPositions = new List<int>();
foreach (var word in originalWords)
{
int position;
if (!uniqueWordPositions.TryGetValue(word, out position))
uniqueWordPositions.Add(word, position = uniqueWordPositions.Count + 1);
originalWordPositions.Add(position);
};
listBox1.Items.Add("Full sentence: " + string.Join(" ", originalWords));
listBox1.Items.Add("Words in the input: " + string.Join(", ", uniqueWordPositions.Keys));
listBox1.Items.Add("Final result: " + string.Join(", ", originalWordPositions));
The easiest way is probably to use a key/value store like a dictionary - once you have eliminated the duplicates you can push the key/values into the dictionary and use that to re-build the sentence.
// Create dictionary
var dict = new Dictionary<string, int>();
// When looping through the words to determine index add each word to the dict - the word being the key and the value being the index
dict.Add(word, index);
Then
foreach(var word in words) {
// Get the value from the dictionary for the associated key (the key being the word)
var index = dict[word];
// do stuff with index
}
You can use string concatenation to re-build the string rather than replace (since you are indexing all words).
Common advice will be to use StringBuilder when you are concerned about performance/memory since strings are immutable
var sb = new StringBuilder();
foreach(var word in words) {
sb.Append(dict[word]);
sb.Append(" ");
}
sb.ToString();
Edit:
Here's a more full example...
var sentence = "hello world this is a test hello world";
var words = sentence.Split(' ');
var distinctWords = words.Distinct(StringComparer.InvariantCultureIgnoreCase);
var dict = new Dictionary<string, int>();
var ix = 0;
foreach (var word in distinctWords)
{
dict.Add(word.ToLower(), ix++);
}
var sb = new StringBuilder();
foreach (var word in words)
{
sb.Append(dict[word.ToLower()]);
sb.Append(" ");
}
// sb.ToString();
// 0 1 2 3 4 5 0 1
Obviously since I'm not doing a replace, this may affect the formatting of the original string but it gives you an idea. You can use replace but it will be a lot slower - but it depends on the length of string and how much processing you are doing.

Ignoring case sensitivity

I have to count how many times each word from given input text appears in it.
And the thing where I'm stuck: The character casing differences should be ignored.
For example: "You are here.You you" -> the output :
are=1
here=1
You=3
What I've done:
string text = "You are here.You you";
IDictionary<string, int> wordsCount = new SortedDictionary<string, int>();
string[] words = text.Split(' ',',','.','-','!');
foreach (string word in words)
{
int count = 1;
if (wordsCount.ContainsKey(word))
count = wordsCount[word] + 1;
wordsCount[word] = count;
}
var items = from pair in wordsCount
orderby pair.Value ascending
select pair;
foreach (var p in items)
{
Console.WriteLine("{0} -> {1}", p.Key, p.Value);
}
There is a chance to make this possible without checking manually every word from the given text? For example if I have a very long paragraph to not check every word using the specific method?
Just add
for(i = 0; text[i] != '\0'; i++){
text[i] = text[i].ToLower();
}
But as text is a string, just do :
text = text.ToLower();
Just before the string[] words = text.Split(' ',',','.','-','!'); line.
And then enjoy !
How about linq?
var text = "You are here.You you";
var words = text.Split(' ', ',', '.', '-', '!');
words
.GroupBy(word => word.ToLowerInvariant())
.OrderByDescending(group => group.Count())
.ToList()
.ForEach(g=> Console.WriteLine(g.Key + "=" + g.Count()));

Cannot replace last element in string List

I have an input file that includes data on an entertainer and their performance score. For example,
1. Bill Monohan from North Town 10.54
2. Mary Greenberg from Ohio 3.87
3. Sean Hollen from Markell 7.22
I want to be able to take the last number from a line (their score), perform some math on it, and then replace the old score with the new score.
Here's a brief piece of code for what I'm trying to do:
string line;
StreamReader reader = new StreamReader(#"file.txt");
//Read each line and split by spaces into a List.
while ((line = reader.ReadLine())!= null){
//Find last item in List and convert to a Double in order to perform calculations.
List<string> l = new List<string>();
l = line.Split(null).ToList();
string lastItem = line.Split(null).Last();
Double newItem = Convert.ToDouble(lastItem);
/*Do some math*/
/*Replace lastItem with newItem*/
System.Console.WriteLine(line); }
When I write the new line, nothing changes but I want lastItem to be switched with newItem at the end of the line now. I've tried using:
l[l.Length - 1] = newItem.ToString();
But I'm getting no luck. I just need the best way to replace the last value of a string List like this. I've been going at this for a few hours now and I'm almost at the end of my rope.
Please help me c# masters!
You can use regular expression MatchEvaluator to get number from each line, do calculations, and replace original number with new one:
string line = "1. Bill Monohan from North Town 10.54";
line = Regex.Replace(line, #"(\d+\.?\d*)$", m => {
decimal value = Decimal.Parse(m.Groups[1].Value);
value = value * 2; // calculation
return value.ToString();
});
This regex captures decimal number at the end of input string. Output:
1. Bill Monohan from North Town 21.08
You're not changing anything to your line object before doing your WriteLine.
You will have to rebuild your line, something like this:
var items = string.Split();
items.Last() = "10";//Replace
var line = string.Join(" ", items)
Tip: strings are immutable, look it up.
This should work:
//var l = new List<string>(); // you don't need this
var l = line.Split(null).ToList();
var lastItem = l.Last(); // line.Split(null).Last(); don't split twice
var newItem = Convert.ToDouble(lastItem, CultureInfo.InvariantCulture);
/*Do some math*/
/*Replace lastItem with newItem*/
l[l.Count - 1] = newItem.ToString(); // change the last element
//Console.WriteLine(line); // line is the original string don't work
Console.WriteLine(string.Join(" ", l)); // create new string
This would probably do the job for you. A word on reading files though, if possible, ie they fit in memory, read the entire file at once, it gives you one disk access (well, depends on file size, but yeah) and you do not have to worry about filehandles.
// Read the stuff from the file, gets an string[]
var lines = File.ReadAllLines(#"file.txt");
foreach (var line in lines)
{
var splitLine = line.Split(' ');
var score = double.Parse(splitLine.Last(), CultureInfo.InvariantCulture);
// The math wizard is in town!
score = score + 3;
// Put it back
splitLine[splitLine.Count() - 1] = score.ToString();
// newLine is the new line, what should we do with it?
var newLine = string.Join(" ", splitLine);
// Lets print it cause we are out of ideas!
Console.WriteLine(newLine);
}
What do you want to do with the end result? Do you want it written back to file?
Try this
string subjectString = "Sean Hollen from Markell 7.22";
double Substring =double.Parse(subjectString.Substring(subjectString.IndexOf(Regex.Match(subjectString, #"\d+").Value), subjectString.Length - subjectString.IndexOf(Regex.Match(subjectString, #"\d+").Value)).ToString());
double NewVal = Substring * 10; // Or any of your operation
subjectString = subjectString.Replace(Substring.ToString(), NewVal.ToString());
Note: This will not work if the number appears twice on the same line
You are creating and initializing the list in a loop, hence it contains always only the current line. Do you want to find the highest score of all entertainers or the highest score of each entertainer (in case an entertainer could repeat in the file)?
However, here is an approach that gives you both:
var allWithScore = File.ReadAllLines(path)
.Select(l =>
{
var split = l.Split();
string entertainer = string.Join(" ", split.Skip(1).Take(split.Length - 2));
double score;
bool hasScore = double.TryParse(split.Last(), NumberStyles.Float, CultureInfo.InvariantCulture, out score);
return new { line = l, split, entertainer, hasScore, score };
})
.Where(x => x.hasScore);
// highest score of all:
double highestScore = allWithScore.Max(x => x.score);
// entertainer with highest score
var entertainerWithHighestScore = allWithScore
.OrderByDescending(x => x.score)
.GroupBy(x => x.entertainer)
.First();
foreach (var x in entertainerWithHighestScore)
Console.WriteLine("Entertainer:{0} Score:{1}", x.entertainer, x.score);
// all entertainer's highest scores:
var allEntertainersHighestScore = allWithScore
.GroupBy(x => x.entertainer)
.Select(g => g.OrderByDescending(x => x.score).First());
foreach (var x in allEntertainersHighestScore)
Console.WriteLine("Entertainer:{0} Score:{1}", x.entertainer, x.score);

Categories