Replacing data from a list with its position - c#

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.

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)

Combining Two Lists Based on first 6 Characters

I have two lists (Current & New). Current can contain 100,000+ strings each starting with a unique number. New can contain anywhere between 50 and 200 strings each with a unique number.
If New contains a string starting with the same 6 characters it should replace the same entry in Current. Any new entries that don't exist in Current but exist in New should be added to Current. I've considered Union, Concat and Intersect, but each only deal with the entire string.
Is there some way to compare just the first 6 characters of an item in a list and replace the entry in Current if found it exists in New?
Perhaps the easiest way to visualise the above is:
Current
123456 66 Park Avenue Sydney
New
123456 88 River Road Sydney
The result in Current needs to be
123456 88 Park Avenue Sydney
If Current.Union(New, first X characters) was possible it would be perfect.
Any suggestions on Combine the two lists without duplicates based on the first 6 characters would be greatly appreciated.
string.StartsWith is what you are looking for.
This should do it. Note I have used two dictionaries because there might be duplicates to be replaced, if not, you can use one.
public static void Coder42(List<string> current, IEnumerable<string> news)
{
Dictionary<string, string> newDict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, string> unfound = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var n in news)
{
if (n.Length < 6) throw new Exception("Too short");
var ss = n.Substring(0, 6);
if (newDict.ContainsKey(ss)) throw new Exception("Can't be too new.");
newDict[ss] = n;
unfound[ss] = n;
}
for (int i = 0; i < current.Count; i++)
{
var s = current[i];
if (s.Length >= 6)
{
var ss = s.Substring(0, 6);
if (newDict.TryGetValue(ss, out string replacement))
{
current[i] = replacement;
unfound.Remove(ss);
}
}
}
foreach(var pair in unfound)
current.Add(pair.Value);
}
And tested using:
var current = new List<string>();
current.Add("123456 a");
current.Add("123457 b");
current.Add("123458 c");
var news = new List<string>();
news.Add("123457 q");
news.Add("123456 p");
news.Add("123459 z");
Coder42(current, news);
foreach (var s in current) Console.WriteLine(s);
Console.ReadLine();
Gives:
123456 p
123457 q
123458 c
123459 z

Separate words from a string in 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};

Occurence of elements in the file with c# and Dictionary

