I have some code which populates a hashtable with a question as the key and an arraylist of answers as the value.
I want to then print out these values from the hashtable so that it displays the question and corresponding solutions for each individual question in the hashtable.
I know I have done something totally stupid with the foreach loop to printout the hashtable contents, but i've been coding for a good few hours straight and I can't think of the logic to printout my nested arraylist.
Help appreciated greatly.
Here is the code:
//Hashtable Declaration
static Hashtable sourceList = new Hashtable();
//Class For Storing Question Information
public class QuestionAnswerClass
{
public string simonQuestion;
public ArrayList simonAnswer = new ArrayList();
}
//Foreach loop which populates a hashtable with results from
//a linq query that i need to print out.
foreach (var v in linqQueryResult)
{
Debug.WriteLine(v.question);
newques.simonQuestion = v.question;
//Debug.WriteLine(v.qtype);
//newques.simonQType = v.qtype;
foreach (var s in v.solution)
{
Debug.WriteLine(s.Answer);
newques.simonAnswer.Add(s.Answer);
}
}
sourceList.Add(qTextInput,newques);
//foreach loop to print out contents of hashtable
foreach (string key in sourceList.Keys)
{
foreach(string value in sourceList.Values)
{
Debug.WriteLine(key);
Debug.WriteLine(sourceList.Values.ToString());
}
}
As you are using LINQ you are obviously not constrained to framework 1.1, so you should not be using the HashTable and ArrayList classes. You should use the strictly typed generic Dictionary and List classes instead.
You don't need a class to keep the question and answers in as you have the Dictionary. The class would only be an extra container with no real purpose.
//Dictionary declaration
static Dictionary<string, List<string>> sourceList = new Dictionary<string, List<string>>();
//Foreach loop which populates a Dictionary with results from
//a linq query that i need to print out.
foreach (var v in linqQueryResult) {
List<string> answers = v.solution.Select(s => s.Answer).ToList();
sourceList.Add(v.question, answers);
}
//foreach loop to print out contents of Dictionary
foreach (KeyValuePair<string, List<string>> item in sourceList) {
Debug.WriteLine(item.Key);
foreach(string answer in item.Value) {
Debug.WriteLine(answer);
}
}
If you need the class for some other reason, that could look like below.
(Note that the question string is both referenced in the class and used as key in the dictionary, but the dictionary key isn't really used for anything in this code.)
//Class For Storing Question Information
public class QuestionAnswers {
public string Question { get; private set; }
public List<string> Answers { get; private set; }
public QuestionAnswers(string question, IEnumerable<string> answers) {
Question = question;
Answers = new List<string>(answers);
}
}
//Dictionary declaration
static Dictionary<string, QuestionAnswers> sourceList = new Dictionary<string, QuestionAnswers>();
//Foreach loop which populates a Dictionary with results from
//a linq query that i need to print out.
foreach (var v in linqQueryResult) {
QuestionAnswers qa = new QuestionAnswers(v.question, v.solution.Select(s => s.Answer));
sourceList.Add(qa.Question, qa);
}
//foreach loop to print out contents of Dictionary
foreach (QustionAnswers qa in sourceList.Values) {
Debug.WriteLine(qa.Question);
foreach(string answer in qa.Answers) {
Debug.WriteLine(answer);
}
}
Try this
foreach (DictionaryEntry entry in sourceList)
{
Debug.WriteLine(entry.Key);
foreach (object item in (ArrayList)entry.Value)
{
Debug.WriteLine(item.ToString());
}
}
Minor tweaks
foreach (string key in sourceList.Keys)
{
Console.WriteLine(key);
foreach(string value in sourceList[key])
{
Console.WriteLine("\t{0}", value); // tab in answers one level
}
Console.WriteLine(); // separator between each set of q-n-a
}
Shouldn't this:
Debug.WriteLine(sourceList.Values.ToString());
be this?
foreach(var obj in sourceList.Values)
Debug.WriteLine(obj);
First, a strongly typed generic collection would make it easier. Let's start by defining an alias for the strongly typed collection:
using MyHash = System.Collections.Generic.Dictionary<string,
System.Collections.Generic.List<string>>;
From now on, MyHash means the same as the lengthy generic definition. Now you can declare the hashtable member like:
static MyHash sourceList = new MyHash();
And iterate over it like:
foreach (var pair in sourceList)
{
var question = pair.Value;
Console.WriteLine(question);
foreach (var answer in pair.Value)
Console.WriteLine(" " + answer);
}
Hope this is useful.
foreach (DictionaryEntry entry in Hashtable)
{
}
Find more in http://www.dotnetperls.com/hashtable
Related
I have a Dictionary<string, string> object where values are stored that look like this:
examplePlanet : defaultText0
examplePlanet* : defaultText1
examplePlanet** : defaultText2
examplePlanetSpecificlocationA : specificAText0
examplePlanetSpecificlocationA* : specificAText1
examplePlanetSpecificlocationB : specificBText
And I have a string filter that matches one of these keys or is a subset of a key.
This filter has the form planetLocation, which can be split into planet and location.
My goal is to create a list of values where the filter is matched in this way: if planetLocation exists in the dictionary, add its value, and all values where the key matches but has extra *'s, to a list.
If planetLocation does not exist, only add values where the key matches the planet part of the filter (with possible extra *'s).
Basically, I want all values where the filter matches the key as much as possible.
Examples:
examplePlanetSpecificlocationA gives [specificAText0, specificAText1]
examplePlanetSpecificlocationB gives [specificBText]
examplePlanetSpecificlocationC gives [defaultText0, defaultText1, defaultText2]
I have already tried (among other things that didn't work) this:
private List<string> filteredResults;
///<summary>Filters dictionaries and returns a list of values</summary>
private List<string> GetFilteredResults(Dictionary<string, string> inputdictionary, string filter)
{
List<string> _filteredResults = new List<string>();
foreach (KeyValuePair<string, string> entry in inputdictionary)
{
if (entry.Key.Contains(filter))
{
_filteredResults.Add(entry.Value);
}
}
return _filteredResults;
}
public void main()
{
//stuff happens here that assigns a value to filterPlanet and filterLocation
filteredResults = new List<string>();
filteredResults = GetFilteredResults(exampledictionary, filterPlanet + filterLocation);
if (filteredResults.Count == 0)
{
filteredResults = GetFilteredResults(exampledictionary, filterPlanet);
}
//do stuff with the filtered results
}
This almost worked, but returns all values where the key contains filterPlanet and not just filterPlanet itself plus possible *'s. I'm not sure how to make this function do what I want, and even if it somehow works I'm sure there is a more efficient way of filtering than this. Could you please help me here?
I wouldn't use the *'s as a way of differentiating multiple values for the same key, and I'd keep filterPlanet and filterLocation separate. That way you can use a simple O(1) dictionary lookup, rather than iterating across all keys, doing substring searching, etc.
public class PlanetFilterer
{
private readonly Dictionary<string, List<string>> lookup = new Dictionary<string, List<string>>();
public PlanetFilterer(IEnumerable<(string filter, string value)> filters)
{
foreach (var (filter, value) in filters)
{
var filterWithoutStars = filter.TrimEnd('*');
if (!lookup.TryGetValue(filterWithoutStars, out var values))
{
values = new List<string>();
lookup[filterWithoutStars] = values;
}
values.Add(value);
}
}
public IReadOnlyList<string> Lookup(string planet, string location)
{
List<string> results;
if (lookup.TryGetValue(planet + location, out results))
{
return results;
}
if (lookup.TryGetValue(planet, out results))
{
return results;
}
return Array.Empty<string>();
}
}
Usage:
var filters = new[]
{
("examplePlanet", "defaultText0"),
("examplePlanet*", "defaultText1"),
("examplePlanet**", "defaultText2"),
("examplePlanetSpecificlocationA", "specificAText0"),
("examplePlanetSpecificlocationA*", "specificAText1"),
("examplePlanetSpecificlocationB", "specificBText"),
};
var filterer = new PlanetFilterer(filters);
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationA")));
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationB")));
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationC")));
Try it online
I have a dictionary:
Dictionary<string, string> valuesDict = new Dictionary<string, string> {
{“Q1”, “A1”},
{“Q2”, “A2”},
{“Q3”, “A3”},
{“Q4”, “A4”} /*20000 Q and A pairs*/
};
Inorder to load this to a third party interface which only accepts a list of objects (of class QuestionAnswer), I am manually converting it to a list like so
Public Class QuestionAnswer {
Public string Question;
Public string Answer;
}
objects of the QuestionAnswer class are then created within the loop
List<QuestionAnswer> qaList = new List<QuestionAnswer>();
foreach(var key in valuesDict.Keys) {
qaList.add(new QuestionAnswer {Question = key, Answer = valuesDict[key]});
}
I want to know if there is a faster way to populate this list from the dictionary.
What I have found so far:
While looking around for the solution, I came across a solution for a conversion of simple Dictionary to List of simple types like so: Convert dictionary to List<KeyValuePair>
Could someone please help me in utilizing this solution to my case please.
I am also open to any other solution that can remove this overhead.
You're doing an unnecessary lookup for the key:
foreach(var item in valuesDict) {
qaList.add(new QuestionAnswer {Question = item.Key, Answer = item.Value});
}
You can also provide the list count when intializing to avoid resize:
List<QuestionAnswer> qaList = new List<QuestionAnswer>(valuesDict.Keys.Count);
You can use LinQ-based solutions, but that is slower and you're asking for optimal solution.
You can create a list with LINQ by projecting each KeyValuePair of the dictionary into your QuestionAnswer object:
var qaList =
valuesDict.Select(kvp => new QuestionAnswer { Question = kvp.Key, Answer = kvp.Value })
.ToList()
Faster? Well, yes, absolutely, iterate directly the dictionary, not the Keys collection:
foreach(var kv in valuesDicts) {
qaList.add(new QuestionAnswer {Question = kv.Key, Answer = kv.Value});
Or better yet, using System.Linq:
valuesDict.Select(kv => new QuestionAnswer(kv.Key, kv.Value);
In your code you are performing an unecessary key search on each iteration.
Basically ther are two common approaches. Using a foreach or LINQ. To check the performance you can use a stopwatch and run a simple code like this:
Dictionary<string, string> valuesDict = new Dictionary<string, string>();
for (uint i = 0; i < 60000; i++)
{
valuesDict.Add(i.ToString(), i.ToString());
}
List<QuestionAnswer> qaList;
Stopwatch stp = new Stopwatch();
stp.Start();
//LINQ approach
qaList = valuesDict.Select(kv => new QuestionAnswer { Question = kv.Key, Answer = kv.Value }).ToList();
stp.Stop();
Console.WriteLine(stp.ElapsedTicks);
stp.Restart();
//Foreach approach
qaList = new List<QuestionAnswer>();
foreach (var item in valuesDict)
{
qaList.Add(new QuestionAnswer { Question = item.Key, Answer = item.Value });
}
stp.Stop();
Console.WriteLine(stp.ElapsedTicks);
My result: Foreach performes about 30% faster than the LINQ approach.
Struggling with this code, very new to c# and I've tried multiple answers on stackoverflow before writing this question so please hear me out...
var onlinePlayers = new Dictionary<int, Dictionary<string,string>>();
var count = 0;
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
onlinePlayers.Add(
count,
new Dictionary<string, string> {
{ "SteamID", player.userID.ToString() },
{ "PlayerName", player.displayName.ToString() },
{ "IPAddress", Regex.Replace(player.net.connection.ipaddress, #":{1}[0-9]{1}\d*", "").ToString }
}
);
count += 1;
}
foreach (var k in onlinePlayers.Keys)
{
foreach (KeyValuePair<string, string> pair in onlinePlayers[k])
{
Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
}
}
I'm expecting to see
SteamID: 1231231221321
PlayerName: HelloWorld
IPAddress: 127.0.0.1
for each item in the collection.
Compiler error:
Error while compiling StatLogger.cs(38,111): error CS1503: Argument #2 cannot
convert method group expression to type string
You're missing the parentheses () at the end of your .ToString().
{ "IPAddress", Regex.Replace(player.net.connection.ipaddress,
#":{1}[0-9]{1}\d*", "").ToString }
Update :
Also player.net.connection.ipaddress.ToString()
The above will turn the IP into a string and that should prevent the error.
You're trying to enumerate the Dictionary's key, rather than the Dictionary itself.
Try this:
foreach (player in onlinePlayers) {
foreach (playerInfo in player.Value) {
Console.WriteLine("{0}: {1}", playerInfo.Key, playerInfo.Value);
}
}
See if that helps.
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);