Get dictionary key object properties - c#

I am trying to retrieve a dictionary's key property, as the key is a class. How do you do it? Here is the class that I use:
public class Item
{
public int Sku { get; set; }
public string Name { get; set; }
public Item()
{
}
}
I am trying to retrieve a property of it for example Name:
Dictionary<Item,double> myDictionary = new Dictionary<Item,double>();
Item item = new Item { Sku = 123, Name = "myItem" };
myDictionary.Add(item,10.5);
So now for example how from this dictionary I would retrieve the item's Name or Sku, or any other property if it would have them?

First, you have to override GetHashCode and Equals if you want to use your class as key of a Dictionary, otherwise you're comparing references.
Here's an example where Equals checks if two items have the same Name.
public class Item
{
public override int GetHashCode()
{
return Name == null ? 0 : Name.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if(object.ReferenceEquals(this, obj)) return true;
Item i2 = obj as Item;
if(i2 == null) return false;
return StringComparer.CurrentCulture.Equals(Name, i2.Name);
}
// rest of class ...
}
But the question is not clear. You use a dictionary to lookup elements by the key. So you want to find the value by providing the key. That means you have already a key which makes your question pointless.
However, you can loop a dictionary even if it's not made for this:
foreach(var kv in mydictronary)
{
Item i = kv.Key;
// now you have all properties of it
}

To retrieve your item, you need to use the same item (the same reference). You can do it in such way:
var myDouble = myDictonary[item];
When you use object as a key in a directory, its hash code is use to add/retrieve item from it - you can read more here
If you want use a string to retrieve items, then you should use strings as a key in you dictionary:
Dictonary<string,double> mydictronary = new Dictonary<string,double>();

you can iterate of the dictionary like this:
foreach(var keyValuePair in myDictionary)
{
kvp.Key.
}
then youll get all of the properties

You can use linq:
var item = myDictionary.Where(x => x.Key.Name == "myItem");
var item = myDictionary.Where(x => x.Key.Sku == 123);

You have three options.
You can use the same instance of the class to index, as in var x = myDictionary[item].
You can implementa customer comparer (something that implements IEqualityComparer<Item>), and pass that in to the constructor of your dictionary. For details see MSDN.
You can implement IEquatable<Item> on your Item class. For details see IEquatable on MSDN.

You can access keys from the Dictionary<TKey, TValue>.Keys property.
From MSDN
// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl = openWith.Keys;
// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
Console.WriteLine("Key = {0}", s);
}

Related

Find object from collection of object

I have a class
public class ABC
{
public int Id { get; set; }
public string Name { get; set; }
public Enum Msg { get; set; }
}
and collection of this class and single object
List<ABC> objColl = new List<ABC>();
ABC obj = new ABC();
Assume collection have items and i am trying to find single object which already exists in collection.
i want to find a single object inside that collection whether it exists or not.
I had already tried
var res = objColl.contains(obj);
it always return false. i dont want compare each property of object or loop.
Use Any with your criteria:
bool res = objColl.Any(s => s.Id == obj.Id);
if you want to use Contains then override Equals().
When you call Contains(), it searches for an item in the collection that is equal to the argument you've provided. Since you have not overridden Equals(), it uses the default implementation.
You have two options:
Override Equals() in class ABC to specify checking only the properties you want to check;
Use LINQ: objColl.Any(e => e.[some property] == obj.[some property])
You can use FirstOrDefault()
Returns the first element of a sequence, or a default value if no
element is found.
var res = objColl.FirstOrDefault(x => x.Id == obj.Id);
var res = objColl.Where(s=>s.Id == obj.Id).Any();

Converting a distinct list to dictionary not working

I'm trying to convert a list of objects to a dictionary using the following code:
var MyDictionary = MyList.Distinct().ToDictionary(i => i.ObjectId, i => i);
I know that a dictionary should not contain duplicate elements, hence the .Distinct(). Yet I still get the following Exception whenever there's a duplicate element:
An item with the same key has already been added.
MyList is a list of MyObject that looks like this:
public class MyObject{
public string ObjectId { get; set; }
public string FName { get; set; }
public string LName { get; set; }
}
Is there a better way to create a dictionary from a list of objects ? or am I doing something wrong?
If you want to compare on the ObjectId, you'll need to pass in a custom comparer to .Distinct(). You can do so like this:
class MyObjectComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.ObjectId == y.ObjectId;
}
public int GetHashCode(MyObject obj)
{
return obj.ObjectId.GetHashCode();
}
}
var MyDictionary = MyList
.Distinct(new MyObjectComparer())
.ToDictionary(i => i.ObjectId, i => i);
You could use Group by and then select first from the List as below:
var MyDictionary = MyList.GroupBy(i => i.ObjectId, i => i).ToDictionary(i => i.Key, i => i.First());
Distinct works using the objects built in Equals and GetHashCode methods by default but your dictionary works only over the id. You need to pass in a IEqualityComparer in to distinct that does the comparison on Id to test if items are equal or make MyObject implment Equals and GetHashCode and have that compare on the Id.

