Get hashtable obj by key & change its public properties - c#

First of all I declare a hashtable and its values. The key of a hashtable entry is a GUID and the value is an object with a few string values.
Guid g = Guid.NewGuid();
Hashtable hash = new Hashtable();
InstallationFiles instFiles = new InstallationFiles(string1, string2, string3);
hash.Add(g, instFiles);
//...add many other values with different GUIDs...
My goal is to give a user a possibility to EDIT string 1, string2, string3. To cut a long story short, I am in a position where I can get the "GUID g" of the entry which needs to be edited:
public void edit()
{
//here I retrieve the GUID g of the item which has to be edited:
object objectHash = item.Tag;
//here i loop through all hash entries to find the editable one:
foreach(DictionaryEntry de in hash)
{
if(de.Key.ToString() == objectHash)
{
//here I would like to access the selected entry and change string1 -
//the line below is not working.
hash[de.Key].string1 = "my new value";
}
}
}
How do I make this line work?
hash[de.Key].string1 = "my new value";

Use Dictionary<Guid, InstallationFiles> instead HashTable
upd. You can use this.
(hash[de.Key] as InstallationFiles).string1 = "asdasd"
Ok, explanation:
Because Hashtable is not generic type, it contains references on keys and values as Objects.
Thats why, when you access to your value hashtable[mykey], you got reference to Object. To make it as reference to your type (InstallationFiles), you have to from "reference to Object" get "reference to InstallationFiles". Im my sample I use "as" operator to do this.

Related

Allow multiple values for the same key when creating JSON using ExpandoObject and IDictionary

I am trying to create dynamic JSON using ExpandoObject and IDictionary.
During the dynamic creation of JSON there could be instances when the Name or Value would repeat. However when adding the repeated Name or Value to the ExpandoObject, it gives an error:
An item with the same key has already been added.
Below is my code snippet :
DataTable dt_MappedColumns = (DataTable)ViewState["MappedColumns"];
dynamic ManCols = new ExpandoObject();
var dictionary1 = (IDictionary<string, object>)ManCols;
foreach (DataRow dr in dt_MappedColumns.Rows)
{
dictionary1.Add(dr["TColumnName"].ToString(), dr["AColumnName"].ToString());
}
string Manjson = Newtonsoft.Json.JsonConvert.SerializeObject(dictionary1);
The DataTable looks like this:
Sr.No TColumnName AColumnName
----- ----------- -----------
1 Apple Lion
2 Orange Tiger
3 Mango Fox
4 Orange Wolf
In the above table the first 3 Rows are added successfully into dictionary1; however, when we try to add the fourth Row, it gives the error.
My desired JSON structure for repeated values would look like this:
{"Apple":"Lion", "Orange":["Tiger","Wolf"], "Mango":"Fox"}
Is it possible to create this JSON structure from the table?
Sure this is possible. Inside your loop you just need to check whether the key already exists in the dictionary and take the appropriate action. There are three cases:
The key doesn't exist, so add it as you are doing now.
The key exists and the existing value is a string, in which case you need to replace it with a list containing the old string value and the new string value.
The key exists and the existing value is a list, in which case you just need to add the new string to the list.
Here is what the code looks like:
foreach (DataRow dr in dt_MappedColumns.Rows)
{
string key = dr["TColumnName"].ToString();
string value = dr["AColumnName"].ToString();
if (!dictionary1.ContainsKey(key))
{
// key does not already exist, so add it
dictionary1.Add(key, value);
}
else
{
// key exists, get the existing value
object existingValue = dictionary1[key];
if (existingValue is string)
{
// replace the existing string value with a list
dictionary1[key] = new List<string> { (string)existingValue, value };
}
else
{
// the existing value is a list, so add the new value to it
((List<string>)existingValue).Add(value);
}
}
}
Fiddle: https://dotnetfiddle.net/PERc0D

Will adding a string property as a key to a dictionary clone the string? Is there a proper workaround?

