C# / MongoDB - Serialize Enum Dictionary Keys to String - c#

I am attempting to serialize a dictionary of dictionaries where the parent dictionary has its keys as type enum and child dictionary has keys of type DateTime. While attempting to insert into my collection I am met with
When using DictionaryRepresentation.Document key values must serialize as strings
I have read forums discussing serialization of enum into string, however with the model definitions that are currently in place I'm not sure how to go about with this method.
The two dictionary models that are currently in use are simply implementations of the Dictionary class:
IndexValues
{
public class IndexValues : Dictionary<Index, DateDictionary> { }
}
DateDictionary
public class DateDictionary : Dictionary<DateTime, double>
{
public double AsOf(DateTime d)
{
DateTime date = d;
while (date >= Keys.Min())
{
if (TryGetValue(date, out var value))
{
return value;
}
date = date.AddDays(-1);
}
throw new Exception($"The dictionary doesn't have a value for any date less than or equal to {d}.");
}
}
Index
public enum Index
{
SPX,
NDX
}
I am adding values to the dictionary in my main program by simply instantiating new instances of both classes and adding the values in the required types.
IndexValues indexRecords = new IndexValues();
...
var enumHead = (Index)Enum.Parse(typeof(Index), header[l]); // header is simply a list of strings
...
DateDictionary dateDict = new DateDictionary();
var date = Convert.ToDateTime(dataLine[j]); // dataLine is simply a list of strings
var value = Convert.ToDouble(dataLine[k]);
if (indexRecords.ContainsKey(enumHead))
{
indexRecords[enumHead].Add(date, value);
}
else
{
dateDict.Add(date, value);
indexRecords.Add(enumHead, dateDict);
}
I have attempted defining the key and value within the model definitions and using both [BsonRepresentation(BsonType.String)] for the enum and DateTime values and [BsonDictionaryOptions(DictionaryRepresentation.Document)] for the DateDictionary, but am still met with the same issue.
What am I missing in this instance, and what would be the correct direction to look towards? For reference, I am using the C# driver v2.8.1.

It turns out I was needing two serializers instead of one. I defined these globally and I was able to insert without an issue.
BsonSerializer.RegisterSerializer(new EnumSerializer<Index>(BsonType.String));
BsonSerializer.RegisterSerializer(new DateTimeSerializer(DateTimeKind.Local, BsonType.String));

Related

How to create the base class of the generic type dictionary to store it in a list?