Find an object within a list and replace its value

I have the following list and class:
List<MyClass> MyList
public class MyClass
{
public int id { get; set; }
public bool checked { get; set; }
}
I also have the two variables:
int idToFind = 1234;
bool newcheckedvalue = true;
What I need to do is search the list and find the MyClass object where the id equals that value of idToFind. Once I have the object in the list, I then want to change the value of the checked property in the class to that of the newcheckedvalue value.
LINQ seems to be the solution to this problem, I just can't get the expression right. Can I do this in a single LINQ expression?
LINQ is for querying collection, not for modification. You can find the object like:
var item = MyList.FirstOrDefault(r=> r.id == idtoFind && r.checked == newcheckedvalue);
To find the item based on the ID only you can do:
var item = MyList.FirstOrDefault(r=> r.id == idtoFind);
Later you can set/modify its property.
//Make sure to check against Null, as if item is not found FirstOrDefault will return null
item.checked = newcheckedvalue; //or any other value
Example (to be noted that MyClass type has to be a class, a reference type, in this example):
var found = MyList.Where(ml=>ml.Id == 1234);
foreach(var f in found)
f.checked = newcheckedvalue;

Can't remove item from Iesi.Collections.Generic ISet<T>

I have a collection of type:
Iesi.Collections.Generic
public ISet<ItemBinding> ItemBindings { get; set; }
where ItemBinding is Domain.Model
I initialize the collection in this way:
ItemBindings = new HashedSet<ItemBinding>();
and I fill the collection with members.
When i want to remove an item from this collection i can't remove it.
private void OnRemove(ItemBinding itemToRemove) {
ItemBindings.Remove(itemToRemove);
}
even the itemToRemove has the same hashCode as the item from the collection.
Also I tried in to find the item in collection, keep it in a variable, and remove it:
private void OnRemove(ItemBinding itemToRemove) {
var foundItem = ItemBindings.Single( x => x.Id == itemToRemove.Id); // always if found
ItemBindings.Remove(foundItem);
}
but this doesn't work.
An workaround which works ok is this:
private void OnRemove(ItemBinding itemToRemove) {
var backUpItems = new List<ItemBinding>(ItemBindings);
backUpItems.Remove(itemToRemove);
ItemBindings.Clear();
ItemBindings.AddAll(backUpItems);
}
but this is an dirty workaround. I'm trying to do this simple Remove in an elegant manner :).
CHANGE the TYPE
If I change the type from ISet in IList it works ok.
public IList<ItemBinding> ItemBindings { get; set; }
ItemBindings = new List<ItemBinding>();
When i want to remove an item from this collection IT IS REMOVED.
private void OnRemove(ItemBinding itemToRemove) {
ItemBindings.Remove(itemToRemove);
}
What i'm missing in the way that i can't remove items from ISet ... ?
Thank you for suggestions, solutions.
This is very simple problem. Just download dotPeek 1.2 and fire up symbol server & you can then check into the ACTUAL implementation of ISet.Remove() and see why it's being picky. As #HansPassant said, it's probably the case of GetHashCode(), or the actual implementation of HashedSet
As for my speculations; take a look at DictionarySet(base class of HashedSet):
https://www.symbolsource.org/Public/Metadata/Default/Project/NHibernate/3.0.0.Alpha2/Release/All/Iesi.Collections/Iesi.Collections/Generic/DictionarySet.cs
As you can see, Remove() uses Contains() to actually test if it should remove element or not. What does Contains() do? Basically it's wrapper around Dictionary.Contains()
http://msdn.microsoft.com/en-us/library/ms182358(v=vs.80).aspx
GetHashCode returns a value based on the current instance that is
suited for hashing algorithms and data structures such as a hash
table. Two objects that are the same type and are equal must return
the same hash code to ensure that instances of
System.Collections.HashTable and System.Collections.Generic.Dictionary
work correctly.
Notice it's important that:
1) Your GetHashCode() can't change. This means that all the fields which are used by GetHashCode() can't change. As soon as you insert element into Dictionary, its GetHashCode() will be called and it's being put to specific bucket. You can't recover it when you have different GetHashCode() later. After the GetHashCode() has passed, your Equals method will be called. Make sure your fields are immutable.
Relevant source from Dictionary:
private int FindEntry(TKey key)
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
if (this.buckets != null)
{
int num = this.comparer.GetHashCode(key) & int.MaxValue;
for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
{
if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key))
return index;
}
}
return -1;
}
See this thread how to override Equals() AND GetHashCode():
Why is it important to override GetHashCode when Equals method is overridden?
Notice the answer by #Albic, in that thread.
I cannot reproduce this behaviour.
private class ItemBinding
{
public string ID { get; set; }
}
[TestMethod]
public void TestMethod1()
{
System.Collections.Generic.HashSet<ItemBinding> set = new System.Collections.Generic.HashSet<ItemBinding>();
ItemBinding item1 = new ItemBinding() { ID = "Jaffa" };
set.Add(item1);
Assert.IsTrue(set.Count == 1);
set.Remove(item1);
Assert.IsTrue(set.Count == 0);
ItemBinding item2 = new ItemBinding() { ID = "Moon" };
set.Add(item2);
ItemBinding item3 = new ItemBinding() { ID = "Moon" };
Assert.IsTrue(item2.GetHashCode() != item3.GetHashCode());
Assert.IsTrue(set.Remove(item3) == false);
Assert.IsTrue(set.Count == 1);
}
The above test shows Hashset working as expected. Is is possible you are falling into the trap shown in the second test of comparing two instances of a class that have the same values, but are in fact different class instances (therefore fail the GetHashCode equality test ?).
If you can alter the posted code here to more accurately represent your particular problem, that would be helpful.
You can try using...
private void OnRemove(ItemBinding itemToRemove)
{
ItemBindings.RemoveWhere(x => x.Id == itemToRemove.Id);
}

