Printing out Hashtable without using a loop in c# - c#

I need to print out in a file a HashTable.
Is it possible to do it without a for/foreach loop? Something like:
Hashtable myHash;
Logging.traceMessage(Datetime.now, myhash) // I want to have just a single datatime entry in my traceMessage.
My expected output is something like:
'hashtable'
[KEY] - [VALUE]
pizzas - one
costumer - three
'TraceFile'
Datetime: pizzas - one , costumer - three
If I use a foreach loop for printing out the hash I will get a datetime in every key and value pair.

I would recommend the following. It should be fairly optimized for your needs.
Hashtable mhash = new Hashtable();
var sb = new StringBuilder();
foreach (var myhash in mhash)
{
sb.AppendLine(myhash.ToString()); -- Note you format your hash however you want here
}
Logging.traceMessage(DateTime.Now, sb.ToString());

you need to iterate in the list at some point - but for your purpose, the below extension method may help
Define your Extension method in a library (lets say myExtensions)
public static string EntriesCSV(this Hashtable ht)
{
string ls_return;
foreach (var pair in ht)
ls_return += String.Format("{0}={1};\r\n", pair.Key, pair.Value); //this is very simple statement, but you can improve this to fit your needs
return ls_return;
}
Reference your extensions library
using myExtensions;
Enjoy simplicity code :)
Hashtable myHash;
Logging.traceMessage(Datetime.now, myhash.EntriesCSV());
Hope this helps

Related

Dictionary Unhandled Exception in C#: "The given key was not present in the dictionary"

I'm trying to print out a dictionary in C# that will simulate a graph. My dictionary looks like this:
Dictionary<int, List<int>> graph = new Dictionary<int, List<int>>();
In main, I add something to the dictionary, then try to print it out:
dicOfLists myDic = new dicOfLists();
myDic.AddEdge(1, 2);
myDic.printList();
The methods AddEdge and PrintList are pretty straightforward:
AddEdge:
public void AddEdge(int v1, int v2)
{
if (graph[v1] == null)
{
graph[v1] = new List<int> { v2 };
return;
}
graph[v1].Add(v2);
}
PrintList:
for (int i = 0; i < 1; i++)
{
Console.WriteLine(graph[i][i]);
}
I haven't done much programming in C# or Python, so dictionaries are new to me. I think why I'm getting tripped up is more conceptual than anything, specifically because I'm not sure how a list works within a dictionary.
The way I currently understand it is as follows:
Upon calling Addedge(1, 2) my dictionary is creating a list with a single element 2 at position 1 of my dictionary. This is because the first parameter represents the dictionary's key, and the second represents the list. The key functions like it would in a hashtable. When the key is provided, the dictionary looks at that position, and then creates a list.
Like I said, I'm new to C# so please don't skewer me too hard. While this might be something trivial like a straightforward syntax error, I'm not able to find much of anything for this specific problem online. Any help would be greatly appreciated!
You've got one method adding key/values to a dictionary and another printing them. The method printing them doesn't "know" what's been inserted, so it's better if that method doesn't make any assumptions about what's in the dictionary. Instead of just looping through a series of possible keys (0 to 1, 0 to n, etc.) it's better to operate according to what actually is in the dictionary.
var keys = graph.Keys;
// or, if you they were entered out of sequence and you want to sort them
var keys = graph.Keys.OrderBy(k => k);
// Now you're using the actual keys that are in the dictionary, so you'll never
// try to access a missing key.
foreach(var key in keys)
{
// It's not quite as clear to me what you're doing with these objects.
// Suppose you wanted to print out everything:
Console.WriteLine($"Key: {key}");
foreach(var value in graph[key])
{
Console.WriteLine(value);
}
}

Handle Collision using Hashtable Class in c#

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.

Trouble with CSV formatting

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();
}

issue while getting values from Dictionary<int, List<int>>