I have a file as
outlook temperature Humidity Windy PlayTennis
sunny hot high false N
sunny hot high true N
overcast hot high false P
rain mild high false P
rain cool normal false P
rain cool normal true N
I want to find occurence of each element e.g
sunny: 2
rain: 3
overcast:1
hot: 3
and so on
My code is:
string file = openFileDialog1.FileName;
var text1 = File.ReadAllLines(file);
StringBuilder str = new StringBuilder();
string[] lines = File.ReadAllLines(file);
string[] nonempty=lines.Where(s => s.Trim(' ')!="")
.Select(s => Regex.Replace(s, #"\s+", " ")).ToArray();
string[] colheader = null;
if (nonempty.Length > 0)
colheader = nonempty[0].Split();
else
return;
var linevalue = nonempty.Skip(1).Select(l => l.Split());
int colcount = colheader.Length;
Dictionary<string, string> colvalue = new Dictionary<string, string>();
for (int i = 0; i < colcount; i++)
{
int k = 0;
foreach (string[] values in linevalue)
{
if(! colvalue.ContainsKey(values[i]))
{
colvalue.Add(values[i],colheader[i]);
}
label2.Text = label2.Text + k.ToString();
}
}
foreach (KeyValuePair<string, string> pair in colvalue)
{
label1.Text += pair.Key+ "\n";
}
Output I get here is
sunny
overcast
rain
hot
mild
cool
N
P
true
false
I also want to find the occurence, which I am unable to get. Can u please help me out here.
This LINQ query will return Dictionary<string, int> which will contain each word in file as key, and word's occurrences as value:
var occurences = File.ReadAllLines(file).Skip(1) // skip titles line
.SelectMany(l => l.Split(new []{' '}, StringSplitOptions.RemoveEmptyEntries))
.GroupBy(w => w)
.ToDictionary(g => g.Key, g => g.Count());
Usage of dictionary:
int sunnyOccurences = occurences["sunny"];
foreach(var pair in occurences)
label1.Text += String.Format("{0}: {1}\n", pair.Key, pair.Value);
Seems to me like you are implementing a simple Tag Cloud. I have used non-generic collection but you can replace it with generic. Replace the HashTable with Dictionary
Follow this code:
Hashtable tagCloud = new Hashtable();
ArrayList frequency = new ArrayList();
Read from a file and store it as array
string[] lines = File.ReadAllLines("file.txt");
//use the specific delimiter
char[] delimiter = new char[] { ' ' };
StringBuilder buffer = new StringBuilder();
foreach (string line in lines)
{
if (line.ToString().Length != 0)
{
buffer.Append((" " + line.Trim()));
}
}
string[] words = buffer.ToString().Trim().Split(delimiter);
Storing occurrence of each word.
List<string> listOfWords = new List<string>(words);
foreach (string i in listOfWords)
{
int c = 0;
foreach (string j in words)
{
if (i.Equals(j))
c++;
}
frequency.Add(c);
}
Store as key value pair. Value will be word and key will be its occurrence
for (int i = 0; i < listOfWords.Count; i++)
{
//use dictionary here
tagCloud.Add(listOfWords[i], (int)frequency[i]);
}
If all you want is the keyword and a count of how many times they appear in the file, then lazyberezovsky's solution is about as elegant of a solution as you will find. But if you need to do any other metrics on the file's data, then I would load the file into a collection that keeps your other metadata intact.
Something simple like:
var forecasts = File.ReadAllLines(file).Skip(1) // skip the header row
.Select(line => line.Split(new []{' '}, StringSplitOptions.RemoveEmptyEntries)) // split the line into an array of strings
.Select (f =>
new
{
Outlook = f[0],
Temperature = f[1],
Humidity = f[2],
Windy = f[3],
PlayTennis = f[4]
});
will give you an IEnumerable<> of an anonymous type that has properties that can be queried.
For example if you wanted to see how many times "sunny" occurred in the Outlook then you could just use LINQ to do this:
var count = forecasts.Count( f => f.Outlook == "sunny");
Or if you just wanted the list of all outlooks you could write:
var outlooks = forecasts.Select(f => f.Outlook).Distinct();
Where this is useful is when you want to do more complicated queries like "How many rainy cool days are there?
var count = forecasts.Count (f => f.Outlook == "rain" && f.Temperature == "cool");
Again if you just want all words and their occurrence count, then this is overkill.

C# - Stream Reader or whats the best way to do it?

I have a text file with something like this in it.
Tom 1 2
Jerry 3 4
using C#, I have populate this into two arrays
1st array = {Tom,Jerry} - 1 dim array
2nd array ={(1,2),(3,4)} - 2 dim array
Please help me with this. Any help would be appreciated.
Console.WriteLine("Enter the file name with extension:");
string filename = Console.ReadLine();
string s = System.IO.File.ReadAllText("C:/Desktop/" + filename);
Console.WriteLine("\n Text Details in the file: \n \n"+s);
I guess this is a follow up to the last question :)
More hw hints:
As I said in my last answer, splitting on tab (assuming each item is delimited by tab, which looks to be the case) will give you a 1D array of every item in a line (if you use ReadLine).
Item 1 in the ReadLine() array will be the name. Put that into your 1D names array.
Items 2 to N of ReadLine() array will be the test scores. Put that into your 2D scores array.
The first dimension of the scores array will be the student index. The second dimension will be the score array.
That may sound confusing, but if you think about it, a 2D array is an array of arrays.
So even though your data file doesn't show the student index, it's implied:
0 Joe 100 80 77
1 Bob 65 93 100
Names array will look like:
[0] Joe
[1] Bob
and scores array will look like:
[0][0] 100
[0][1] 80
[0][2] 77
[1][0] 65
[1][1] 93
[1][2] 100
Notice that the index (first dimension) in the scores array coincide with the index of the names array.
There are few ways, it depends how "elegant" you want to be, and / or whether Tom, Jerry is always going to be one word.
Parse every line with String methods
Parse every line with RegEx
Use Linq to Text
Simplest way would be something like this (quick and dirty, very fragile solution):
var path = "fileName.txt";
var names = new List<string>();
var values = new List<KeyValuePair<int, int>>();
using (var reader = File.OpenText(path))
{
string s = "";
while ((s = reader.ReadLine()) != null)
{
String[] arr = s.Split(' ');
names.Add(arr[0]);
values.Add(new KeyValuePair<int, int>(int.Parse(arr[1]), int.Parse(arr[2])));
}
}
If you need you can convert lists to array
A more complete version ;)
string filename = "";
do
{
Console.WriteLine("Enter the file name with extension:");
filename = Environment.GetEnvironmentVariable("HOMEDRIVE") + Environment.GetEnvironmentVariable("HOMEPATH") + "\\Desktop\\" + Console.ReadLine();
if (!System.IO.File.Exists(filename))
Console.WriteLine("File doesn't exist!");
else
break;
} while (true);
System.IO.StreamReader readfile = new System.IO.StreamReader(filename);
List<string> Names = new List<string>();
List<int[]> Numbers = new List<int[]>();
string val = "";
while ((val = readfile.ReadLine()) != null)
{
if (val == string.Empty)
continue;
List<string> parts = val.Split(' ').ToList<string>();
Names.Add(parts[0]);
parts.RemoveAt(0);
Numbers.Add(parts.ConvertAll<int>(delegate(string i) { return int.Parse(i); }).ToArray());
}
readfile.Close();
//Print out info
foreach (string name in Names)
{
Console.Write(name + ", ");
}
Console.WriteLine();
foreach (int[] Numberset in Numbers)
{
Console.Write("{");
foreach (int number in Numberset)
Console.Write(number + ", ");
Console.Write("} ");
}
Console.ReadLine();
I like a functional approach.
// var fileContent = System.IO.File.ReadAllText("somefilethathasthestuff");
var fileContent = #"Tom 1 2
Jerry 3 4";
var readData = fileContent.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(new { names = new List<string>(), data = new List<int[]>() },
(result, line) => {
var fields = line.Split(new []{' '}, 2);
result.names.Add(fields[0]);
result.data.Add(fields[1].Split(new[] { ' ' }).Select(n => int.Parse(n)).ToArray());
return result;
}
);
string[] firstarray = readData.names.ToArray();
int[][] secondarray = readData.data.ToArray();
This uses a jagged array for the numbers, but you can copy it to a 2d if that is what you really need. Better yet, don't copy to arrays at all. Use List < string> for names and List < int[] > for the numbers.

Categories