How to access specific keys/values in a dictionary C# - c#

I am building a flash card application to help me study in High School. The user enters terms and definitions in a dictionary. Now I can't figure out how to set the labels to randomly show a term and its definition. My methods code goes something like this:
public void startQuiz()
{
Random random = new Random();
int randNum;
//My dictionary with all the terms and
//definitions is called terms
randomNum = random.Next(terms.Count);
termLabel.Text = // ???
definitionLabel.text = //???
}
I hope that is coherent enough. I basically want the randomNum to index a specific key and value from my dictionary of "terms." Then set the termLabel text to the chosen key value (which is a string), and the definitionLabel text to the specified value (also a string). I will be happy to provide clarification, as I am barely learning how to use visual c#
Here is my dictionary
Dictionary<string, string> terms = new Dictionary<string, string>()
//Here is how terms get added
private void addTermButton_Click(object sender, EventArgs e)
{
term = termBox.Text;
definition = definitionBox.Text;
terms.Add(term, definition);
//Clear text boxes for more terms and definitions
termBox.Text = "";
definitionBox.Text = "";
}

You may want to use an OrderedDictionary.
http://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary.aspx
You will be then able to use an index.

You can use linq to retrieve a Tuple. Be aware that this is not necessarily the order by which the elements were inserted into the dictionary.
var tuple = terms.ElementAt(randNum);
TitleLabel = tuple.Key;
DescriptionLabel = tuple.Value;

Related

Searching dictionary for matching key from a textbox C#

