Using custom object as dictionary key but get values by object property - c#

I have a class:
public class Foo
{
public string Name { get; set; }
public double Value { get; set; }
public override string ToString()
{
return Name;
}
}
And I need to create a dictionary where key is object of Foo. Like this:
Foo foo1 = new Foo { Name = "Foo1", Value = 2.2 };
Foo foo2 = new Foo { Name = "Foo2", Value = 3.6 };
Dictionary<Foo, int> dic = new Dictionary<Foo, int>();
dic.Add(foo1, 1234);
dic.Add(foo2, 2345);
And now I want to get values from dictionary by passing Foo.Name property as key. Like this:
int i=dic["Foo1"];
// i==1234
i = dic["Foo2"];
// i==2345
Is it possible? Or the only way to pass object of Foo as key and override Equals method?

If you use a Foo as a key, you will need to use a Foo to index the dictionary as well.
Provided that what you actually need is most likely a Dictionary<string, int>, you could try overriding GetHashCode and Equals so that you can compare Foo objects based on the name only:
using System.Collections.Generic;
public class Foo {
public string Name { get; set; }
public double Value { get; set; }
public override string ToString() {
return Name;
}
public override int GetHashCode() {
return Name.GetHashCode();
}
public override bool Equals(object obj) {
Foo other = obj as Foo;
if (other == null) {
return false;
}
return Name.Equals(other.Name);
}
}
class Program {
static void Main(string[] args) {
Foo foo1 = new Foo { Name = "Foo1", Value = 2.2 };
Foo foo2 = new Foo { Name = "Foo2", Value = 3.6 };
Dictionary<Foo, int> dic = new Dictionary<Foo, int>();
dic.Add(foo1, 1234);
dic.Add(foo2, 2345);
int i = dic[new Foo { Name = "Foo1" }];
}
}

How about this:
class Program
{
public class Foo
{
public string Name { get; set; }
public double Value { get; set; }
public override string ToString()
{
return Name;
}
}
static void Main(string[] args)
{
Foo foo1 = new Foo { Name = "Foo1", Value = 2.2 };
Foo foo2 = new Foo { Name = "Foo2", Value = 3.6 };
var dic = new Dictionary<string, KeyValuePair<Foo, int>>();
dic.Add(foo1.Name, new KeyValuePair<Foo, int>(foo1, 1234));
dic.Add(foo2.Name, new KeyValuePair<Foo, int>(foo2, 2345));
int x = dic["Foo1"].Value;
var y = dic["Foo2"].Value;
Console.WriteLine(x);
Console.WriteLine(y);
Console.ReadKey();
}
}
Result will be:
1234
2345

