I'm writing a chat helper tool for a game with a custom library.
I want to change specific variables when player sends the message.
This is my code
static List<string> asciis = new List<string> { "shrug", "omg" };
static List<string> converteds = new List<string> { #"¯\_(ツ)_/¯", #"◕_◕"};
private static void Game_OnInput(GameInputEventArgs args)
{
newtext = args.Input;
foreach (var ascii in asciis)
{
foreach (var converted in converteds)
{
if (args.Input.Contains(ascii))
{
newtext = args.Input.Replace(ascii, converted);
Game.Say(newtext);
}
}
}
}
As you can see I'm trying to get the texts from "asciis" and convert them to "converteds" (in order).
Whenever I type something that not in "asciis" list it perfectly works. But whenever I type shrug it prints ¯\_(ツ)_/¯ + ◕_◕ + ◕_◕ (it prints omg 2 times). Same in omg too.
You probably understand that I'm really beginner. I really didn't understand what is wrong with this code...
It seems that your two lists have the same length (in terms of elements contained) and each element in one list has its replacement in the same position in the other list.
Then you could treat the two lists as two arrays and use a different way to search for the input term and replace it with the substitution text
private static void Game_OnInput(GameInputEventArgs args)
{
newtext = args.Input;
for(int x = 0; x < ascii.Count; x++)
if (args.Input.Contains(ascii[x]))
{
newtext = args.Input.Replace(ascii[x], converted[x]);
Game.Say(newtext);
}
}
While i don't think there is a big improvement, you could also implement the same with a dictionary
static Dictionary<string, string> converter = new Dictionary<string, string>()
{
{"shrug", #"¯\_(ツ)_/¯"},
{"omg", #"◕_◕"}
};
private static void Game_OnInput(GameInputEventArgs args)
{
newtext = args.Input;
foreach(KeyValuePair<string, string> kvp in converter)
if (args.Input.Contains(kvp.Key))
{
newtext = args.Input.Replace(kvp.Key, kvp.Value);
Game.Say(newtext);
}
}
Well, probably is a bit more readable, but still we need traversing the dictionary Keys one by one.
As Daniel pointed out in his comment, this is a good use case for dictionaries.
Have a dictionary that maps the text you want replaced to the stuff you want to be replaced with:
Dictionary<string, string> dict = new Dictionary<string, string>
{
{"shrug", #"¯\_(ツ)_/¯" },
{"omg", "◕_◕" }
}; // etc
Then find all occurrences of the keys from the dictionary and replace them with the corresponding values.
Also why are you using static methods and fields? I may be wrong, but I expect most, if not all of your other methods and fields are static as well. I strongly recommend avoiding getting used to them. Try learning more about OOP instead.
Your main problem is that you are always replacing on args.Input, but storing the results in newtext each time, overwriting your previous replacements. Your next problem is that you are outputting the result after each replacement attempt so that's why you are getting multiple weird output results.
I also suggest a dictionary since by definition, it is a mapping of one thing to another. Also, note my changes below, I have moved the Game.Say call outside of the loops and changed "args.Input.Replace" to "newtext.Replace"
Dictionary<string, string> dictionary = new Dictionary<string, string>
{
{"shrug", #"¯\_(ツ)_/¯" },
{"omg", "◕_◕" }
};
private static void Game_OnInput(GameInputEventArgs args)
{
string newtext = args.Input;
foreach(string key in dictionary.Keys){
newtext = newtext.Replace(key,dictionary[key]);
}
Game.Say(newtext);
}
Related
I generate a random string of 500 characters and want to check for words.
bliduuwfhbgphwhsyzjnlfyizbjfeeepsbpgplpbhaegyepqcjhhotovnzdtlracxrwggbcmjiglasjvmscvxwazmutqiwppzcjhijjbguxfnduuphhsoffaqwtmhmensqmyicnciaoczumjzyaaowbtwjqlpxuuqknxqvmnueknqcbvkkmildyvosczlbnlgumohosemnfkmndtiubfkminlriytmbtrzhwqmovrivxxojbpirqahatmydqgulammsnfgcvgfncqkpxhgikulsjynjrjypxwvlkvwvigvjvuydbjfizmbfbtjprxkmiqpfuyebllzezbxozkiidpplvqkqlgdlvjbfeticedwomxgawuphocisaejeonqehoipzsjgbfdatbzykkurrwwtajeajeornrhyoqadljfjyizzfluetynlrpoqojxxqmmbuaktjqghqmusjfvxkkyoewgyckpbmismwyfebaucsfueuwgio
I import a Dictionary Words txt file and check the string to see if it contains each word. If a match is found, it's added to a list.
I read using Dictionary<> is faster than Array for a words list.
When I use that method, I can see the cpu working the foreach loop in the debugger, and my loop counter goes up, about 10,000+ times in 10 seconds, but the loop continues on forever and does not return any results.
When I use Array for Dictionary, the program works, but slower at around 500 times in 10 seconds.
Not Working
Using Dictionary<>
// Random Message
public string message = Random(500);
// Dictionary Words Reference
public Dictionary<string, string> dictionary = new Dictionary<string, string>();
// Matches Found
public static List<string> matches = new List<string>();
public MainWindow()
{
InitializeComponent();
// Import Dictionary File
dictionary = File
.ReadLines(#"C:\dictionary.txt")
.Select((v, i) => new { Index = i, Value = v })
.GroupBy(p => p.Index / 2)
.ToDictionary(g => g.First().Value, g => g.Last().Value);
// If Message Contains word, add to Matches List
foreach (KeyValuePair<string, string> entry in dictionary)
{
if (message.Contains(entry.Value))
{
matches.Add(entry.Value);
}
}
}
Working
Using Array
// Random Message
public string message = Random(500);
// Dictionary Words Reference
public string[] dictionary = File.ReadAllLines(#"C:\dictionary.txt");
// Matches Found
public List<string> matches = new List<string>();
public MainWindow()
{
InitializeComponent();
// If Message Contains word, add to Matches List
foreach (var entry in dictionary)
{
if (message.Contains(entry))
{
matches.Add(entry);
}
}
}
I doubt if you want Dictionary<string, string> as a dictionary ;) HashSet<string> will be enough:
using System.Linq;
...
string source = "bliduuwfhbgphwhsyzjnlfyizbj";
HashSet<string> allWords = new HashSet<string>(File
.ReadLines(#"C:\dictionary.txt")
.Select(line => line.Trim())
.Where(line => !string.IsNullOrEmpty(line)), StringComparer.OrdinalIgnoreCase);
int shortestWord = allWords.Min(word => word.Length);
int longestWord = allWords.Max(word => word.Length);
// If you want duplicates, change HashSet<string> to List<string>
HashSet<string> wordsFound = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
for (int length = shortestWord; length <= longestWord; ++length) {
for (int position = 0; position <= source.Length - length; ++position) {
string extract = source.Substring(position, length);
if (allWords.Contains(extract))
wordsFound.Add(extract);
}
}
Test: for
https://raw.githubusercontent.com/dolph/dictionary/master/popular.txt
dictionary donwloaded as C:\dictionary.txt file
Console.WriteLine(string.Join(", ", wordsFound.OrderBy(x => x)));
we have output
id, li, lid
Using a Dictionary in this scenario doesn't make much sense. A Dictionary is, essentially, a list of variables that stores both the variable name and the variable value.
I could have the following:
int age = 21;
int money = 21343;
int distance = 10;
int year = 2017;
And convert it to a Dictionary instead, using the following:
Dictionary<string, int> numbers = new Dictionary<string, int>()
{
{ "age", 21 },
{ "money", 21343},
{ "distance", 10 },
{ "year", 2017 }
};
And then I can access a value in the dictionary using its key (the first value). So, for example, if I want to know what "age" is, I would use:
Console.Log(numbers["age"]);
This is only a single example of the power of dictionaries - there is a LOT more that they can do, and they can make your life a lot easier. In this scenario, however, they aren't going to do what you're expecting them to do. I would suggest just using the Array, or a List.
You are misusing the dictionary,
you are basically using the dictionary as a list, so it only added some overhead to the program. not helping in any way.
It would have been useful if you had something you want to query against the dictionary not the other way around.
Also, in any case, what you want is a HashSet, not a dictionary since your key in the dictionary is not the word you are querying against but an irrelevant int.
you can read more about dictionary and HashSet here:
dictionary: https://www.dotnetperls.com/dictionary
hashset: https://www.dotnetperls.com/hashset
In the below scenario how can I handle or implement collision in C# using the Hashtable class? If the 'Key' value is same I am getting an "Argument Exception".
static void Main(string[] args)
{
Console.Write("Enter a string:");
string input = Console.ReadLine();
checkString(input);
Console.ReadLine();
}
static void checkString(string input)
{
Hashtable hashTbl = new Hashtable();
foreach(char c in input)
{
hashTbl.Add(c.GetHashCode(), c);
}
printHash(hashTbl);
}
static void printHash(Hashtable hash)
{
foreach(int key in hash.Keys)
{
Console.WriteLine("Key: {0} Value: {1}",key,hash[key]);
}
}
My Expectation:
What do I need to do in the 'Value' argument to get around the 'Collision' issue. I am trying to check if the string consists of unique characters.
It seems you are misunderstanding how the Hashtable class works (and it has been deprecated since 2005 - use Dictionary<K,V> instead, but its behavior here is identical).
It seems you're expecting it to be your job to get an object's hashcode and add it to the hashtable. It isn't. All you need to do is add the object you want to use as key (each character), and the internal implementation will extract the hashcode.
However, what you're actually doing won't work even if you added the key object yourself. You're taking an input string (say, "test"), and for each character, you're adding it to the hashtable as a key. But since keys are, by definition, unique, you'll be adding the character 't' twice (it shows up twice in the input), so you'll get an exception.
I am trying to check if the string consists of unique characters.
Then you need keys only without values, that's what HashSet<T> is for.
var chars = new HashSet<char>();
foreach (char c in input)
{
if (chars.Contains(c))
{
// c is not unique
}
else
{
chars.Add(c);
}
}
But I'd prefer usin LINQ in this case:
var hasUniqueChars = input.Length == input.Distinct().Count();
As previously stated you should probably switch to the Dictionary<TKey, TValue> class for this.
If you want to get around the collission issue, then you have to check the key for existence.
Dictionary<string, object> dictValues = new Dictionary<string, object>();
Then you can use check for collission:
if (dictValues.ContainsKey(YourKey))
{
/* ... your collission handling here ... */
}
else
{
// No collission
}
Another possibility would be, if you are not interested in preserving previous values for the same key:
dictValues[YourKey] = YourValue;
This will add the key entry if it is not there already. If it is, it will overwrite its value with the given input.
I wasn't 100% sure how to explain this in the title or how to search it up, so sorry if this has been post before. What I'm trying to do is read and write a file, for example I would do:
WriteToFile(key + "=" + value);
And in the text file it would say something like:
AKey=AValue
I was able to get that fairly easily but I was wonder how exactly would I get code to find that 'key' and return its value. For example:
int integer = GetValue("AKey");
And the method GetValue would find the 'key', "AKey", and if it existed in the file, return the value.
For the sake of complete-ness, I'll expand on my comment, here is some example code of how you would accomplish this:
Dictionary<string, string> _keys = new Dictionary<string, string>();
private void ReadInKeyFile(string keyFileName)
{
_keys.Clear();
string[] lines = System.IO.File.ReadAllLines(keyFileName);
foreach (string line in lines)
{
string[] keyval = line.Split('=');
_keys.Add(keyval[0], keyval[1]);
}
}
private string GetValue(string key)
{
string retVal = string.Empty;
_keys.TryGetValue(key, out retVal);
return retVal;
}
The Dictionary<string, string> holds the key/value pairs that are read in from the file, so when you want to refresh it, you would call the ReadInKeyFile function with the file name. You can then use GetValue to get the value for a particular key (or String.Empty if the key is not found).
There are some obvious checks that I'm missing in the code above (file doesn't exist, line doesn't contain a =, etc), but it gets you 90% of the way there.
Edit
Here is some extensions to that for adding new keys and writing it out to a file:
private void SaveKeysToFile(string keyFileName)
{
StringBuilder sb = new StringBuilder();
_keys.ToList().ForEach(kvp =>
sb.AppendLine(string.Format("{0}={1}", kvp.Key, kvp.Value)));
System.IO.File.WriteAllText(keyFileName, sb.ToString());
}
private void AddValue(string key, string value)
{
_keys[key] = value;
}
The SaveToFile method converts the dictionary to the file format you use, and the AddValue adds a key and value (or changes the existing key if it already exists).
So I've found myself writing code along these lines lately.
Dictionary<string, byte> dict = new Dictionary<string, byte>();
foreach(string str in arbitraryStringCollection)
{
if(!dict.ContainsKey(str))
{
ProcessString(str);
dict[str] = 0;
}
}
The example is overly generic, but the common goal I find myself shooting for is "Have I done this one already?".
I like using Dictionary for the fast key lookup, but since I never care about the value field, I can't help but feel it's slightly excessive, even if it's just a byte per entry.
Is there a better .NET tool out there that accomplishes this, something with the key lookup speed of a Dictionary but without the arbitrary and unnecessary values?
You should use HashSet<T>
HashSet<string> hashSet= new HashSet<string>();
foreach(string str in arbitraryStringCollection)
{
if(!hashSet.Contains(str))
{
ProcessString(str);
hashSet.Add(str);
}
}
To make it shorter:
foreach(string str in arbitraryStringCollection)
{
if(hashSet.Add(str)) ProcessString(str);
}
There isn't a tool or library for that, however you can refactor this code to be less verbose. For example, the code as is could be simplified using the Distinct method.
foreach (var str in arbitraryStringCollection.Distinct())
{
ProcessString(str)
}
You could further refactor it using some sort of ForEach extension method, or refactor the entire thing into an extension method.
Alternatively, if your requirements are slightly different (e.g. you want to keep dict for the lifetime of the application), then this could be refactored in a slightly different way, e.g.
HashSet<string> dict = new HashSet<string>();
foreach(string str in arbitraryStringCollection)
{
dict.DoOnce(str, ProcessString);
}
// Re-usable extension method)
public static class ExtensionMethods
{
public static void DoOnce<T>(this ISet<T> set, T value, Action<T> action)
{
if (!set.Contains(value))
{
action(value);
set.Add(value);
}
}
}
Having problems with formatting CSV created from C# code. In the notepad file the output scrolls vertically down one row (the values seen in the structs below are output in one row. There is a row of numbers as well that appears directly below the struct values but the numbers should be in a new row beside the structs). When I open in excel it's a similar story only the output from the structs is where it should be however the row of numbers appears directly below the struct values but one row to the right if that makes sense, and the numbers should appear directly beside their corresponding struct values. The code I'm using is below.
Here are the structs for the dictionaries im working with.
public enum Genders
{
Male,
Female,
Other,
UnknownorDeclined,
}
public enum Ages
{
Upto15Years,
Between16to17Years,
Between18to24Years,
Between25to34Years,
Between35to44Years,
Between45to54Years,
Between55to64Years,
Between65to74Years,
Between75to84Years,
EightyFiveandOver,
UnavailableorDeclined,
}
the csv file that does the outputting using a streamwriter and stringbuilder.
public void CSVProfileCreate<T>(Dictionary<T, string> columns, Dictionary<T, int> data)
{
StreamWriter write = new StreamWriter("c:/temp/testoutputprofile.csv");
StringBuilder output = new StringBuilder();
foreach (var pair in columns)
{
//output.Append(pair.Key);
//output.Append(",");
output.Append(pair.Value);
output.Append(",");
output.Append(Environment.NewLine);
}
foreach (var d in data)
{
//output.Append(pair.Key);
output.Append(",");
output.Append(d.Value);
output.Append(Environment.NewLine);
}
write.Write(output);
write.Dispose();
}
And finally the method to feed the dictionaries into the csv creator.
public void RunReport()
{
CSVProfileCreate(genderKeys, genderValues);
CSVProfileCreate(ageKeys, ageValues);
}
Any ideas?
UPDATE
I fixed it by doing this:
public void CSVProfileCreate<T>(Dictionary<T, string> columns, Dictionary<T, int> data)
{
StreamWriter write = new StreamWriter("c:/temp/testoutputprofile.csv");
StringBuilder output = new StringBuilder();
IEnumerable<string> col = columns.Values.AsEnumerable();
IEnumerable<int> dat = data.Values.AsEnumerable();
for (int i = 0; i < col.Count(); i++)
{
output.Append(col.ElementAt(i));
output.Append(",");
output.Append(dat.ElementAt(i));
output.Append(",");
output.Append(Environment.NewLine);
}
write.Write(output);
write.Dispose();
}
}
You write Environment.NewLine after every single value that you output.
Rather than having two loops, you should have just one loop that outputs
A "pair"
A value
Environment.NewLine
for each iteration.
Assuming columns and data have the same keys, that could look something like
foreach (T key in columns.Keys)
{
pair = columns[key];
d = data[key];
output.Append(pair.Value);
output.Append(",");
output.Append(d.Value);
output.Append(Environment.NewLine);
}
Note two complications:
If pair.Value or d.Value contains a comma, you need to surround the output of that cell with double quotes.
If If pair.Value or d.Value contains a comma and also contains a double-quote, you have to double up the double-quote to escape it.
Examples:
Smith, Jr
would have to be output
"Smith, Jr"
and
"Smitty" Smith, Jr
would have to be output
"""Smitty"" Smith, Jr"
UPDATE
Based on your comment about the keys...
For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair structure representing a value and its key. The order in which the items are returned is undefined.
http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
If you cannot use the key to associate the right pair with the right data, how do you make that association?
If you are iterating the dictionary and they happen to be in the order you hope, that is truly undefined behavior that could change with the next .NET service pack.
You need something reliable to relate the pair with the correct data.
About the var keyword
var is not a type, but rather a shortcut that frees you from writing out the entire type. You can use var if you wish, but the actual type is KeyValuePair<T, string> and KeyValuePair<T, int> respectively. You can see that if you write var and hover over that keyword with your mouse in Visual Studio.
About disposing resources
Your line
write.Dispose();
is risky. If any of your code throws an Exception prior to reaching that line, it will never run and write will not be disposed. It is strongly preferable to make use of the using keyword like this:
using (StreamWriter write = new StreamWriter("c:/temp/testoutputprofile.csv"))
{
// Your code here
}
When the scope of using ends (after the associated }), write.Dispose() will be automatically called whether or not an Exception was thrown. This is the same as, but shorter than,
try
{
StreamWriter write = new StreamWriter("c:/temp/testoutputprofile.csv");
// Your code here
}
finally
{
write.Dispose();
}