I am having a dictionary which was defined as follows
Dictionary<int, List<int>> dict1 = new Dictionary<int, List<int>>();
I will have a list element where i can store the values which will come from database
List<int> lstdbTaskID = new List<int>();
assume this holds
100 105 110 200
respectively.
I will have my dictionary with values stored as follows. Assume that i have 2 keys 10,20 respectively, and for this key values i will have my values as for 10 i will have 100,105 and 110 and for 20 i will have 200.
I would like to compare these values with the list available lstdbTaskID
I tried this
foreach (int strlst in lstdbTaskID)
{
if (dict1.ContainsValue(lstdbTaskID[strlst]))
}
But i am getting errors as follows
The best overloaded method match for 'System.Collections.Generic.Dictionary>.ContainsValue(System.Collections.Generic.List)' has some invalid arguments`
and
Cannot convert from 'int' to 'System.Collections.Generic.List'`
can any help me on this?
Your code is wrong at because you are trying to compare int value with List of int.
Your dictionary is:
Dictionary of int to List of int.
and you have another structure as
List of int
so when you do:
// Compiler fails here because you are trying to check whether dictionary contains
// the given integer value. Dictionary in this case has a list of integers as its `Value`
// in its `<Key,Value>` pair.
dict1.ContainsValue(lstdbTaskID[strlst])
Use linq statement:
foreach (int strlst in lstdbTaskID)
{
if (dict1.Any(pair => pair.Value.Contains(strlst)))
{
// do something
}
}
Edit: If you want this without linq, do the linq task by self.
foreach (int strlst in lstdbTaskID)
{
foreach (int key in dict1.Keys)
{
if (dict1[key].Contains(strlst))
{
// do something
}
}
}
Look at the type of the value you're storing in your dictionary - it's a List<int>, not an int. So it makes no sense to ask whether the dictionary contains a value of 5, say.
However, it does make sense to ask if the dictionary contains any value (list) which itself contains 5.
For example:
foreach (int strlst in lstdbTaskID)
{
if (dict1.Values.Any(list => list.Contains(strlst))
{
...
}
}
However, that's really not a very efficient way of representing it. It's not clear what the best of representing it is though, without knowing what you're going to do with the results. You may just want a HashSet<int> containing all the values from all the lists, for example - but if you want to get back to the keys whose values contained a particular ID, that's a different matter.
EDIT: In .NET 2.0 land, you could use:
foreach (int strlst in lstdbTaskID)
{
foreach (List<int> list in dict1.Values)
{
if (list.Contains(strlst))
}
}
... but you're really not using the dictionary as a dictionary in either case here...
I am having slight problems understanding your question fully, however, my answer should push you in the right direction. Seeing as you do not have access to Linq (as you are using .Net 2.0):
static bool IsContained(IEnumerable<int> lstdbTaskID, Dictionary<int, HashSet<int>> dict1)
{
foreach (int strlst in lstdbTaskID)
foreach (HashSet<int> value in dict1.Values)
if (value != null && value.Contains(strlst))
return true;
return false;
}
You should use a HashSet<int> as it is far faster for looking up values (and is supported in .Net 2.0); however, you should not use HashSet<int> (and instead use List<int>) if:
The list needs to store duplicates.
- or - The order of the values is important.
Well, dict1 is a dictionary of dictionaries, not ints. So dict1.ContainsValue takes a dictionary as a parameter - you're checking whether it contains a given dictionary or not. But you're giving it an int.
Try this:
if (dict1.Any(x => x.Value.ContainsValue(strlst))) // ...

String to dictionary using regex (want to optimize)

I have string on the format "$0Option one$1$Option two$2$Option three" (etc) that I want to convert into a dictionary where each number corresponds to an option. I currently have a working solution for this problem, but since this method is called for every entry I'm importing (few thousand) I want it to be as optimized as possible.
public Dictionary<string, int> GetSelValsDictBySelValsString(string selectableValuesString)
{
// Get all numbers in the string.
var correspondingNumbersArray = Regex.Split(selectableValuesString, #"[^\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();
List<int> correspondingNumbers = new List<int>();
int number;
foreach (string s in correspondingNumbersArray)
{
Int32.TryParse(s, out number);
correspondingNumbers.Add(number);
}
selectableValuesString = selectableValuesString.Replace("$", "");
var selectableStringValuesArray = Regex.Split(selectableValuesString, #"[\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();
var selectableValues = new Dictionary<string, int>();
for (int i = 0; i < selectableStringValuesArray.Count(); i++)
{
selectableValues.Add(selectableStringValuesArray.ElementAt(i), correspondingNumbers.ElementAt(i));
}
return selectableValues;
}
The first thing that caught my attention in your code is that it processes the input string three times: twice with Split() and once with Replace(). The Matches() method is a much better tool than Split() for this job. With it, you can extract everything you need in a single pass. It makes the code a lot easier to read, too.
The second thing I noticed was all those loops and intermediate objects. You're using LINQ already; really use it, and you can eliminate all of that clutter and improve performance. Check it out:
public static Dictionary<int, string> GetSelectValuesDictionary(string inputString)
{
return Regex.Matches(inputString, #"(?<key>[0-9]+)\$*(?<value>[^$]+)")
.Cast<Match>()
.ToDictionary(
m => int.Parse(m.Groups["key"].Value),
m => m.Groups["value"].Value);
}
notes:
Cast<Match>() is necessary because MatchCollection only advertises itself as an IEnumerable, and we need it to be an IEnumerable<Match>.
I used [0-9] instead of \d on the off chance that your values might contain digits from non-Latin writing systems; in .NET, \d matches them all.
Static Regex methods like Matches() automatically cache the Regex objects, but if this method is going to be called a lot (especially if you're using a lot of other regexes, too), you might want to create a static Regex object anyway. If performance is really critical, you can specify the Compiled option while you're at it.
My code, like yours, makes no attempt to deal with malformed input. In particular, mine will throw an exception if the number turns out to be too large, while yours just converts it to zero. This probably isn't relevant to your real code, but I felt compelled to express my unease at seeing you call TryParse() without checking the return value. :/
You also don't make sure your keys are unique. Like #Gabe, I flipped it around used the numeric values as the keys, because they happened to be unique and the string values weren't. I trust that, too, is not a problem with your real data. ;)
Your selectableStringValuesArray is not actually an array! This means that every time you index into it (with ElementAt or count it with Count) it has to rerun the regex and walk through the list of results looking for non-whitespace. You need something like this instead:
var selectableStringValuesArray = Regex.Split(selectableValuesString, #"[\d]+").Where(x => (!String.IsNullOrWhiteSpace(x))).ToArray();
You should also fix your correspondingNumbersString because it has the same problem.
I see you're using C# 4, though, so you can use Zip to combine the lists and then you wouldn't have to create an array or use any loops. You could create your dictionary like this:
return correspondingNumbersString.Zip(selectableStringValuesArray,
(number, str) => new KeyValuePair<int, string>(int.Parse(number), str))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

Categories

Resources