I have a lot of methods which return lists of data sets which are represented as the key (DateTime) and value (some ValueType or string, I can't know it) pair, and this data sets may have size for about 10 000 items and more. For example:
public List<DataSet> GetAnyData(...)
{
var resultList = new List<DataSet>(); // Create the list for the base class
resultList.Add(new DoubleDataSet()); // This classes will be filled in this method
resultList.Add(new StringDataSet()); // See the description for them below
return resultList;
}
Example of the result list from this method:
ListItem1 ListItem2
Date1 Values1 Date2 Values2
01:00 10.5 02:00 "a"
01:15 20.42 02:15 "b"
01:30 30.01 02:30 "c"
After receiving this data sets I need to process them by different ways (using LINQ). And my problem is: how to create the DataSet class?
I tryed a lot of solutions:
1) To Inherit the DataSet class from Dictionary using generic types:
abstract class DataSet<T> : Dictionary<DateTime, T> {...}
and to create specific classes:
class DoubleDataSet : DataSet<double> {...}
class StringDataSet : DataSet<string> {...}
But the problem is: how to create the base class which I could store in the List of data sets?
2) Create the class of the DataItem of the DataSet and use it in the first solution:
class DataItem
{
public double DoubleValue { get; set; }
public string StringValue { get; set; }
// etc
}
class DataSet : Dictionary<DateTime, DataItem> {...}
But the problem is: how to know what property of the DataItem I need to take?
3) Store data in the object class:
public class DataSet : Dictionary<DateTime, object> {...}
public class DoubleDataSet : DataSet
{
public new double this[DateTime key]
{
get { return base[key] as double; }
set { base[key] = value; }
}
}
But the problem is: what about boxing and unboxing? If I will have 50 000 values and every value will be converted to object and back to the required type it will spent a lot of time. Also using LINQ the type of DataSet value will be an object and I will need to know what type is it.
4) Store the dictionary with the data inside the DataSet class:
public class DataSet
{
public Dictionary<DateTime, DataItem> Items {get; set;}
}
public class DoubleDataSet : DataSet
{
public new Dictionary<DateTime, double> Items
{
get { return base.Items // It wouldn't work because there a new dictionary is created
.ToDictionary(q => q.Key, q => q.Value.DoubleValue); }
set { base.Items = value
.ToDictionary(q => q.Key, q => new DataItem(q.Value, null)); }
}
}
But the problem is: get { return base.Items.ToDictionary(q => q.Key, q => q.Value.DoubleValue); } returns a new instance of the Items dictionary and I can't modify the original list.
So help me please how to create the base class of the generic type dictionary to store it in a list?
I will be very grateful for any help! I know that there is an error of designing (or coding) and I will be glad if anyone will help me to find it! Is it even possible to solve this problem?
If I understood you correctly, you need to store and process objects of different types in your list, but at the processing time, you don't know on which type of object you are working on.
One thing you could try: Since your DataItem class knows what type it is, why don't you let it decide how it gets processed by it implementing the visitor pattern?
To implement this, you would have to do the following:
First, implement your different data items:
// base class you can use in dictionaries
// note that is does not store any data type
class DataItem
{
// method called from outside
// for processing the item
public abstract void GetProcessed(Processor p);
}
// DataItem which stores a double
class DoubleDataItem : DataItem
{
public double Data{get;set;}
public override void GetProcessed(Processor p)
{
p.Process(this);
}
}
// DataItem which stores a string
class StringDataItem : DataItem
{
public string Data {get;set}
public override void GetProcessed(Processor p)
{
p.Process(this);
}
}
Now the information, which type of data is used is only stored in the relevant class. Every class can also retrieve a processor (or a visitor). This processor/visitor can then have different implementations depending on the type it is operating on:
public class Processor
{
// do processing for double items
public void Process(DoubleDataItem d);
// do processing for string items
public void Process(StringDataItem d);
}
Now when you create your dictionary, the processor will execute the correct code depending on which item it is operating on:
var dictionary = new Dictionary<DateTime,DataItem>();
// fill dictionary with data item
var processor = new DoSomethingProcessor();
foreach (var entry in dictionary)
entry.Value.GetProcessed(processor);

How to convert an object of a class to an object of type T using C# generics?

