Check that all values of an array have been used - c#

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) { ... }

Related

Searching in ListArray C#

What is the fastest method for searching data from list array in C#?
My code:
public class fruits
{
public string Initial;
public string Fruit;
public fruits(string initials, string names)
{
Initial = initials;
Fruit = names;
}
}
// load
List<fruits> List = new List<fruits>();
List.Add(new fruits("A", "Apple"));
List.Add(new fruits("P", "Pineapple"));
List.Add(new fruits("AP", "Apple Pineapple"));
//combo box select text
var text = combobox.SelectText();
for (int i=0; i<list.Count(); i++)
{
if (list[i].Fruit == text)
{
MessageBox.Show(list[i].Initial);
}
}
I know this search method is not good, if list data contains too much data.
If you want a "fast" solution, you should use a foreach instead of LINQ. This solution can improve your perfomance a lot:
fruits firstOrDefault = null:
foreach (fruits f in List)
{
if (f.Fruit == text)
{
FirstOrDefault = f;
break;
}
}
You can get few more information about the LINQ performance in posts like
Is a LINQ statement faster than a 'foreach' loop?
http://geekswithblogs.net/BlackRabbitCoder/archive/2010/04/23/c-linq-vs-foreach---round-1.aspx
https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
You can use linq
var result = List.FirstOrDefault(q => q.Fruit == text );
MessageBox.Show(result.Initial);
The best (and only) way to tell what method is fastest for a certain situation is to actually benchmark/measure it with different algorithms. You already have two answers/approaches here (LINQ and foreach). Time both of them and then pick the faster one.
Or in other words: Measuring your code gives you an advantage over those people who think they are too smart to measure. ;)
To speed things up further you might want to consider to keep the list sorted and then do a binary search on the list. It increases the time for insertion, because you have to sort the list after inserts, but it should speed up the search process. But then again: Do not just take my word for it, measure it!

Not all empty lists are removed from ObservableCollection?

I have code to create a grouped list for a ListView in Xamarin Forms, which for some reason only sometimes removes a group from the list if it is empty.
char[] alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
var animals = new List<string>() { "Jaguar", "Elephant", "Nemo", "Cat", "Dog", "Iguana", "Puma", "Crow", "Hawk", "Owl", "Badger", "Meerkat", "Lion", "Tiger", "Rabbit", "Pig" };
var groups = new ObservableCollection<GroupedItemModel>();
for (int i = 0; i < alpha.Length; i++)
{
groups.Add(new GroupedItemModel(alpha[i].ToString()));
}
foreach (var _group in groups)
{
foreach (var _animal in animals)
{
if (_animal[0].ToString().ToUpper() == _group.GroupName.ToUpper())
{
_group.Add(_animal);
}
}
}
for (int i = 0; i < groups.Count; i++)
{
if (groups[i].Count == 0)
{
groups.RemoveAt(i);
}
}
ListSource = groups;
However, this happens:
Why do these groups not get removed? Any solutions?
When removing from a list you need to work backwards.
such as:
if you have 1,2,3,4,5,6,7,8,9,10 and you say remove even numbers as its 1-10,
on 1 thats ok, on 2, you remove it, so 3 moves into its place.. so now you arent checking 3...you skip to 4.. so in this example you'd get away withit, but what if your list was already missing say 3, it would have moved number 4 to the place where 2 had been, and move on, 4 would be missed
Change your for loop to be decremental
As described by BugFinder above, the problem you are getting is caused by modifying the list as you iterate through it. Problem solved. But it might also be worth taking a look at the overall approach. Currently we:
Create list of all possible groups (A-Z)
Iterate though the list of animals and add each to a group based upon the first letter
Iterate though the groups and throw away any which are empty.
This can all be done with a few lines of Linq code and a tweak to the GroupedItemModel
public class GroupedItemModel
{
public GroupedItemModel(string name, IEnumerable<string> values){
Name = name;
Values = new List<string>(values);
}
public string Name { get; }
public List<string> Values { get; }
}
We can now populate an ObservableCollection<GroupedItem> with
new ObservableCollection<GroupedItem>(animals.GroupBy(a => char.ToUpper(a[0]).ToString()).OrderBy(g => g.Key).Select(g => new GroupedItem(g.Key, g)));
Very minor afterword: Prefixing variable names with underscores is usually used for member variables of a class, not for local variables (_group, _animal). It doesn't change how the code works but when sharing code with others, using the general conventions helps speed up reading and understanding the code.

Serially assign values to OrderedDictionary in C#