The Dictionary is based on a hash table, that means it uses a hash lookup, which is a rather efficient algorithm to look up things. I suggest you think on your design and just use Dictionary<string,int>if you just want to use string as key
As a work around( not preferred way use only if you can't change your design), you could do this.
var key = dic.Keys.FirstOrDefault(c=>c.Name == "Foo1");
if(key != null)
dic[key]; //

Ok. I solved it by creating a class inherits Dictionary and overriding get value method. This solves my problem but I'm not sure about productivity with large collections.
public class MyDictionary:Dictionary<Foo, int>
{
public Bar():base()
{
}
new public int this[string key]
{
get
{
return this[base.Keys.Single(a => a.Name == key)];
}
}
}
And then this code works well:
MyDictionary<Foo, int> dic = new MyDictionary<Foo, int>();
dic.Add(foo1, 1234);
dic.Add(foo2, 2345);
int i=dic["Foo1"];

Related

C# - how to write a collection (Dictionary?) with one key and two values [duplicate]

This question already has answers here:
C# Dictionary with two Values per Key?
(10 answers)
Closed 6 years ago.
What's the easiest way to create a collection of key, value1, value2 ?
Important to me is that it is easy & short to retrieve either value1 or value2 given the pair (please show an example)
I know I can do this -
class MyObject
{
internal string NameA { get; set; }
internal string NameB { get; set; }
}
class Other
{
Dictionary<string, MyObject> MyObjectCollection { get; set; }
private void function()
{
MyObjectCollection = new Dictionary<string, MyObject>()
{ { "key", new MyObject { NameA = "something", NameB = "something else" } } };
var valueA = MyObjectCollection["key"].NameA; // = "something"
}
}
Is there a better way?
The solution which is easiest to implement (Dictionary<String, Tuple<String, String>>) is not the one which is easiest to support and develop (what is MyObjectCollection[key].Value2?). So I suggest using your own class:
class MyPair {
public string NameA { get; internal set; }
public string NameB { get; internal set; }
public MyPair(nameA, nameB) {
NameA = nameA;
NameB = nameB;
}
public override String ToString() {
return $"NameA = {NameA}; NameB = {NameB}";
}
}
class Other {
// you don't want "set" here
// = new ... <- C# 6.0 feature
public Dictionary<string, MyPair> Data { get; } = new Dictionary<string, MyPair>() {
{"key", new MyPair("something", "something else")},
};
...
Other myOther = new Other();
String test = myOther.Data["key"].NameA;
You can use a Tuple:
Dictionary<string,Tuple<string,string>> MyObjectCollection {get; set;}
private void function()
{
MyObjectCollection = new Dictionary<string, Tuple<string,string>>();
MyObjectCollection.Add("key1", Tuple.Create("test1", "test2"));
}
List<MyObject> list = new List<MyObject>();
var res = list.ToDictionary(x => x, x => string.Format("Val: {0}", x));
The first lambda lets you pick the key, the second one picks the value.

Generic Dictionary retrieve value

I have the following C# class:
class BatchData
{
public string batchNumber { get; set; }
public string processDate { get; set; }
public int TotalRecords { get; set; }
public int SuccessCount { get; set; }
}
and a dictionary:
Dictionary<int, BatchData> BatchData = new Dictionary<int, BatchData>();
Now, I want to search the whole dictionary to see if the value
x
is held in:
BatchData.batchNumber
eg
for the whole dictionary, if
BatchData.batchNumber = x
I know Dictionary has a method
.contains
But I am not sure how I can apply this.
EDIT:
BatchData.batchNumber = x
Can occur multiple times within the dictionary
You could do this:
BatchData.Values.Any(x=>x.batchNumber == "x");
For example:
Dictionary<int, BatchData> BatchData = new Dictionary<int, BatchData>();
BatchData.Add(1, new BatchData { batchNumber = "x"});
var hasX = BatchData.Values.Any(x=>x.batchNumber == "x"); //true;
A dictionary is a collection of KeyValuePair objects, each of which has a Key property (an int in your case), and a Value property (a BatchData object).
There could be multiple entries with that batch number. If you just want to see if any key contains that number you can use
batchData.Any(kvp => kvp.Value.batchNumber == x);
If you want all key-value pairs with that batch number, change to Where:
batchData.Where(kvp => kvp.Value.batchNumber == x);
You can also use First, Single, etc. as appropriate.
You should use batchNumber as the key to your dictionary:
Dictionary<string, BatchData> BatchData = new Dictionary<string, BatchData>();
BatchValues.Add(batch1.batchNumber, batch1);
BatchValues.Add(batch2.batchNumber, batch2);
BatchValues.Add(batch3.batchNumber, batch3);
...
Then checking for existence is an O(1) operation (link):
BatchValues.ContainsKey(batchNumber);
You can use another one solution by Contains method from System.Linq.
First, you need to implement IEqualityComparer<> interface
public class BatchDataComparer : IEqualityComparer<KeyValuePair<int, BatchData>>
{
public bool Equals(KeyValuePair<int, BatchData> x, KeyValuePair<int, BatchData> y)
{
return (x.Value.batchNumber == y.Value.batchNumber);
}
public int GetHashCode(KeyValuePair<int, BatchData> obj)
{
//or something else what you need
return obj.Value.batchNumber.GetHashCode();
}
}
After that, you can get value from Dictionary like this:
private static void Main(string[] args)
{
Dictionary<int, BatchData> dic = new Dictionary<int, BatchData>();
dic.Add(1, new BatchData() { batchNumber = "x" });
dic.Add(2, new BatchData() { batchNumber = "y" });
dic.Add(3, new BatchData() { batchNumber = "z" });
bool contain = dic.Contains(new KeyValuePair<int, BatchData>(100, new BatchData()
{
batchNumber = "z"
}), new BatchDataComparer());
Console.ReadKey();
}
public class BatchData
{
public string batchNumber { get; set; }
public string processDate { get; set; }
public int TotalRecords { get; set; }
public int SuccessCount { get; set; }
}

Create a dictionary of a model?

It's quite hard for me to explain this, but I will give it a go.
Objective:
Create a LINQ query that will return a dictionary of data. However it must be a dictionary of the model which I am using.
View Model:
public class ValueBySupplierAndClaimTypeViewModel : ReportViewModel
{
public IQueryable<ValueBySupplierAndClaimTypeModel> ReportData {get; set; }
public TotalValueBySupplierAndClaimTypeModel ReportTotalData { get; set; }
public Dictionary<string, decimal> DictionaryData { get; set; }
public string output { get; set; }
}
Interface:
Dictionary<string, decimal> DictData;
TotalValueBySupplierAndClaimTypeModel GetTotalValueBySupplierAndClaimType(
int ClientID, int ReviewPeriodID, int StatusCategoryID);
SQL Repository:
public TotalValueBySupplierAndClaimTypeModel GetTotalValueBySupplierAndClaimType(int ClientID, int ReviewPeriodID, int StatusCategoryID)
{
var rt =
this.GetValueBySupplierAndClaimType(ClientID, ReviewPeriodID, StatusCategoryID);
TotalValueBySupplierAndClaimTypeModel x = new TotalValueBySupplierAndClaimTypeModel()
{
NormalTotal = rt.Sum(c=>c.Normal) ?? 0,
QueryTotal = rt.Sum( c => c.Query) ?? 0,
StrongTotal = rt.Sum( c => c.Strong) ?? 0
};
return x;
}
I'm really not sure how to do this. Can anybody help?
I have this function that converts an object to a dictionary. It gets all the properties of the class, as the dictionary's keys. May be you can modify it to meet your needs:
public Dictionary<string, object> ConvertClassToDict(object classToConvert)
{
Dictionary<string, object> result = new Dictionary<string, object>();
PropertyInfo[] properties = classToConvert.GetType().GetProperties();
List<string> propertiesNames = properties.Select(p => p.Name).ToList();
foreach (var propName in propertiesNames)
{
PropertyInfo property = properties.First(srcProp => srcProp.Name == propName);
var value = property.GetValue(classToConvert, null);
result.Add(propName, value);
}
return result;
}
The argument classToConvert, is just an instance of any class.
Similar to #lukiller's answer, but with LINQ:
public Dictionary<string, object> MapToDictionary(object instance)
{
if(instance == null) return null;
return instance.GetType()
.GetProperties()
.ToDictionary(p => p.Name,
p => p.GetValue(instance));
}
For example, let's suppose we have the following class:
public class User
{
public string Username { get; set; }
public string Password { get; set; }
}
We can print it like this (one line):
MapToDictionary(new User()
{
Username = "mcicero",
Password = "abc123"
}).ToList().ForEach(i => Console.WriteLine("{0}: {1}", i.Key, i.Value));
This prints out:
Username: mcicero
Password: abc123

Need explanation for some code.

What does
public object this[string name]
do
class ObjectWithProperties
{
Dictionary<string, object> properties = new Dictionary<string, object>();
public object this[string name]
{
get
{
if (properties.ContainsKey(name))
{
return properties[name];
}
return null;
}
set
{
properties[name] = value;
}
}
}
You will be able to reference the values in your dictionary directly from your object using indexes (ie, no property name)
In your case it would be
var foo = new ObjectWithProperties();
foo["bar"] = 1;
foo["kwyjibo"] = "Hello world!"
// And you can retrieve them in the same manner...
var x = foo["bar"]; // returns 1
MSDN guide: http://msdn.microsoft.com/en-gb/library/2549tw02.aspx
Basic tutorial: http://www.tutorialspoint.com/csharp/csharp_indexers.htm
Edit to answer question in comment:
This is equivalent to doing something like the following:
class ObjectWithProperties
{
public Dictionary<string, object> Properties { get; set; }
public ObjectWithProperties()
{
Properties = new Dictionary<string, object>();
}
}
// instantiate in your other class / app / whatever
var objWithProperties = new ObjectWithProperties();
// set
objWithProperties.Properties["foo"] = "bar";
// get
var myFooObj = objWithProperties.Properties["foo"]; // myFooObj = "bar"

How to get class and property names and values from undeclared type

If I have these two classes:
public class A
{
public int Id { get; set; }
}
public class B
{
public string Name { get; set; }
}
Can I use a generic method like this:
public void InitMethod(object classProperty)
To pass in data like this:
var a = new A() { Id = 1 };
var b = new B() { Name = "John" };
InitMethod(a.Id);
InitMethod(b.Name);
And get the following information from within the method:
Class name (ex: "A", "B")
Property name (ex: "Id", "Name")
Property value (ex: 1, "John")
Sort of, although it may be more trouble than it is worth.
ASP.Net MVC frequently uses expressions to get at property info in a strongly-typed fashion. The expression doesn't necessarily get evaluated; instead, it is parsed for its metadata.
This isn't specific to MVC; I mention it to cite an established pattern in a Microsoft framework.
Here's a sample that gets a property name and value from an expression:
// the type being evaluated
public class Foo
{
public string Bar {
get;
set;
}
}
// method in an evaluator class
public TProperty EvaluateProperty<TProperty>( Expression<Func<Foo, TProperty>> expression ) {
string propertyToGetName = ( (MemberExpression)expression.Body ).Member.Name;
// do something with the property name
// and/or evaluate the expression and get the value of the property
return expression.Compile()( null );
}
You call it like this (note the expressions being passed):
var foo = new Foo { Bar = "baz" };
string val = EvaluateProperty( o => foo.Bar );
foo = new Foo { Bar = "123456" };
val = EvaluateProperty( o => foo.Bar );
In this example you need to pass object to InitMethod not property of that object, maybe it will be OK.
class Program
{
static void Main(string[] args)
{
InitMethod(new A() { Id = 100 });
InitMethod(new B() { Name = "Test Name" });
Console.ReadLine();
}
public static void InitMethod(object obj)
{
if (obj != null)
{
Console.WriteLine("Class {0}", obj.GetType().Name);
foreach (var p in obj.GetType().GetProperties())
{
Console.WriteLine("Property {0} type {1} value {2}", p.Name, p.GetValue(obj, null).GetType().Name, p.GetValue(obj, null));
}
}
}
}

Categories