I have some classes that are supposed to generate charts. I have three classes that use different type of data for the charts' series. I will provide just the properties of these classes, to avoid confusion :)
public class PointBase
{
public decimal X { get; set; }
}
public class Point : PointBase
{
public decimal Y { get; set; }
}
public class Bubble : Point
{
public decimal Size { get; set; }
}
These three classes are used as the basic piece of data in the series of the charts. The series of the charts are represented as Dictionary objects in other three classes (the chart ones). I won't provide code of the chart classes in order to focus on the specific problem. I will just provide the names of the classes:
CoordinateChart (using PointBase as a basic piece of data).
ScatterChart (using Point).
BubbleChart (using Bubble).
I have the following method that does exactly the same algorithm for all my chart classes. That's why I decided to make the method using generics.
public Dictionary<string, T[]> GenerateSeriesDataDictionary<T>(string[] series, string[] categories) where T : PointBase, new()
{
// Create a new Dictionary.
Dictionary<string, T[]> seriesData = new Dictionary<string, T[]>();
// Gets the number of categories and assigns the categoriesNumber variable with the returned value.
int categoriesNumber = categories.Length;
foreach (string serie in series)
{
Dictionary<string, T> categoriesData = (Dictionary<string, T>)GetCategoriesData(serie);
// Create a new array of Ts that will store the data for the current serie.
T[] dataArray = new T[categories.Length];
for (int i = 0; i < categoriesNumber; i++)
{
// If the serie contains a value for the current category, store it in the array.
// Otherwise, set the value of the current element of the array to the default one.
dataArray[i] = categoriesData.ContainsKey(categories[i]) ? categoriesData[categories[i]] : new T();
}
// Add the dataArray to the seriesData Dictionary.
seriesData.Add(serie, dataArray);
}
return seriesData;
}
So, the above method recieves two arrays of strings. The first one represents the names of the series, for example "Tokyo" and "London". The second one represents the names of the categories for the chart - for example, the names of the months - "January", "February", etc.
This method is supposed to create a Dictionary object, that uses the name of a serie as a key and a corresponding array of objects, representing the data for each category (in my case - each month). For example, a single key-value pair in the Dictionary should look something like this:
Key: "London"; Value: 17, 16, 20, 25 (let's say the data represents maximum temperature for each month).
I'm basically executing the same algorithm, whenever I'm creating a line, a scatter or a bubble chart (represented by the CoordinateChart, ScatterChart and BubbleChart classes). The only thing that is different is the type of the dictionary I'm generating.
CoordinateChart uses Dictionary(string, PointBase[])
ScatterChart uses Dicitonary(string, Point[])
BubbleChart uses Dictionary(string, Bubble[])
I replaced the '<' and '>' characters with '(' and ')', because they seem to disappear :)
That's why I decided to put the logic that is supposed to generate the Dictionary object in a separate method and then override this method in each of these classes.
My method looks like this:
public virtual Dictionary<string, PointBase> GetCategoriesData(string serie)
{
Dictionary<string, PointBase> categoriesData = sqlResultDataTable.AsEnumerable()
.Where(row => row["City"].ToString() == serie)
.Select(row => new KeyValuePair<string, PointBase>(row["Month"].ToString(), new PointBase(decimal.Parse(row["Temperature"].ToString()))))
.ToDictionary(item => item.Key, item => item.Value);
return categoriesData;
}
This method basically gets all the key-value pairs representing a "Month" and a "Temperature" value for a serie (for instance, "London").
So, I'm doing all this in order to have code reusability. Otherwise, I could do the same thing in all of my three classes and I could provide an individual implementation of the GenerateSeriesDataDictionary method in each one, but I don't think this is a good approach.
Everything is okay, but I get an Error on this line:
Dictionary<string, T> categoriesData = (Dictionary<string, T>)GetCategoriesData(serie);
The message of the error states: 'Cannot convert type Dictionary(string, PointBase) to Dictionary(string, T)'.
I replaced the '<' and '>' characters with '(' and ')', because they seem to disappear :)
What can I do?
Create a base class/interface with a generic type parameter.
public abstract class Chart<T> where T : PointBase, new()
{
public abstract IDictionary<string, T> GetCategoriesData(string serie);
public IDictionary<string, T[]> GenerateSeriesDataDictionary<T>(string[] series, string[] categories)
{
// Call GetCategoriesData, etc.
}
}
Then create derived classes with specific point types:
public class ScatterChart : Chart<Point>
{
public override IDictionary<string, Point> GetCategoriesData(string serie)
{
// Create new Point and set its properties.
// We know it is a Point because we specified it in the class generic type.
}
// ...
}
public class BubbleChart : Chart<Point>
{
public override IDictionary<string, Bubble> GetCategoriesData(string serie)
{
// Create new Bubble and set its properties.
// We know it is a Bubble because we specified it in the class generic type.
}
// ...
}
You cannot cast Dictionary<string, PointBase> to e.g. Dictionary<string, Bubble>, so that's why your code won't compile. They are not the same thing.
You either need to declare categoriesData as a Dictionary<string, PointBase>, or make the GetCategoriesData() method generic.
This is a problem of type variance, covariance to be more specific.
.Net let's you do a safe covariance only, otherwise it would mean
Dictionary<string, Bubble> bubbles = GetBubbles();
Dictionary<string, PointBase> points = bubbles;
points["London"] = new Point(); //crash, not a Bubble
Consider using IEnumerable that is a readonly interface with covariant generic type.