I'm reading a csv file that contains abbreviations and the full version for example LOL,Laughing out Loud. I've created a dictionary where the abbreviation is the key and full version is the value.
'''
private void btnFilter_Click(object sender, RoutedEventArgs e)
{
var keys = new List<string>();
var values = new List<string>();
using (var rd = new StreamReader("textwords.csv"))
{
while (!rd.EndOfStream)
{
var splits = rd.ReadLine().Split(',');
keys.Add(splits[0]);
values.Add(splits[1]);
}
}
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v }).ToDictionary(x => x.Key, x => x.Value);
foreach (var k in dictionary)
{
//
}
aMessage.MessageContent = txtContent.Text;
}
'''
I'm using a button that will check if the textbox txtContent contains any of the abbreviations in the text and change this to the full version. So if the textbox contained the following. "Going to be AFK" after the button click it would change this to "Going to be away from keyboard".
I want to write a foreach loop that would check for any abbreviations, elongate them and then save this to a string variable MessageContent.
Would anyone be able to show me the best way to go about this as I'm not sure how to do this with the input from a textbox?
Thanks
You can just use LINQ to read and create a dictionary object:
var dictionary = File.ReadAllLines(#"textwords.csv")
.Select(x => x.Split(",",StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(key => key.FirstOrDefault().Trim(),
value => value.Skip(1).FirstOrDefault().Trim());
If the abbrevs are correct, i.e. you don't need fuzzy logic, you can use a variety of .NET objects and search the keys rather quickly.
if(dict.ContainsKey(myKey))
{
}
I did that freehand, so it might be dict.Keys.Contains() or similar. The point is you can search the dictionary directly rather than loop.
If you need to do a more fuzzy search, you can utilize LINQ to write queries to iterate over collections of objects extremely fast (and few lines).
As for yours, you would iterate over dictionary.Keys to seach keys, but I still don't see why you need the extra code.

How to insert an item into an array at a specific index in c#?

I have 2 text boxes (txt1, txt2), 2radio button (rb1, rb2), 2 numericupdown box(nud1,nud2) & combo box.
I want to store the data from the above in an array, in the named places. array name is mycode.
example:
rb1 = mycode[3], combobox=mycode[0], txt1=mycode[6]....etc
Eventually I want to concatnate some of the named idexes in my array. And show in the message box
example:
my code is mycode[3] + mycode[7]
how to do it?
but I don't know best optios is to do this arry, list or any other?
Is this what you are looking for??
myCode[0] = myValue
To use an array, you can create an empty array like this;
var arr = Object[7] //This allows you to add different data types, and 7 sets the array length to hold 7 items
arr[0] = txt1.text;
arr[1] = txt2.text;
You can concatenate this way:
Console.Write($" my code is {arr[1]} + {arr[2]}")
Array is too primitive for your needs. as #timur suggested, you can create a Dictionary<TKey,TValue> which your "TKey" is string and your "TValue" is object. you can use it like this:
// Create a new dictionary of objects, with string keys.
//
Dictionary<string, object> mycode=
new Dictionary<string, object>();
// Add some elements to the dictionary. There are no
// duplicate keys, but some of the values are duplicates.
mycode.Add("txt1", "Hello");
mycode.Add("rb1", 2);
you can use actual control references instead of their names as string. all of controls are inherited from Control class. so you can use Dictionary<Control, object>.
here i provide you a simple exapmle with a form that have a TextBox (textBox1), a NumericUpDown (numericUpDown1) and a Button (button1).
Dictionary<Control, object> myDictionary = new Dictionary<Control, object>();
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (myDictionary.ContainsKey((Control)sender))
myDictionary[(Control)sender] = ((NumericUpDown)sender).Value;
else
myDictionary.Add((Control)sender, ((NumericUpDown)sender).Value);
}
private void button1_Click(object sender, EventArgs e)
{
if (myDictionary.ContainsKey(numericUpDown1))
textBox1.Text = ((decimal)myDictionary[numericUpDown1]).ToString();
}
with this approach you must do a lot of cast and you must be careful.

Search anywhere in list, not just the first letter

What I want to do, is to search in a ComboBox, for a word, or a part of a word like this:
For example I have these entries in my combobox:
abc
Abc
Dabc
adbdcd
And when I search for "abc", it should show me every one in the list, except "
adbdcd"
I fill my combobox from a mysql database, so my items are in a "Collection".
I have autocomplete enabled (mode: SuggestAppend, source: ListItems)
This is the code, I am using right now:
private void comboBox1_KeyPress(object sender, KeyPressEventArgs e)
{
comboKeyPressed();
}
private void comboBox1_TextChanged(object sender, EventArgs e)
{
if (comboBox1.Text.Length == 0) comboKeyPressed();
}
private void comboKeyPressed()
{
comboBox1.DroppedDown = true;
object[] originalList = (object[])comboBox1.Tag;
if (originalList == null)
{
// backup original list
originalList = new object[comboBox1.Items.Count];
comboBox1.Items.CopyTo(originalList, 0);
comboBox1.Tag = originalList;
}
// prepare list of matching items
string s = comboBox1.Text.ToLower();
IEnumerable<object> newList = originalList;
if (s.Length > 0)
{
newList = originalList.Where(item => item.ToString().ToLower().Contains(s));
}
// clear list (loop through it, otherwise the cursor would move to the beginning of the textbox...)
while (comboBox1.Items.Count > 0)
{
comboBox1.Items.RemoveAt(0);
}
// re-set list
comboBox1.Items.AddRange(newList.ToArray());
}
The problem with this code, is if I search for "abc" in my example list, "adbdcd" will show up too. And this code randomly crashes my program when I hit backspace in the combobox.
This is the root cause of crash:
while (comboBox1.Items.Count > 0)
{
// this is raising exception if you try to remove the last item
// Check the doc of RemoveAt
comboBox1.Items.RemoveAt(0);
}
use that instead:
comboBox1.Items.Clear();
However what you are trying to achieve is still unclear.
If Text of combox is empty, then nothing will ever happen apart from clearing and re adding the same items to the combo box.
My understanding is that you're trying to duplicate the completion behavior while having it enabled. It can raise exception too (AccessViolationException) since you try to modify the Items collection while the framework tries to do the same.
If you're not happy with the default auto completion behavior, disable it and try to implement it completely inside the comboKeyPressed method.
It means then calling it whenever text is modified.
Modification of your code to make it work (Disable auto completion though):
private void comboBox1_TextChanged(object sender, EventArgs e)
{
comboKeyPressed();
}
private void comboKeyPressed()
{
if (comboBox1.Text == lastFilter)
{
return;
}
object[] originalList = (object[]) comboBox1.Tag;
if (originalList == null)
{
// backup original list
originalList = new object[comboBox1.Items.Count];
comboBox1.Items.CopyTo(originalList, 0);
comboBox1.Tag = originalList;
}
// prepare list of matching items
string s = comboBox1.Text.ToLower();
IEnumerable<object> newList = originalList;
if (s.Length > 0)
{
newList = originalList.Where(item => item.ToString().ToLower().Contains(s));
}
// clear list (loop through it, otherwise the cursor would move to the beginning of the textbox...)
comboBox1.Items.Clear();
// re-set list
comboBox1.Items.AddRange(newList.ToArray());
comboBox1.Select(Text.Length -1, 0);
lastFilter = comboBox1.Text;
comboBox1.DroppedDown = true;
}
So, you want fulltext search.
Where is your data coming from? What kind of data is it? What are the edge cases?
For databases, I like to use Sphinx for fulltext indexing.
For in-memory data, efficient fulltext search algorithms include Suffix Arrays, Suffix Trees, and Patricia Tries. Implementing them can be a fun (and time-consuming) challenge. Or you might find a suitable implementation online. You can find barebones implementations of these algorithms, as well as more polished implementations of fulltext search, such as Lucene.
To give you a sketch of how they work, imagine taking every possible suffix of every word that you store, e.g. needle:
needle
eedle
edle
dle
le
e
Put all these suffixes into an ordered data structure, such as a sorted array or list (for static data) or a B-tree or SortedDictionary (if data is added regularly). After inserting needle, it would contain:
dle
e
edle
eedle
le
needle
Now we can search for any part of a word (e.g. edl) using binary search or better, and see if we have a hit.
To extract more information than just whether or not we have a hit, we could add data to, for example, the values of the SortedDictionary. (We used the suffixes as keys.) Interesting data could be the entire word, or perhaps the original text and location where the word was encountered.
If the number of entries to search from is always low - perhaps no more than a few hundred - then a simple implementation will suffice.
// Select all words that contain our query string
var matchingWords = wordList.Where(word => word.Contains(query));
This is the naive, linear implementation that will become very slow for large data. For small data, though, it is super easy. Just feed the new subset to your combo box.
You might want to add case-insensitivity to the Contains() call, using its optional second parameter.

Queries regarding combobox.valuemember when using a custom dictionary

I have the following code as part of a WPF I am creating:
Dictionary<string, string[]> storeDictionary = new Dictionary<string, string[]>();
private void populateDropbox()
{
storeDropDown.DataSource = new BindingSource(storeDictionary, null);
storeDropDown.DisplayMember = "Key";
storeDropDown.ValueMember = "Value";
storeDropDown.SelectedValue = 0;
}
Note: storeDropDown is name of the combobox.
How would I get storeDropdown.Valuemember to be the first element of the string[]?
Please bare in mind this is my first program and c# is my first programming language.
If you need more info let me know, thanks.
You say you are doing WPF but the ComboBox you are using comes from winforms.
So what are you actually working on?
Assuming you are using Winforms (the ComboBox of your example) here is my solution:
1 - Create a class to encapsulate your array of string and override the ToString() method
internal class CustomArrayOfStrings
{
public string[] _strings;
public override ToString()
{
return _strings[0];
}
}
Now your dictionary looks like something like this:
Dictionary<string, CustomArrayOfStrings> storeDictionary = new Dictionary<string, CustomArrayOfStrings>();
2- Then, to display the first element in your ComboBox you change
storeDropDown.DisplayMember = "Key";
to
storeDropDown.DisplayMember = "Value";
This will call the ToString methode of your Value wich is a CustomArrayOfStrings.
3 - If you want to manipulate the selected first element of your array, you can do it this way:
CustomArrayOfStrings selected = storeDropDown.SelectedValue as CustomArrayOfStrings;
string firstValue = selected._strings[0];

Check that all values of an array have been used

Sorry if this is a stupid noob question. I'm doing a very small project for my girlfriend - a list of countries and she has to enter their capitals (obscure countries, mind you) . Since I'm a total beginner, I had to resort to using two arrays, one for countries and the other for capitals, with matching indexes. That way it's easy to check for the right answer and I don't have to parse any text files or use any data-bases. I'm using random numbers to make it more interesting. To stop the program from generating the same countries over and over again, I'm using a List of integers that keeps tracks of what indexes have already been used and regenerates the number if the list contains the previous one. Pretty basic stuff. Surprisingly, it all works.
But I'm having a problem. How do I check that I've run out of countries, basically? :) I can't simply check the List size against my countries array, since List probably includes more values than the array, and if (taken.Equals(Countries.Length)) doesn't seem to work. Or I can't find the right place in the code to put this check.
Sorry if this is simple, but I can't seem to find a proper solution.
EDIT
Wow, what an amazing community. During the short walk from Starbucks to my place I get dozens of quality answers which cover a huge array of design techniques. This is so great! Thank you everyone! Obviously, the question has been answered but I will post the code for you, if anyone has any additional comments.
// JUST A TEST FOR NOW, 13 COUNTRIES
string[] Countries = {"Belgium", "France", "The Netherlands", "Spain", "Monaco", "Belarus", "Germany",
"Portugal", "Ukraine", "Russia", "Sweden", "Denmark", "South Africa"};
string[] Capitals = {"Brussels", "Paris", "Amsterdam", "Madrid", "Monaco", "Minsk", "Berlin",
"Lisbon", "Kiev", "Moscow", "Stockholm", "Copenhagen", "Pretoria"};
Random number = new Random();
List<int> taken = new List<int>();
int index;
int score = 0;
private int Generate()
{
while (true) {
index = number.Next(0, Countries.Length);
if (taken.Contains(index)) continue;
// THIS IS WHAT I WAS INITIALLY TRYING TO DO
if (taken.Equals(Countries.Length)) {
MessageBox.Show("Game over!");
return -1;
}
return index;
}
}
private void Form1_Load(object sender, EventArgs e)
{
index = Generate();
taken.Add(index);
label1.Text = Countries[index];
label3.Text = "0 out of " + Countries.Length.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Trim() == Capitals[index].ToString()) {
label2.Text = "You win!";
index = Generate();
taken.Add(index);
label1.Text = Countries[index];
textBox1.Clear();
label3.Text = ++score + " out of " + Countries.Length.ToString();
}
else {
label2.Text = "Wrong!";
textBox1.Clear();
}
}
}
}
To stop the program from generating the same countries over and over again, I'm using a List of integers that keeps tracks of what indexes have already been used and regenerates the number if the list contains the previous one.
...
How do I check that I've run out of countries, basically?
You might want to consider an alternative approach, as this is going to be quite expensive and overly complicated.
Instead of trying to add one country at random, checking against ones you've already added, you could just make the entire list of countries, then perform a shuffle ("random sort") on the collection. This way, you'll get all of the countries in one shot in a random order.
Instead of using two arrays, or an array and a list, let's introduce something of C# 4.0 that actually looks and is easy to use and seems to be made for this type of assignments.
Follow this code with your eyes and specifically look how these "anonymous types" are used in the end. It makes life real easy.
// initialize your array like so,
// now you can access your items as countries[1].name and countries[1].city
// and you will never have to worry about having too much cities or countries
// PLUS: they're always together!
var countries = new [] {
new { name = "The Netherlands", city = "Amsterdam"},
new { name = "Andorra", city = "Vaduz" },
new { name = "Madagascar", city = "Antananarivo"}
};
// randomize by shuffling (see http://stackoverflow.com/questions/375351/most-efficient-way-to-randomly-sort-shuffle-a-list-of-integers-in-c-sharp/375446#375446)
Random random = new Random();
for (int i = 0; i < countries.Length; i += 1)
{
int swapIndex = random.Next(i, countries.Length);
if (swapIndex != i)
{
var temp = countries[i];
countries[i] = countries[swapIndex];
countries[swapIndex] = temp;
}
}
// go through all your items in the array using foreach
// so you don't have to worry about having too much items
foreach(var item in countries)
{
// show your girlfriend the country, something like
string inputString = DisplayCountry(item.country);
if(inputString == item.city)
{
ShowMessage("we are happy, you guessed right!");
}
}
// at the end of the foreach-loop you've automatically run out of countries
DisplayScore(to-your-girlfriend);
Note: you can easily expand on this anonymous types by adding whether or not that particular country/city pair was guessed right and make a subsequent test with the ones she failed.
You could use a HashSet<int> to keep track of indexes that have been used. This won't accept duplicate values. The Add method returns a boolean that indicates whether the value was already in the list:
if (hashSet.Add(index))
DisplayValue(index);
else
//regenerate
But I would probably use your existing stragegy, but backwards: create a list pre-filled with values from 0 to Count - 1. Pick indexes from this list, removing them as you use them. This is logically similar to Reed Copsey's suggestion of sorting, but probably requires less change to your existing code.
var availableIndexes = new List<int>(Enumerable.Range(0, countryCount));
var random = new Random();
while (availableIndexes.Count > 0)
{
var index = availableIndexes[Random.Next(0, availableIndexes.Count)];
DisplayValue(index);
availableIndexes.Remove(index);
}
You can use a key/value pair, like a Dictionary<string, string> to store your countries and capitals. Then iterate through the collection using a random LINQ orderby clause:
Dictionary<string, string> Countries = new Dictionary<int, string>();
// populate your collection of countries
foreach(var country in Countries.OrderBy(c => Guid.NewGuid()))
{
Console.WriteLine("Key: {0} Value: {1}", country.Key, country.Value);
}
Create a Country class and a Capital class.
Then model your classes to use a Dictionary<TKey, TValue> Generic Collection so that you declare the generic Dictionary object as:
Dictionary<Country, Capital>
where Country is the key and Capital is its value.
For MSDN reference to Dictionary and its sample usage, you can follow below link:
http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
As you keep using Countries and Capitals, add them to above Dictionary instance after checking for their existence in the Dictionary instance, if any of them do exist then either popup an info message or a warning.
Quick and dirty, not necessarily efficient or secure.
Dictionary<string, string> countriesAndCapitals = new Dictionary<string, string>()
{
{ "Afghanistan", "Kabul" },
{ "Albania", "Tirane" },
{ "Algeria","Algers" },
{ "Andorra", "Andorra la Vella" } //etc, etc
};
foreach (var countryCapital in countriesAndCapitals.OrderBy(f => Guid.NewGuid()))
{
Console.WriteLine(countryCapital.Key + " " + countryCapital.Value);
}
It seems like what you need is a different type of data structure, two sets of lists would work fine but it is complicated for nothing. I suggest looking into the dictionary list type.
Dictionary<string,string> countryList = new Dictionary<string,string>();
countryList.Add("Canada","Ottawa");
countryList.Add("Thailand","Bankok");
etc...
You could then iterate through the list while a boolean value sees whether or not there was a hit. More info on Dictionary list type.
Why don't you remove the items from the list that you used? Then you don't have conflicts. Then you check states.Count() > 0.
The quickest thing I can think to do is to use the Distinct() call on your list. Then your count of items in the list can be compared to your array's count to see if all have been used.
if(myUsedList.Distinct().Count() < myArray.Count) { ... }

Categories