I have two key-value pairs, and now I want to fill up the larger one with values from the smaller one in a serial manner.
OrderedDictionary pickersPool = new OrderedDictionary(); // Small
OrderedDictionary pickersToTicketMap = new OrderedDictionary(); // Big
pickersPool.Add("emp1", 44);
pickersPool.Add("emp2", 543);
Now I need to update pickersToTicketMap to look like this:
("100", 44);
("109", 543);
("13", 44);
("23", 543);
So basically I need the pickersPool value to cycle through the keys of the pickersToTicketMap dictionary.
I need pickerPool values to keep cycling pickersToTicketMap and updating its value serially.
The pickersToTicketMap orderedlist initially has a value of:
("100", "null");
("109", "null");
("13", "null");
("23", "null");
so I need for the values of PickerPool orderedDictionary to fill up those nulls in a repeated fashion.
It sounds like you should start with a List<string> (or possibly a List<int>, given that they all seem to be integers...) rather than populating your map with empty entries to start with. So something like:
List<string> tickets = new List<string> { "100", "109", "13", "23" };
Then you can populate your pickersToTicketMap as:
var pickers = pickersPool.Values;
var pickerIterator = pickers.GetEnumerator();
foreach (var ticket in tickets)
{
if (!pickerIterator.MoveNext())
{
// Start the next picker...
pickerIterator = pickers.GetEnumerator();
if (!pickerIterator.MoveNext())
{
throw new InvalidOperationException("No pickers available!");
}
}
ticketToPickerMap[ticket] = pickerIterator.Current;
}
Note that I've changed the name from pickersToTicketMap to ticketToPickerMap because that appears to be what you really mean - the key is the ticket, and the value is the picker.
Also note that I'm not disposing of the iterator from pickers. That's generally a bad idea, but in this case I'm assuming that the iterator returned by OrderedDictionary.Values.GetEnumerator() doesn't need disposal.
There may be what you are looking for:
using System.Linq;
...
int i = 0;
// Cast OrderedDictionary to IEnumarable<DictionaryEntry> to be able to use System.Linq
object[] keys = pickersToTicketMap.Cast<DictionaryEntry>().Select(x=>x.Key).ToArray();
IEnumerable<DictionaryEntry> pickersPoolEnumerable = pickersPool.Cast<DictionaryEntry>();
// iterate over all keys (sorted)
foreach (object key in keys)
{
// Set the value of key to element i % pickerPool.Count
// i % pickerPool.Count will return for Count = 2
// 0, 1, 0, 1, 0, ...
pickersToTicketMap[key] = pickersPoolEnumarable
.ElementAt(i % pickersPool.Count).Value;
i++;
}
PS: The ToArray() is required to have a separate copy of the keys, so you don't get a InvalidOperationException due to changing the element you are iterating over.
So you want to update the large dictionary's values with consecutive and repeating values from the possibly smaller one? I have two approaches in mind, one simpler:
You can repeat the smaller collection with Enumerable.Repeat. You have to calculate the count. Then you can use SelectMany to flatten it and ToList to create a collection. Then you can use a for loop to update the larger dictionary with the values in the list via an index:
IEnumerable<int> values = pickersPool.Values.Cast<int>();
if (pickersPool.Count < pickersToTicketMap.Count)
{
// Repeat this collection until it has the same size as the larger collection
values = Enumerable.Repeat( values,
pickersToTicketMap.Count / pickersPool.Count
+ pickersToTicketMap.Count % pickersPool.Count
)
.SelectMany(intColl => intColl);
}
List<int> valueList = values.ToList();
for (int i = 0; i < valueList.Count; i++)
pickersToTicketMap[i] = valueList[i];
I would prefer the above approach, because it's more readable than my second which uses an "infinite" sequence. This is the extension method:
public static IEnumerable<T> RepeatEndless<T>(this IEnumerable<T> sequence)
{
while (true)
foreach (var item in sequence)
yield return item;
}
Now you can use this code to update the larger dictionary's values:
var endlessPickersPool = pickersPool.Cast<DictionaryEntry>().RepeatEndless();
IEnumerator<DictionaryEntry> endlessEnumerator;
IEnumerator<string> ptmKeyEnumerator;
using ((endlessEnumerator = endlessPickersPool.GetEnumerator()) as IDisposable)
using ((ptmKeyEnumerator = pickersToTicketMap.Keys.Cast<string>().ToList().GetEnumerator()) as IDisposable)
{
while (endlessEnumerator.MoveNext() && ptmKeyEnumerator.MoveNext())
{
DictionaryEntry pickersPoolItem = (DictionaryEntry)endlessEnumerator.Current;
pickersToTicketMap[ptmKeyEnumerator.Current] = pickersPoolItem.Value;
}
}
Note that it's important that I use largerDict.Keys.Cast<string>().ToList(), because I can't use the original Keys collection. You get an exception if you change it during enumeration.
Thanks to #jon skeet, although he modified my objects too much while trying to provide a hack for this.
After looking at your solution, I implemented the following, which works well for all my objects.
var pickerIterator = pickerPool.GetEnumerator();
foreach (DictionaryEntry ticket in tickets)
{
if (!pickerIterator.MoveNext())
{
// Start the next picker...
pickerIterator = pickerPool.GetEnumerator();
if (!pickerIterator.MoveNext())
{
throw new InvalidOperationException("No pickers available!");
}
}
ticketToPickerMap[ticket.Key] = pickerIterator.Value.ToString();
}

How to access specific keys/values in a dictionary 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;

Populate ArrayList 2 columns and keep a count in second column of each occurrence c#

I'm new to ASP.NET C#. Trying to create an ArrayList with 2 columns one for the value (string) and one for counting how many of each. While adding values I need to search the ArrayList to find if the value already exist, if so add 1, if not, add it to the array and set count column to 1. Can someone provide a bit of code sample? If there is a better approach then I'd like to hear it.
private static Dictionary<string, int> values = new Dictionary<string, int>();
private static void Add(string newValue)
{
if(values.ContainsKey(newValue))
{
values[newValue]++; // Increment count of existing item
}
else
{
values.Add(newValue, 1); // Add new item with count 1
}
}
If you're just starting with a list of strings, there are plenty of simpler ways to do this.
I'd probably use the GroupBy extension here
List<string> items = GetItems(); // from somewhere
var groups = items.GroupBy(i => i);
var countedItems = groups.Select(g => new
{ Value = g.First(), HowMany = g.Count() });
Then putting into an ArrayList, if you want:
var arrayList = new ArrayList();
foreach (var thing in countedItems)
{
arrayList.Add(thing.Value + " " thing.HowMany);
}
But I'd probably prefer to put this into a Dictionary, because you know that each word will map to just one value - the number of times it appears.
var result = countedItems.ToDictionary(i => i.Value, i => i.HowMany);

Categories