Let's say I have a class with a string property
public class Something
{
public int SomeIntProperty { get; set; }
public string SomeStringProperty { get; set; }
}
and let's say that the SomeStringPropertys can be very long and I want to create a dictionary
this.dic = somethings
.GroupBy(s => s.SomeStringProperty)
.ToDictionary(g => g.Key);
that I hold in memory for the duration of when my application is running. My question is whether, due to the way that strings act like value types, that will end up duplicating the strings to hold in the dictionary. If so, what is a workaround so that I can instead hold references to the strings, or compress/hash/etc. them?
My question is whether, due to the way that strings act like value types, that will end up duplicating the strings to hold in the dictionary?
Strings in C# are not value types, and they are most certainly do not act like ones.
C# strings are immutable, which makes them suitable for use as keys in associative containers. However, using strings as keys, or in any other capacity for that matter, does not result in cloning of their content.
You can verify that no cloning is going on by checking for reference equality of your dictionary keys to SomeStringProperty of your source array. Each key in the dictionary will be present in the source array:
var data = new[] {
new Something {SomeIntProperty=1, SomeStringProperty="A"}
, new Something {SomeIntProperty=2, SomeStringProperty="A"}
, new Something {SomeIntProperty=3, SomeStringProperty="A"}
, new Something {SomeIntProperty=4, SomeStringProperty="A"}
, new Something {SomeIntProperty=5, SomeStringProperty="A"}
, new Something {SomeIntProperty=6, SomeStringProperty="B"}
, new Something {SomeIntProperty=7, SomeStringProperty="B"}
, new Something {SomeIntProperty=8, SomeStringProperty="C"}
, new Something {SomeIntProperty=9, SomeStringProperty="D"}
};
var dict = data.GroupBy(s => s.SomeStringProperty)
.ToDictionary(g => g.Key);
foreach (var key in dict.Keys) {
if (data.Any(s => ReferenceEquals(s.SomeStringProperty, key))) {
Console.WriteLine("Key '{0}' is present.", key);
} else {
Console.WriteLine("Key '{0}' is not present.", key);
}
}
The above code prints
Key 'A' is present.
Key 'B' is present.
Key 'C' is present.
Key 'D' is present.
Demo.
due to the way that strings act like value types
strings are not value type, they are immutable reference types.
that will end up duplicating the strings to hold in the dictionary
wrong, you will only end up creating new string if you try to modify them. then a new string with new content will be created.
The documentation describes passing String variables by value, which is misleading in this case because, although the string is immutable, the runtime maintains a reference to the original value until we change it.
So, even though the Linq ToDictionary() method passes the string as an argument to Dictionary.Add() under the hood, both SomeStringProperty and the Dictonary key point to the same location in memory.
However, if we were to change the string in the key selector:
.ToDictionary(g => g.Key + "changed!");
...then the runtime will copy the original string value to create the new key.
We can verify that the reference is the same:
var first = this.dict.First();
Console.WriteLine(object.ReferenceEquals(first.Key, first.Value.SomeStringProperty));
This article does a great job describing the nuances of String objects in C#.

How do I make a generated Object name?

How do I make a generated Object name? For example:
ObjectEx "name" = new ObjectEx();
Edit:
The object will be named by a user input.
The code will be:
Console.Write("Input new user's name: ");
string newUsersName = Console.ReadLine();
(Create ObjectEx)
Edit2:
I have a Dictionary for ObjectEx(Person) which handles all ObjectExs.
Person is the real class name, sorry about making the example object ObjectEx.
public static List<Person> persons = new List<Person>();
Objects don't have names - variables do, and they're always determined at compile-time.
If you want a map from string to object, just use a Dictionary<string, ObjectEx> - then come with random strings using Random. (There are plenty of examples of generating random strings on Stack Overflow.)
If you just want a collection of objects and you were using "random name" as a way of expressing that, use List<ObjectEx> - you don't need a name at all in that case.
If you need something else, please be more specific.
You can use array and store object in to that.
ObjectEx []arrObjectEx = new ObjectEx[10];
arrObjectEx[0] = new ObjectEx();
I would use list<T> (generic list) instead of array if the number of random elements are unknown.
List<ObjectEx> lstObjectEx = new List<ObjectEx>();
lstObjectEx.Add(new ObjectEx());
If randomly generated object need to be accessed uniquely then you can use dictionary. e.g
Dictionary<int, ObjectEx> dicObjectEx = new Dictionary<int, ObjectEx>();
dicObjectEx.Add(someUniqueNumber, new ObjectEx());
That is not possible but how about using a Dictionary. You can use a string value Add and Get hold of an Object you stored.
// somewhere near the start in your code initialize the dictionary
var dict = new Dictionary<string, Person>();
// later on you can dynamically add an Object to the Dictionary
// newUsersName is the so called Index
string newUsersName = Console.ReadLine();
dict.Add(newUsersName, new Person());
// if you need to get hold of that object again use the Index
// myObj is a Person type
var myObj = dict[newUsersName];
// assume Person has an Age property
myObj.Age = 20;
// show all Persons now in the dictionary
foreach(var username in dict.Keys)
{
Console.WriteLine(username);
var pers = dict[username];
Console.WriteLine("{0} is {1} years old", username, pers.Age );
}
You could use a dictionary to store objects, where the Key is the object name

