So I have this Hashtable
Hashtable Months = new Hashtable();
Months.Add(0, "JANUARY");
Months.Add(1, "FEBRUARY");
Months.Add(2, "MARCH");
Months.Add(3, "APRIL");
Months.Add(4, "MAY");
Months.Add(5, "JUNE");
Months.Add(6, "JULY");
Months.Add(7, "AUGUST");
Months.Add(8, "SEPTEMBER");
Months.Add(9, "OCTOBER");
Months.Add(10, "NOVEMBER");
Months.Add(11, "DECEMBER");
I would like for the user to enter a month e.g."May" the be able to retrieve index[4] from an array within my program.
string Month = Console.ReadLine();
Basically to retrieve the index from the number of the corresponding Month entered.
Try this
var key = Months.Keys.Cast<int>().FirstOrDefault(v => Months[v] == "MAY");
Note: Don't forget to include this namespace - using System.Linq;
Get elements from your Hashtable in DictionaryEntry format
foreach (DictionaryEntry e in Months)
{
if ((string)e.Value == "MAY")
{
//get the "index" with e.Key
}
}
You can perform it just using a loop;
public List<string> FindKeys(string value, Hashtable hashTable)
{
var keyList = new List<string>();
IDictionaryEnumerator e = hashTable.GetEnumerator();
while (e.MoveNext())
{
if (e.Value.ToString().Equals(value))
{
keyList.Add(e.Key.ToString());
}
}
return keyList;
}
Usage;
var items = FindKeys("MAY",Months);
If you want to lookup the index from the month's name, a Dictionary<string, int> would be more suitable. The reason why I've swapped the parameters is becuase if you only want to lookup the index, and not also the other way around, this will be much faster.
You should declare the dictionary as case-insensitive so that it detects for instance may, May, mAy and MAY as the same thing:
Dictionary<string, int> Months = new Dictionary<string, int>(StringComparison.OrdinalIgnoreCase);
Then just use its TryGetValue() method whenever you want to get the month index:
int MonthIndex = 0;
if(Months.TryGetValue(Month, out MonthIndex)) {
//Month was correct, continue your code...
else {
Console.WriteLine("Invalid month!");
}
Related
I am trying to add values to a dictionary as they get determined based on certain conditions. The app has to loop through each line and once a certain condition has been met then a value must be added to the dictionary. Here is the code that is task with looping through the lines and determine values to be added.
foreach (var line in this.FileLines)
{
count++;
string[] bits = line.Split(',');
fineNumber = bits[0].Trim();
int length = bits.Length;
if (length == 9)
{
//other processing gets done here, code not included as its of no interest for this question
}
else
{
//AddErrorFinesToFile(line, fineNumber);
AddFinesToDictonary(fineNumber, line);
continue;
}
}
Then below is the actual method signature and its code, in this method I am simply trying to add values to the dictionary as they come.
public Dictionary<string, string> AddFinesToDictonary(string fineNumber, string errorLine)
{
Dictionary<string, string> erroredLines = new Dictionary<string, string>();
erroredLines.Add(fineNumber, errorLine);
return erroredLines;
}
The only problem that seems to arise here is, only the latest value gets added to the dictionary, meaning the previous added value gets overwritten.
Make erroredLines as global scope.
Dictionary<string, string> erroredLines = new Dictionary<string, string>();
foreach (var line in this.FileLines)
{
count++;
string[] bits = line.Split(',');
fineNumber = bits[0].Trim();
int length = bits.Length;
if (length == 9)
{
//other processing gets done here, code not included as its of no interest for this question
}
else
{
//AddErrorFinesToFile(line, fineNumber);
AddFinesToDictonary(fineNumber, line);
continue;
}
}
public void AddFinesToDictonary(string fineNumber, string errorLine)
{
erroredLines.Add(fineNumber, errorLine);
// return erroredLines;
}
And also no need to return erroredLines dictionary.
What about this;
Dictionary<string, string> erroredLines = new Dictionary<string, string>();
foreach (var line in this.FileLines)
{
count++;
string[] bits = line.Split(',');
fineNumber = bits[0].Trim();
int length = bits.Length;
if (length == 9)
{
//other processing gets done here, code not included as its of no interest for this question
}
else
{
erroredLines.Add(fineNumber, line);
continue;
}
}
after foreach you can use erroredLines dictionary.
The reason is every time you add data to the Directory you create a new one instead of adding data to the exists one
There are two choices you could make:
make a Dictionary out of the function
pass the Dictionary as an out ref to the function
#Hameed Syed 's answer has already given the first one (option 1).
Here is how you could pass the Dictionary as a ref parameter (out) to the function (option 2):
public void AddFinesToDictonary(out Dictionary<string,string>dict, string fineNumber, string errorLine)
{
dict.Add(fineNumber, errorLine);
}
In a dictionary, I want to add a list of numbers for a given key.But I am unable to do it.
for(int i = 0 ; i < size ; i++){
string input = Console.ReadLine();
string[] inputList = input.Split(' ');
count[Convert.ToInt32(inputList[0])]++;
if(!map.ContainsKey(Convert.ToInt32(inputList[0]))){
map.Add(Convert.ToInt32(inputList[0]),new List<string>());
map_index.Add(Convert.ToInt32(inputList[0]),new List<int>());
}
}
The question is bit unclear. My understanding of your problem is as follows: You have a dictionary, a value of the dictionary is a list, and you have trouble adding an item to that list. Since you didn't explain your notation I'm using more general names, just to give you an idea what has to be done:
Dictionary<int, List<string>> myDict = new Dictionary<int, List<string>>();
if (myDict.ContainsKey(myKey))
{
myDict[myKey].Add(myVal);
}
else
{
myDict[myKey] = new List<string> { myVal };
}
If the key is not in the dictionary you create an entry together with the list and initialize the list with the new value. If the key is there you just access the list (by using myDict[myKey]) and add the new value to the list. Since the list is always created for a new key you don't have to worry that it's not initialized when adding a value for an existing key.
This could be one the efficient Solution and much easier than if-else.
Dictionary<int, List<string>> myDict = new Dictionary<int, List<string>>();
try
{
myDict[myKey].Add(myVal);
}
catch
{
myDict[myKey] = new List<string> { myVal };
}
There is a 'one-command-line' way to do this using AddOrUpdate from ConcurrentDictionary:
using System.Linq;
using System.Collections.Generic;
using System.Collections.Concurrent;
...
var dictionary = new ConcurrentDictionary<int, string[]>();
var itemToAdd = "item to add to key-list";
dictionary.AddOrUpdate(1, new[]{item1ToAdd}, (key, list) => list.Append(itemToAdd));
// If key 1 doesn't exist, creates it with a list containing itemToAdd as value
// If key 1 exists, adds item to already existent list (third parameter)
This question already has answers here:
Remove Item in Dictionary based on Value
(6 answers)
Closed 9 years ago.
The question should be clear from the title itself. I need to check if an item exist in the dictionary and remove it from the dictionary in C#. The only catch is that i have to do this using only the value item and not the key.
The declaration is as below:
IDictionary<string, myCustomClassObject> clients = new IDictionary<string, myCustomClassObject>();
Now i fill in the dictionary by:
clients["key"] = myCustomClassObject1;
Now how can i find and remove this item myCustomClassObject1 from my Dictionary. I only want to use the value item and not the key
Is this doabale...if so please guide...
regards
Edit: Thank you all....got valuable comments...probably have some thinking to do ...thanks
It depends on how you need it to perform. If you can accept O(N) performance, you could just do something like:
foreach(var pair in clients) {
if(pair.Value == expected) {
clients.Remove(pair.Key);
break;
}
}
However, if you need faster you would need two dictionaries - one the reverse of the other (i.e. keyed by the instances). So when adding, you would do:
clientsByKey.Add(key, value);
clientsByValue.Add(value, key);
so you can do (to remove-by-value):
string key;
if(clientsByValue.TryGetValue(value, out key)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
or similarly (to remove-by-key):
Foo value;
if(clientsByKey.TryGetValue(key, out value)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
It's not very efficient to search a dictionary by it's values. However, you can use Linq to find all entries with a given value.
IEnumerable<KeyValuePair<string, myCustomClassObject>> pairs = clients
.Where(entry => entry.Value.Equals(myCustomClassObject1)).ToList();
foreach (KeyValuePair<string, myCustomClassObject> kv in pairs)
clients.Remove(kv.Key);
This should do it. It removes all clients having a given value.
while (clients.ContainsValue(myCustomClassObject1))
clients.Remove(clients.Where(x => x.Value == myCustomClassObject1).FirstOrDefault().Key);
Or create a new dictionary without the values you want removed
clients = clients.Where(x => x.Value != myCustomClassObject1).ToDictionary(k => k.Key, v => v.Value);
If the collection only contains one item with the value to be removed then you can use one of the other answers here, which will work just fine.
However, if your collection can have multiple items with the same value then you need to be careful.
You cannot modify a collection while iterating over it, so you will need to find the keys of all the items that you want to remove in one loop and put them in a list, and then iterate over that list in a separate loop to delete the items.
For example:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
void run()
{
var dict = new Dictionary<string, int>
{
{"Key1", 1},
{"Key2", 2},
{"Key3", 3},
{"Key4", 2},
{"Key5", 4}
};
int valueToRemove = 2;
var keysToRemove = (from element in dict
where element.Value == valueToRemove
select element.Key).ToList();
foreach (var key in keysToRemove)
dict.Remove(key);
foreach (var element in dict)
Console.WriteLine("Key = {0}, Value = {1}", element.Key, element.Value);
}
static void Main(string[] args)
{
new Program().run();
}
}
}
Use,
Following will remove only first matching value
client newClient = new client();
foreach(KeyValuePair<string, client> client in clients) {
if(client.value.equals(newClient)) {
clients.remove(client.key);
break;
}
}
Or if you want to remove all matching values,
foreach(var client in clients.Where(kvp => kvp.Value == newClient).ToList()) {
clients.Remove(client.Key);
}
First of all, apologies for the nasty title. I will correct it later.
I have some data like below,
"BOULEVARD","BOUL","BOULV", "BLVD"
I need a data structure that is O(1) for looking up any of this words by other. For example, if I use a dictionary I would need to store this keys/values like this, which looks odd to me,
abbr.Add("BLVD", new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" });
abbr.Add("BOUL", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULV", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
abbr.Add("BOULEVARD", new List<string> { "BOULEVARD", "BOUL", "BOULV", "BLVD" });
Which data structure to use to keep this data appropriate to my querying terms?
Thanks in advance
Create two HashMap - one maps word to a group number. And the other one maps group number to a list of words. This way you save some memory.
Map<String, Integer> - Word to Group Number
Map<Integer, List<String>> - Group Number to a list of words
You need two O(1) lookups - first to get the group number and then by it - get the list of words.
Assuming abbr is a Dictionary<String, IEnumerable<String>>, you could use the following function:
public static void IndexAbbreviations(IEnumerable<String> abbreviations) {
for (var a in abbreviations)
abbr.Add(a, abbreviations);
}
This will populate the dictionary with the provided list of abbreviations such that when any of them is looked up in the dictionary. It is slightly better than the example code you provided, because I am not creating a new object for each value.
From the documentation, "Retrieving a value by using its key is very fast, close to O(1), because the Dictionary(Of TKey, TValue) class is implemented as a hash table."
The choice of dictionary looks fine to me. As mentioned above, you should use the same list to be referenced in the dictionary. The code could go something like this:
var allAbrList = new List<List<string>>
{
new List<string> {"BOULEVARD", "BOUL", "BOULV", "BLVD"},
new List<string> {"STREET", "ST", "STR"},
// ...
};
var allAbrLookup = new Dictionary<string, List<string>>();
foreach (List<string> list in allAbrList)
{
foreach (string abbr in list)
{
allAbrLookup.Add(abbr, list);
}
}
The last part could be converted into LINQ to have less code, but this way it is easier to understand.
If you don't create a new list for each key, then a Dictionary<string, List<string>> will be fast and reasonably memory-efficient as long as the amount of data isn't enormous. You might also be able to get a little extra benefit from reusing the strings themselves, though the optimizer might take care of that for you anyway.
var abbr = new Dictionary<string, List<string>>;
var values = new List<string> { "BOULEVARD","BOUL","BOULV", "BLVD" };
foreach(var aValue in values) abbr.add(value, values);
As Petar Minchev already said, you can split your list into an list of groups and a list of keys that points to this group. To simplify this (in usage) you can write an own implementation of IDictionary and use the Add method to build those groups. I gave it a try and it seems to work. Here are the important parts of the implementation:
public class GroupedDictionary<T> : IDictionary<T,IList<T>>
{
private Dictionary<T, int> _keys;
private Dictionary<int, IList<T>> _valueGroups;
public GroupedDictionary()
{
_keys = new Dictionary<T, int>();
_valueGroups = new Dictionary<int, IList<T>>();
}
public void Add(KeyValuePair<T, IList<T>> item)
{
Add(item.Key, item.Value);
}
public void Add(T key, IList<T> value)
{
// look if some of the values already exist
int existingGroupKey = -1;
foreach (T v in value)
{
if (_keys.Keys.Contains(v))
{
existingGroupKey = _keys[v];
break;
}
}
if (existingGroupKey == -1)
{
// new group
int newGroupKey = _valueGroups.Count;
_valueGroups.Add(newGroupKey, new List<T>(value));
_valueGroups[newGroupKey].Add(key);
foreach (T v in value)
{
_keys.Add(v, newGroupKey);
}
_keys.Add(key, newGroupKey);
}
else
{
// existing group
_valueGroups[existingGroupKey].Add(key);
// add items that are new
foreach (T v in value)
{
if(!_valueGroups[existingGroupKey].Contains(v))
{
_valueGroups[existingGroupKey].Add(v);
}
}
// add new keys
_keys.Add(key, existingGroupKey);
foreach (T v in value)
{
if (!_keys.Keys.Contains(v))
{
_keys.Add(v, existingGroupKey);
}
}
}
}
public IList<T> this[T key]
{
get { return _valueGroups[_keys[key]]; }
set { throw new NotImplementedException(); }
}
}
The usage could look like this:
var groupedDictionary = new GroupedDictionary<string>();
groupedDictionary.Add("BLVD", new List<string> {"BOUL", "BOULV"}); // after that three keys exist and one list of three items
groupedDictionary.Add("BOULEVARD", new List<string> {"BLVD"}); // now there is a fourth key and the key is added to the existing list instance
var items = groupedDictionary["BOULV"]; // will give you the list with four items
Sure it is a lot of work to implement the whole interface but it will give to an encapsulated class that you don't have to worry about, after it is finished.
I don't see a reason to define the value part of your dictionary as a List<string> object, but perhaps that is your requirement. This answer assumes that you just want to know whether the word essentially means "Boulevard".
I would pick one value as the "official" value and map all of the other values to it, like this:
var abbr = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", "BLVD"); // this line may be optional
abbr.Add("BOUL", "BLVD");
abbr.Add("BOULV", "BLVD");
abbr.Add("BOULEVARD", "BLVD");
Alternatively, you could define an enum for the value part of the dictionary, as shown below:
enum AddressLine1Suffix
{
Road,
Street,
Avenue,
Boulevard,
}
var abbr = new Dictionary<string, AddressLine1Suffix>(StringComparer.CurrentCultureIgnoreCase);
abbr.Add("BLVD", AddressLine1Suffix.Boulevard);
abbr.Add("BOUL", AddressLine1Suffix.Boulevard);
abbr.Add("BOULV", AddressLine1Suffix.Boulevard);
abbr.Add("BOULEVARD", AddressLine1Suffix.Boulevard);
i have a generic sorted list "results" with key = some filename and value = boolean.
I would like to sort the list by the boolean entry or value column. does anyone know how i can do this?
Thanks!
SortedList is optimized so that inertions occur in an ordered fashion, such that enumeration occurs in a sorted order at minimal cost. Anything else requires a re-sort. Thus:
SortedList<string,bool> l = new SortedList<string, bool>();
l.Add("a", true);
l.Add("b", false);
l.Add("c", true);
l.Add("d", false);
var orderByVal = l.OrderBy(kvp => kvp.Value);
but this enumeration will be significantly slower to calculate, and be performed up-front, requiring extra storage to do so.
Depending on your situation it might be cheaper to maintain 2 SortedList instances with the key/value reversed.
In .NET 2.0, you could add your items to a SortedList:
public static List<MyObject> SortedObjects(IEnumerable<MyObject> myList) {
SortedList<string, MyObject> sortedList = new SortedList<string, MyObject>();
foreach (MyObject object in myList) {
sortedList.Add(object.ValueIWantToSort, object);
}
return new List<MyObject>(sortedList.Values);
}
For descending all list items
list.OrderByDescending();
or
var list = list.OrderByDescending(x => x.Product.Name)
.ThenBy(x => x.Product.Price).ToList();
Normally that sorted by the first key on the list so if you swap the key and value on the add, then match that on the binding
that sample example i use and work fine
public static SortedList<string, string> GetCountries(string conn)
{
var dict = new SortedList<string, string>();
dict.Add("","Select One");
var sql = "SELECT [CountryID] ,[Descr] FROM [dbo].[Countries] Order By CountryID ";
using (var rd = GetDataReader(conn, sql))
{
while (rd.Read())
{
dict.Add(rd["Descr"].ToString(), rd["CountryID"].ToString());
}
}
return dict;
}
Dim List As SortedList(Of String, String) = VDB.CoreLib.DbUtils.GetCountries(connDB)
ddlBankCountry.DataSource = List
ddlBankCountry.DataTextField = "Key"
ddlBankCountry.DataValueField = "Value"
ddlBankCountry.DataBind()