How to iterate over this particular type of Dictionary<int, Result>?

I have a bunch of results from the Database that I keep in a Dictionary<int, Result>().
The Result class I have is:
public int Id { get; set; }
public string something { get; set; }
public string somethingtwo { get; set; }
public string somethingthree { get; set; }
public DateTime Posted { get; set; }
public string Location { get; set; }
public bool somethingfour { get; set; }
So, the Dictionary<int, Result> has many Results inside and I'd like to iterate over them. How an I do this? I've tried a few ways, but even I knew they wouldn't work.
I would like to do something like this:
foreach(var result in resultDict)
{
mynewstring = result.Location;
mynewstringtwo = result.Posted;
// etc etc.
}
foreach(var kvp in resultsDict) {
//Do somethign with kvp
UseResult(kvp.Value); //kvp.Value is a Result object
UseKey(kvp.Key); //kvp.Key is the integer key you access it with
}
In the above code kvp is a KeyValuePair<int, Result>. You can access the Key and Value properties to get the integer key of the Dictionary and the Result value associated with that Key. I'll leave it as an excercise/guessing game for you to figure out which property is which! ;-)
As #Wiktor mentions, you can also access the dictionary's Values and Keys collections to do the same thing, but retrieve a sequence of int or Value properties instead.
Alternatives using the other collections:
foreach(var val in resultsDict.Values) {
// The key is not accessible immediately,
// you have to examine the value to figure out the key
UseResult(val); //val is a value.
}
foreach(var key in resultsDict.Keys) {
//The value isn't directly accessible, but you can look it up.
UseResult(resultsDict[key]);
UseKey(key);
}
Dcitionary has a ValueCollection called Values, so the code would be:
foreach (Result r in dict.Values)
{
mynewstring = result.Location;
mynewstringtwo = result.Posted;
}
var dictionary = ...;
foreach ( var result in dictionary.Values )
{
...
}
Is that what you need?
You can use the Values property to iterate over the values of a Dictionary (see also the MSDN page).
Code example:
// Your dictionary
var myResultDictionary = new Dictionary<int, Result>();
foreach (var item in myResultDictionary.Values)
{
// "item" holds a Result object
// Do something with item
}
You can also regularly loop over your Dictionary, however item will be a KeyValuePair (MSDN page) object. You can access the value with the Value property on the variable item.
Code example:
// Your dictionary
var myResultDictionary = new Dictionary<int, Result>();
foreach (var item in myResultDictionary)
{
// "item" holds a KeyValuePair<int, Result> object
// You can access the value (a Result object) by calling "item.Value"
// Do something with item
}
You can iterate over Dictionary.Values, which would be like any other IEnumerable<Result>
Or, you can iterate over Dictionary, which is an IEnumerable<KeyValuePair<int, Result>>
foreach (KeyValuePair<int,Result> item in dictionary)
{
//do stuff
}
foreach(KeyValuePair<int,result> keyValue in yourDictionary)
{
Debug.WriteLine(keyValue.Value);
//or
Debug.WriteLine(keyValue.Key);
}
or the same thing, but using var:
foreach(var keyValue in yourDictionary)
{
Debug.WriteLine(keyValue.Value);
//or
Debug.WriteLine(keyValue.Key);
}
^^ same thing, but var dynamically figures out its own type
or you can do this, if you just need the result:
foreach(var result in yourDictionary.Values)
{
Debug.WriteLine(result);
}
Use a foreach on the Dictionary:
foreach (var keyValue in dictionary)
{
//Use key value here
}

Categories