How to use TreeView.Tag = object?

Something like this:
Dictionary<int, string> myData = new Dictionary<int, string>();
myData.Add(1, "England");
myData.Add(2, "Canada");
myData.Add(3, "Australia");
myTreeView.Node[0].Tag = myData;
Then I want to get this object, how should I do it ?
Like:
string str = new string();
str = myTreeView.Node[0].Tag[2]; // "str" should be equal to "Canada"
myTreeView.Node[0].Tag[1] = "Spain";
str = myTreeView.Node[0].Tag[1]; // now "str" is equal to "Spain"
Second question - what will return this expression:
Dictionary<int, string> myData = new Dictionary<int, string>();
myData.Add(1, "England");
myData.Add(2, "Canada");
myData.Add(3, "Australia");
string str1 = new string();
str = myData[4]; // there isn't such a key as 4
Exception or null ?
Control.Tag is typed as object so you'll need to cast it to access it as a Dictionary<int, string>:
Dictionary<int, string> dict = (Dictionary<int, string>)myTreeView.Node[0].Tag;
string str = dict[2];
And similarly to set a value:
var dict = (Dictionary<int, string>)myTreeView.Node[0].Tag;
dict[1] = "Spain";
If you try to access a non-existent key, a KeyNotFoundException will be thrown. You can check if the dictionary contains a given key using TryGetValue or ContainsKey:
if(dict.ContainsKey(key))
{
var value = dict[key];
}
else
{
}
TryGetValue does the lookup and sets the given variable to the value (it it exists) in a single call, so is usually preferred.
string value;
if(dict.TryGetValue(key, out value))
{
//use value
}
else { ... }
Exception or null ?
Well, you could just try it out yourself or read the documentation (it throws an exception).
Also, your code will not compile as posted. Tag is a way to associate arbitrary data with an object. As a result, its type is object, so you will need to cast it before using it as you do.
If your code ever becomes unwieldy due to overuse of the Tag property I would suggest creating your own node type which inherits from the default TreeNode (or whatever it is) and populating your tree view with that type of object instead.
The tightest means for addressing the properties of a node tag is:
((TagType)node_identity.Tag).TagProperty = value;
Thus the tag properties of the parent node can be addressed so:
((TagType)node_identity.Parent.Tag).TagProperty = value;
The tag properties of a child node can be addressed so:
((TagType)node_identity.Nodes[index_of_child].Tag).TagProperty = value;
And as each Nodes[index_identifier] resolves to a node, the tag properties of more remote child nodes can be addressed so:
((TagType)node_identity.Nodes[index_of_child].Nodes[index_of_further_child].Tag).TagProperty = value;
Etc.
As far as the assumable objects of Tagging your dictionary are concerned... assuming your purposes require the same integer-string associations everywhere, I would instead simply store the integer key value to your Tag field, and pull the associated string from perhaps a global List<string> DictionaryStrings object. Then you could store only the integer index value to your Tags; and only the string values to your List<string>.
You would then read your Tag values as follows:
((int)node_identity.Tag)
Given these assumptions, you would instead populate your infrastructure so:
List<string> DictionaryStrings = new List<string>;
DictionaryStrings.Add("Canada"); // etc.
Then you would read the tag to pull your DictionaryString from the index value stored to your Tags as follows:
string DictionaryString = DictionaryStrings[((int)node_identity.Tag)];
Your error message is a consequence of addressing a prospective member of your dictionary which you have not yet added (at that execution point) to your dictionary. Fully populate the List or dictionary first; then only address valid indexes belonging to the List/dictionary.

How to get the key of a Hashtable entry