Can I access a class variable with another variable?

i want to do a class constructor that takes a dicionary as parameter and initialize all the class variables that are listed as key in the dictionary, after of course a type conversion:
public class User
{
public int id;
public string username;
public string password;
public string email;
public int mana_fire;
public int mana_water;
public int mana_earth;
public int mana_life;
public int mana_death;
public User ()
{
}
public User(Dictionary<string,string> dataArray){
FieldInfo[] classVariablesInfoList = typeof(User).GetFields();
for(int i = 0; i < classVariablesInfoList.Length; i++)
{
if(dataArray.ContainsKey(classVariablesInfoList[i].Name)){
//missing code here :)
//need something like classVariable= dataArray["classVariablesInfolist[i].name"]; ?
}
}
}
}
but i can't find out how to do this!
Can you please help? :)
You can use the SetValue frunction from reflection:
FieldInfo f = classVariablesInfoList[i];
if (f.ReflectedType == typeof(int))
{
f.SetValue(this, Convert.ToInt32(dataArray[f.Name]));
}
else
{
f.SetValue(this, dataArray[classVariablesInfoList[i].Name]);
}
But it is a really uncommon way to do this with a dictionary. You should considder accessing the fields directly or add parameters to the constructor for any field. And fields should never be public - use properties instead.
The following will work if Convert.ChangeType() is able to handle the conversion. There are a lot of problems waiting to occur, for example handling numbers or dates where the string representation depends on the locale. I would really suggest to use usual typed constructor parameters or standard (de)serialization mechanism if possible. Or at least use a dictionary containing objects instead of strings to get rid of the conversion, again if possible.
public User(Dictionary<String, String> data)
{
var fields = typeof(User).GetFields();
foreach (field in fields)
{
if (data.ContainsKey(field.Name))
{
var value = Convert.ChangeType(data[field.Name], field.MemberType);
field.SetValue(this, value);
}
}
}
I would like to separate your problem into two parts.
1. Applying conversion
The FieldInfo type present a FieldType property that is the actual type of the field, using this Type we can use the non-generic ChangeType method of System.Convert, this method will be able convert some types to others. Luckily it support String to Int.
Usage:
Convert.ChangeType(OLD_VALUE, TARGET_TYPE);
2. Setting the field
The field info class has a SetValue method (FieldInfo.SetValue), it has two parameters, the first one is the current (ie. this) instance (or the instance you wish to change). the second is the new value you wish to set.
Putting it all together
[...]
var fieldInfo = classVariablesInfoList[i];
var name = fieldInfo.Name;
var targetType = fieldInfo.Type;
var value = Convert.ChangeType(dataArray[name], targetType);
classVariablesInfoList[i].SetValue(this, value);

Fill struct with String[]?

I'm parsing a CSV file and placing the data in a struct. I'm using the TextFieldParser from this question and it's working like a charm except that it returns a String[]. Currently I have the ugly process of:
String[] row = parser.ReadFields();
DispatchCall call = new DispatchCall();
if (!int.TryParse(row[0], out call.AccountID)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
continue;
}
call.WorkOrder = row[1];
call.Description = row[2];
call.Date = row[3];
call.RequestedDate = row[4];
call.EstStartDate = row[5];
call.CustomerID = row[6];
call.CustomerName = row[7];
call.Caller = row[8];
call.EquipmentID = row[9];
call.Item = row[10];
call.TerritoryDesc = row[11];
call.Technician = row[12];
call.BillCode = row[13];
call.CallType = row[14];
call.Priority = row[15];
call.Status = row[16];
call.Comment = row[17];
call.Street = row[18];
call.City = row[19];
call.State = row[20];
call.Zip = row[21];
call.EquipRemarks = row[22];
call.Contact = row[23];
call.ContactPhone = row[24];
call.Lat = row[25];
call.Lon = row[26];
call.FlagColor = row[27];
call.TextColor = row[28];
call.MarkerName = row[29];
The struct consists of all those fields being Strings except for AccountID being an int. It annoys me that they're not strongly typed, but let's over look that for now. Given that parser.ReadFields() returns a String[] is there a more efficient way to fill a struct (possibly converting some values such as row[0] needing to become an int) with the values in the array?
**EDIT:**One restriction I forgot to mention that may impact what kind of solutions will work is that this struct is [Serializable] and will be sent Tcp somewhere else.
Your mileage may vary on whether it is a better solution, but you could use reflection and define an Attribute class that you use to mark your struct members with. The attribute would take the array index as an argument. Assigning the value from the right array element would then happen by using reflection.
You could define your attribute like this:
[AttributeUsage(AttributeTargets.Property)]
public sealed class ArrayStructFieldAttribute : Attribute
{
public ArrayStructFieldAttribute(int index)
{
this.index = index;
}
private readonly int index;
public int Index {
get {
return index;
}
}
}
This means the attribute can simply be used to associate an int value named Index with a property.
Then, you could mark your properties in the struct with that attribute (just some exemplary lines):
[ArrayStructField(1)]
public string WorkOrder { // ...
[ArrayStructField(19)]
public string City { // ...
The values could then be set with the Type object for your struct type (you can obtain it with the typeof operator):
foreach (PropertyInfo prop in structType.GetProperties()) {
ArrayStructFieldAttribute attr = prop.GetCustomAttributes(typeof(ArrayStructFieldAttribute), false).Cast<ArrayStructFieldAttribute>().FirstOrDefault();
if (attr != null) {
// we have found a property that you want to load from an array element!
if (prop.PropertyType == typeof(string)) {
// the property is a string property, no conversion required
prop.SetValue(boxedStruct, row[attr.Index]);
} else if (prop.PropertyType == typeof(int)) {
// the property is an int property, conversion required
int value;
if (!int.TryParse(row[attr.Index], out value)) {
Console.WriteLine("Invalid Row: " + parser.LineNumber);
} else {
prop.SetValue(boxedStruct, value);
}
}
}
}
This code iterates over all properties of your struct type. For each property, it checks for our custom attribute type defined above. If such an attribute is present, and if the property type is string or int, the value is copied from the respective array index.
I am checking for string and int properties as that's the two data types you mentioned in your question. even though you have only one particular index that contains an int value now, it's good for maintainability if this code is prepared to handle any index as a string or an int property.
Note that for a greater number of types to handle, I'd suggest not using a chain of if and else if, but rather a Dictionary<Type, Func<string, object>> that maps property types to conversion functions.
If you want to create something very flexible you can mark each property on DispatchCall using a custom attribute. Something like this:
class DispatchCall {
[CsvColumn(0)]
public Int32 AccountId { get; set; }
[CsvColumn(1)]
public String WorkOrder { get; set; }
[CsvColumn(3, Format = "yyyy-MM-dd")]
public DateTime Date { get; set; }
}
This allows you to associate each property with a column. For each row you can then iterate over all properties and by using the attribute you can assign the right value to the right property. You will have to do some type conversion from string to numbers, dates and perhaps enums. You can add extra properties to the attribute to assist you in that process. In the example I invented Format which should be used when a DateTime is parsed:
Object ParseValue(String value, TargetType targetType, String format) {
if (targetType == typeof(String))
return value;
if (targetType == typeof(Int32))
return Int32.Parse(value);
if (targetType == typeof(DateTime))
DateTime.ParseExact(value, format, CultureInfo.InvariantCulture);
...
}
Using TryParse methods in the above code can improve the error handling by allowing you to provide more context when an unparsable value is encountered.
Unfortunately, this approach is not very efficient because the reflection code will be executed for each row in your input file. If you want to make this more efficient you need to dynamically create a compiled method by reflecting once over DispatchCall that you then can apply on each row. It is possible but not particular easy.
How dependent are you on the library that you're using? I've found File Helpers to be quite useful for this sort of thing. Your code would look something like:
using FileHelpers;
// ...
[DelimitedRecord(",")]
class DispatchCall {
// Just make sure these are in order
public int AccountID { get; set; }
public string WorkOrder { get; set; }
public string Description { get; set; }
// ...
}
// And then to call the code
var engine = new FileHelperEngine(typeof(DispatchCall));
engine.Options.IgnoreFirstLines = 1; // If you have a header row
DispatchCall[] data = engine.ReadFile(FileName) as DispatchCall[];
You now have a DispatchCall array, and the engine did all the heavy lifting for you.
Use reflection as #Grozz suggested in the comment. Mark each property of the struct class with an attribute (ie [ColumnOrdinal] ) and then use this to map the information with the proper column. If you have double, decimal and so on as a target, you should also consider using Convert.ChangeType to proper convert in the target type. if you are not happy with the performances, you can enjoy create a DynamicMethod on the fly, more challenging, but really performant and beautiful. The challenge is to write the IL instruction in memory to do the "plumbing" you did by hand ( I usually create some example code, and then look inside it with IL spy as a starting point ). of course you will cache somewhere such dynamic methods so creating them is requested just once.
The first thing that comes to mind is to use reflection to iterate over the properties and match them up to the elements in the string[] based on an attribute value.
public struct DispatchCall
{
[MyAttribute(CsvIndex = 1)]
public string WorkOrder { get; set; }
}
MyAttribute would just be a custom attribute with an index that would match up to the field position in the CSV.
var row = parser.ReadFields();
for each property that has MyAttribute...
var indexAttrib = MyAttribute attached to property
property.Value = row[indexAttrib.Index]
next
(Pseudocode, obviously)
or
[StructLayout(LayoutKind.Sequential)] // keep fields in order
public strict DispatchCall
{
public string WorkOrder;
public string Description;
}
StructLayout will keep the struct fields in order, so you can iterate over them without having to explicitly specify a column number for each field. That can save some maintenance if you have a lot of fields.
Or, you could skip the process entirely, and store the field names in a dictionary:
var index = new Dictionary<int, string>();
/// populate index with row index : field name values, preferable from some sort of config file or database
index[0] = "WorkOrder";
index[1] = "Description";
...
var values = new Dictionary<string,object>();
for(var i=0;i<row.Length;i++)
{
values.Add(index[i],row[i]);
}
That's easier to load, but doesn't really take advantage of strong typing, which makes this less than ideal.
You could also generate a dynamic method or a T4 template. You could generate code from a mapping file in the format
0,WorkOrder
1,Description
...
load that, and generate a method that looks like this:
/// emit this
call.WorkOrder = row[0];
call.Description = row[1];
etc.
That approach is used in a few micro-ORMs floating around and seems to work pretty well.
Ideally, your CSV would include a row with field names that would make this a lot easier.
OR, yet another approach, use StructLayout along with a dynamic method to avoid having to keep a field:column_index mapping aside from the struct itself.
OR, create an enum
public enum FieldIndex
{
WorkOrder=0
,
Description // only have to specify explicit value for the first item in the enum
, /// ....
,
MAX /// useful for getting the maximum enum integer value
}
for(var i=0;i<FieldIndex.MAX;i++)
{
var fieldName = ((FieldIndex)i).ToString(); /// get string enum name
var value = row[i];
// use reflection to find the property/field FIELDNAME, and set it's value to VALUE.
}
if you are going for speed you could a brittle switch statement.
var columns = parser.ReadFields();
for (var i = 0; i < columns.Length; i++)
{
SetValue(call, i, columns[i]);
}
private static void SetValue(DispatchCall call, int column, string value)
{
switch column
{
case 0:
SetValue(ref call.AccountId, (value) => int.Parse, value);
return;
case 1:
SetValue(ref call.WorkOrder, (value) => value, value);
return;
...
default:
throw new UnexpectedColumnException();
}
}
private static void SetValue<T>(
ref T property,
Func<string, T> setter
value string)
{
property = setter(value);
}
Its a shame that TextFieldParser does not allow you to read one field at a time, then you could avoid building and indexing the columns array.

What I should use to store a list of assosiated values to be able to retrieve, modify and save one of the elements

I am new to C# and I do not know how to do this
I have a set of associated values
dateValue (string)
valueOne (decimal)
valueTwo (decimal)
I need to be able to pick up the values by a certain date (I could change the dateValue to datetime) and save all the values after modify the valueOne and valueTwo
Should I create a list of objects and loop all the list searching for the proper dateValue which valueOne and valueTwo I want to modify?
What will be the best solution
Create a class, instantiate all the values and then add them to a list?
How could I search for an specific date?
It sounds like a Dictionary may be what you are looking for. A dictionary is a means of storing a Key/value pair and allowing fast and easy lookup of items by key. The key will be dateValue. The Value of the dictionary might be a class that contains valueOne and valueTwo. For example:
var dictionary = new Dictionary<string, Values>();
//Add an item to the dictionary
dictionary.Add("thekey", new Values {ValueOne = 1, ValueTwo = 2});
//Get the item out of the dictionary by key
var values = dictionary["thekey"];
//Update the value of ValueOne for "thekey"
values.ValueOne = 7;
//Print the new value
Console.WriteLine(dictionary["thekey"].ValueOne);
And the class:
public class Values
{
public decimal ValueOne { get; set; }
public decimal ValueTwo { get; set; }
}
As an aside, why not storedateValue as a DateTime rather than a string? This allows you to to have access to various APIs for working with date and time, such as formatting it for display, arithmetic, etc.
In object-oriented programming, you should represent your associated values with a class:
public class MyAssociatedValues // come up with a better name
{
public DateTime Date{get;set;}
public decimal Value1{get;set;} // needs a better name
public decimal Value2{get;set;} // ditto
}
Once you are representing your collection as an IEnumerable<MyAssociatedValues> (this could be a List, an array, or a number of other structures that implement IEnumerable<>), you can easily create a Dictionary to key these values based on their date.
var valuesByDate = values.ToDictionary(v => v.Date);
For easy retrieval by dateValue you could either use a Dictionary with a Tuple containing the two values
Dictionary<string, Tuple<string, string>>
or a Lookup, which is basically a Dictionary that can store a collection of values per key.
I would use a Dictionary to map the DateTime to a structure that contains valueOne and valueTwo.
Something like:
struct Data
{
public decimal valueOne { get; set; }
public decimal valueTwo { get; set; }
}
Dictionary<DateTime, Data> map = new Dictionary<DateTime, Data>();
void addToMap(DateTime dt, decimal one, decimal two)
{
map[dt] = new Data() { valueOne = one, valueTwo = two };
}
Firstly, I would create a class (POCO) instead of a struct unless you have a good reason for a struct.
public class TheClass
{
public DateTime TheDate { get; set; }
public decimal ValueOne { get; set; }
public decimal ValueTwo { get; set; }
}
Secondly, I would store them in any data structure that implements IEnumerable (collection-type) that makes sense. The one that doesn't seem to make sense to me is the one that everyone is suggesting (Dictionary) because it is very much possible that you have 2 objects with the same datetime, so why everyone is suggesting a dictionary baffles me. The only thing I can think of is that they are assuming you only have one pair of values per datetime.
Thirdly, use LINQ to enumerate over the sub-list based on your query.
foreach (var obj in myCollection.Where(item => item.TheDate == requestedDate))
{
//Do whatever you need to do to each enumerated item.
}
Alternatively, if you really want to use the Dictionary instead of LINQ you could create an IDictionary<DateTime, IEnumerable<TheClass>> which means you are caching the collection as the value in the Dictionary.
foreach (var obj in myDictionary[requestedDate])
{
//Do whatever you need to do to each enumerated item.
}

Categories