I've got a hashtable that I want to update from a second hashtable. For any of the keys that match I want to copy the value over. The problem I have is when I enumerate the hashtable keys and try to cast each to a string I receive an exception about casting a Guid to a String. Well it's the string I want. When you use the index operator with something like hashtable["FirstName"] then I expect FirstName to be the key. It might use Guids underneath I guess but I need to get out the string for the key, the key value.
private void UpdateSharePointFromInfoPath(Hashtable infopathFields)
{
// Go through all the fields on the infopath form
// Invalid Cast Exception Here
foreach (String fieldName in infopathFields.Keys)
{
// If the same field is on sharepoint
if (workflowProperties.Item.Fields.ContainsField(fieldName))
{
// Update the sharepoint field with the new value from infopath
workflowProperties.Item[fieldName] = infopathFields[fieldName];
}
}
// Commit the changes
workflowProperties.Item.Update();
}
EDIT
I don't create either of these hashtables. The keys have strings somewhere because I can put the field name in like the following and get the value of the field out. I'm trying to make a shorthand way of doing the following for every field:
workflowProperties.Item["FirstName"] = infopathFields["FirstName"];
workflowProperties.Item["LastName"] = infopathFields["LastName"];
workflowProperties.Item["Address"] = infopathFields["Address"];
workflowProperties.Item["DOB"] = infopathFields["DOB"];
ect...
EDIT
It's been said that the hashtable uses Guids, but it also obviously has a string inside else I wouldn't be able to do infopathFields["FirstName"]. It's the value on the string I pass in there that I want.
Every item is a Key/Value pair of format DictionaryEntry
foreach (DictionaryEntry de in infopathFields)
{
string fieldName = de.Key as string;
if (workflowProperties.Item.Fields.ContainsField(fieldName))
{
workflowProperties.Item[fieldName] = infopathFields[fieldName];
}
}
workflowProperties.Item.Update();
The standard version of the Hashtable can have different type keys, so most of your keys may be strings, but some of your keys may be GUIDs. I'm willing to bet that is the case and is causing your issue. The following little console app demonstrates the problem.
static void Main(string[] args)
{
System.Collections.Hashtable htable = new System.Collections.Hashtable();
htable.Add("MyName", "WindyCityEagle");
htable.Add("MyAddress", "Here");
htable.Add(new Guid(), "That Was My Guid");
int loopCount = 0;
foreach (string s in htable.Keys)
{
Console.WriteLine(loopCount++.ToString());
Console.WriteLine(htable[s]);
}
}
You'll get the exact same exception that you're reporting here.
My suggestion to fix the problem would be to go with the following
private void UpdateSharePointFromInfoPath(Hashtable infopathFields)
{
// Go through all the fields on the infopath form
// Invalid Cast Exception Here
foreach (object key in infopathFields.Keys)
{
string wfpKey = key.ToString();
// If the same field is on sharepoint
if (workflowProperties.Item.Fields.ContainsField(wfpKey))
{
// Update the sharepoint field with the new value from infopath
workflowProperties.Item[wfpKey] = infopathFields[key];
}
}
// Commit the changes
workflowProperties.Item.Update();
}
What creates the Hashtable? the key is actually an object so it sounds like whatever populated it has no implicit cast to a string
If the type of the values of infopathFields is a Guid then the types of the values of workflowProperties will have to be Guids. I can't see from the snippet what workflowProperties is defined as.
To convert a Guid to a string use Guid.ToString()
The objects stored in the hashtable are Guid objects, so to get a string you need to call ToString() on the object you get from the key enumerator. I would also recommend using the generic Dictionary<K,V> class instead of Hashtable, as that would catch problems like this at compile time rather than runtime.
To get largest integer key from Hash table:
public class Example
{
public void hashTableMethod()
{
Hashtable ht = new Hashtable();
ht.Add(5002894, "Hemant Kumar");
ht.Add(5002895, "Himanshee Ratnakar");
ht.Add(5002896, "Pooja Bhatnagar");
ht.Add(5002897, "Hina Saxena");
ht.Add(5002898, "Kanika Aneja");
ht.Add(5002899, "Hitesh Chaudhary");
Console.Write("\nNumber of Key-Value pair elements in HashTable are : {0}",ht.Count);
Console.WriteLine("Elements in HashTable are: ");
ICollection htkey = ht.Keys;
foreach (int key in htkey)
{
Console.WriteLine("{0}. {1}",key,ht[key]);
}
string ch="n";
do
{
Console.Write("\n\nEnter the name to check if it is exist or not, if not then it will add: ");
string newName=Console.ReadLine();
if(ht.ContainsValue(newName))
{
Console.Write("\nYour Name already Exist in the list!!");
}
else
{
Console.Write("\nSorry that name doesn't exist but it will be added!!");
int getKey = 0;
int[] htk= new int[ht.Count];
ht.Keys.CopyTo(htk,0);
string[] val=new string[ht.Count];
ht.Values.CopyTo(val,0);
Array.Sort(htk,val);
foreach (int id in htk)
{
getKey = id;
}
ht.Add(getKey+1,newName);
}
Console.Write("\nDo you want to search more??(y/n) :");
ch=Console.ReadLine();
}while(ch=="y"||ch=="Y");
Console.Write("\nNew List Items: \n");
ICollection htkeys = ht.Keys;
foreach (int key in htkeys)
{
Console.WriteLine("{0}. {1}",key,ht[key]);
}
}
}

Categories

Resources