I hope I can explain my problem in a way that it's clear for everyone.
We need your suggestions on this.
We have an Enum Type which has more than 15 constants defined.
We receive a report from a web service and translate its one column into this Enum type.
And based on what we receive from that web service, we run specific functions using
Dictionary
Why am I asking for ideas?
Let's say 3 of these Enum contants meet specific functions in our Dictionary but the rest use the same function. So, is there a way to add them into our Dictionary in a better way rather than adding them one by one? I also want to keep this structure because when it's time, we might have specific functions in the future for the ones that I described as "the rest".
To be more clear here's an example what we're trying to do:
Enum:
public enum Reason{
ReasonA,
ReasonB,
ReasonC,
ReasonD,
ReasonE,
ReasonF,
ReasonG,
ReasonH,
ReasonI,
ReasonJ,
ReasonK
}
Defining our Dictionary:
public Dictionary<Reason, Action<CustomClassObj, string>> ReasonHandlers = new Dictionary<Reason, Action<CustomClassObj, string>>{
{ Reason.ReasonA, HandleReasonA },
{ Reason.ReasonB, HandleReasonB },
{ Reason.ReasonC, HandleReasonC },
{ Reason.ReasonD, HandleReasonGeneral },
{ Reason.ReasonE, HandleReasonGeneral },
{ Reason.ReasonF, HandleReasonGeneral },
{ Reason.ReasonG, HandleReasonGeneral },
{ Reason.ReasonH, HandleReasonGeneral },
{ Reason.ReasonI, HandleReasonGeneral },
{ Reason.ReasonJ, HandleReasonGeneral },
{ Reason.ReasonK, HandleReasonGeneral }
};
So basically what I'm asking is, is there a way to add Reason, Function pair more intelligently? Because as you can see after ReasonC, all other reasons use the same function.
Thank you for your suggestions.
You could try something like this, only put the custom Reason handler in the dictionary and then fallback to the General one.
public Dictionary<Reason, Action<CustomClassObj, string>> ReasonHandlers = new Dictionary<Reason, Action<CustomClassObj, string>>{
{ Reason.ReasonA, HandleReasonA },
{ Reason.ReasonB, HandleReasonB },
{ Reason.ReasonC, HandleReasonC }};
public Action<CustomClassObj, string> ReasonHandlerLookup (Reason reason) {
Action<CustomClassObj, string> result = null;
ReasonHandlers.TryGetValue(reason, out result);
return result ?? HandleReasonGeneral;
}
Related
This question already has answers here:
C# dictionary - one key, many values
(15 answers)
Closed 12 days ago.
For some reason I cannot append a string variable to string[] value in Dictionary<string;string[]>
I am trying to make a Graph C# class for practice and i ran into a problem: I use Dictionary<string, string[]> graph to make a structure like this:
"Node1":[connection1,connection2,connection3]
"Node2":[connection1,connection2,connection3]
"Node3":[connection1,connection2,connection3]
...
I have a method to append a connections array value:
// from Graph class
private Dictionary<string, string[]> graph;
public void AddEdge(string NodeName, string EdgeName)
{
graph[NodeName].Append(EdgeName);
}
And use it like this:
//from Main
Graph g = new Graph();
string[] Nodes = { "node1", "node2", "node3" };
string[][] Edges = { new[] { "node1", "nodeToEdgeTo" }, new[] { "node2", "nodeToEdgeTo" } };
//nodeToEdgeTo are nodes like "node2" or "node3"
foreach (var i in Edges)
{
g.AddEdge(i[0], i[1]);
}
But as a result i get empty values for some reason:
"Node1":[]
"Node2":[]
"Node3":[]
I have no idea why
As people already said, an array (as in most languages) are not resizeables.
I think it's interesting to see how you instanciate an array in C to understand that.
In C either you instanciate like that
int[5] myArray
or you choose the dynamic way with malloc pretty much like so
int *myArray = malloc(5 *sizeof(int));
What you can see is that when you instanciate them you have to give it a size and it will "lock" a chunck of memory for it.
Now if you want to add more things to it you have to recreate a new one bigger and copy the content from one to another.
Lists work in a different way. each element of a list are alocated separatly and each of them contain a pointer to the next element.
That's why it's way easier to add an element to a list. What happens under the hood is that it creates that element and alocate memory for it and make the last (or first it depends) element of the list to it (you can see it as a chain)
It should remind you of what you're trying to achieve with your tree. Graphs and lists aren't much different but graph doesn't already exist int c# so you have to recreate it.
Simple answer
So the easy solution for you case is to swap the array by a list like so:
Dictionary<string, List<string>> graph
and then you use it like so
public void AddEdge(string NodeName, string EdgeName)
{
graph[NodeName].Add(EdgeName);
}
More complicated answer
So following what we said about list, we should assume that each element of your graph should reference other elements.
That will look like so:
using System.Text.Json;
var edge0 = new Edge() { WhatEverData = "level0 : edge0" }; //Your base node/edge
var edge1 = new Edge() { WhatEverData = "level1 : edge1" };
var edge2 = new Edge() { WhatEverData = "level1 : edge2" };
var edge3 = new Edge() { WhatEverData = "level1 : edge3", Edges = new List<Edge> { new Edge() { WhatEverData = "level2" } } };
edge0.AddEdge(edge1);
edge0.AddEdge(edge2);
edge0.AddEdge(edge3);
Console.WriteLine(JsonSerializer.Serialize(edge0));
public sealed class Edge
{
public string? WhatEverData { get; set; }
public List<Edge>? Edges { get; set; }
public Edge AddEdge(Edge toAdd)
{
Edges ??= new List<Edge>();
Edges.Add(toAdd);
return this;
}
}
In this example everything is an edge, Edge0 being the root of your graph but your root could also be a list of edge if you really want to.
The point is that now that it's an object you can put pretty much everything you want in there.
You can also easily serialize it! Here it's what the output of this tiny code looks like:
{
"WhatEverData": "level0 : edge0",
"Edges": [
{ "WhatEverData": "level1 : edge1", "Edges": null },
{ "WhatEverData": "level1 : edge2", "Edges": null },
{
"WhatEverData": "level1 : edge3",
"Edges": [{ "WhatEverData": "level2", "Edges": null }]
}
]
}
Hope it helped
Instead of an array, you might want a List isntead: Dictionary<string, List<string>>.
Then you could do this:
public void AddEdge(string NodeName, string EdgeName)
{
graph[NodeName].Add(EdgeName);
}
I am currently working on refactoring some code, where I have stumbled upon this static dictionary:
public static Dictionary<string, string> CountryNamesAndCodes()
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("AF", "Afghanistan");
dictionary.Add("AL", "Albania");
dictionary.Add("DZ", "Algeria");
dictionary.Add("AD", "Andorra");
dictionary.Add("AO", "Angola");
dictionary.Add("AG", "Antigua and Barbuda");
dictionary.Add("AR", "Argentina");
dictionary.Add("AM", "Armenia");
...
}
Which first of all is defined in the service layer, and takes up a lot a space - 400 lines, and eventhough it is static, it seem to always recreate the dictionary, meaning making the static part of it redundant - or am I wrong?
how do I ensure that this is only created once, and everytime I call it, it make use of the same instance.
You are quite right, you can extract the local dictionary as a static member
I suggest something like this (field):
// static readonly (we want to create it once) field of
// IReadOnlyDictionary type - we want to read key value pairs after its creation
private static readonly IReadOnlyDictionary<string, string> countries =
// We may want to be nice and let ignore case for keys
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
{ "AF", "Afghanistan" },
{ "AL", "Albania" },
{ "DZ", "Algeria" },
//TODO:Put all the other records here
};
or like this (property):
// static readonly (there's no "set") property of
// IReadOnlyDictionary type - we want just to read key value pairs after its creation
private static IReadOnlyDictionary<string, string> Countries { get; } =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
{ "AF", "Afghanistan" },
{ "AL", "Albania" },
{ "DZ", "Algeria" },
//TODO:Put all the other records here
};
public static class Globals
{
static Dictionary<string, string>
CountryNamesAndCodes = new Dictionary<string, string>
{
{"AF", "Afghanistan"},
{"AL", "Albania"}
};
}
name = Globals.CountryNamesAndCodes["AF"];
Using c#, nunit, selenium for automation. I would like to use attribute [Test, Pairwise] for my test case to verify that object can be posted with any valid value. I have dictionary with all valid values, but [Values()] - requires const as parameter and ReadOnlyCollection(as it was suggested here) doesn't work for it.
I'm having error: An attribute agrument must be a constant expressiom, typeof expression or array expression of an attribute parameter type.
class ObjectBaseCalls : ApiTestBase
{
static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });
[Test, Pairwise]
public void ObjectCanBePostedAndGeted([Values(AllTypes)] string type)
{
//My test
}
}
There are two different problems here.
The error "An attribute argument must be a constant expression, typeof expression or array expression of an attribute parameter type" is coming from the compiler. It describes a limitation of any attribute constructors in .NET, not just NUnit's. If you want to pass arguments in the constructor itself, then you must use constant values.
However, it seems you don't want to pass the args in the constructor. Instead, you would like to refer to a list declared separately. NUnit has a set of attributes for doing exactly that. You should use one of them, for example, ValueSourceAttribute...
public class ObjectBaseCalls : ApiTestBase
{
static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });
[Test]
public void ObjectCanBePostedAndGeted([ValueSource(nameof(AllTypes))] string type)
{
//My test
}
}
Alternatively, since you only have a single argument to the method, youcould use TestCaseSourceAttribute...
public class ObjectBaseCalls : ApiTestBase
{
static ReadOnlyCollection<string> AllTypes = new ReadOnlyCollection<string>(new List<string>() { "Value1", "Value 2" });
[TestCaseSource(nameof(AllTypes))]
public void ObjectCanBePostedAndGeted(string type)
{
//My test
}
}
Either of these should work. Which one you use here is a matter of stylistic preference.
The second problem is with your use of PairWiseAttribute. It is used (along with several other "combining attributes" when you have a test with multiple parameters specified using Values or ValueSource and you want NUnit to combine them in various ways. In the situation with a single parameter, it does nothing. That's why I removed it from my examples above.
If you actually intended to write a test with more than one parameter and have NUnit decide how to combine them, I think you need to ask a different question where you explain what you are trying to accomplish.
I found the solution and it works for me.
I created enum with parameter name and dictionary with parameter name and parameter value for each object parameter and use enum into my object constructor PairWiseAttribute.
public class MyElement : BaseElement
{
public enum Types { Type1, Type2, Type3, Type4}
public Dictionary<Types, string> AllTypes = new Dictionary<Types, string>()
{
{ Types.Type1, "Value 1" },
{ Types.Type2, "Value 2" },
{ Types.Type3, "Value 3" },
{ Types.Type4, "Value 4" },
};
public enum Category { Category1, Category2, Category3, Category4}
public Dictionary<Category, string> Categories = new Dictionary<Category, string>()
{
{ Category.Category1, "Value 1" },
{ Category.Category2, "Value 2" },
{ Category.Category3, "Value 3" },
{ Category.Category4, "Value 4" },
};
public MyElement(Types type, Category category)
{
type = AllTypes[type];
category = Categories[category];
}
}
public class Test
{
[Test, Pairwise]
public void ValidBaseCheckElementCalls
(
[Values(Types.Type1, Types.Type2, Types.Type3, Types.Type4)] Types objType,
[Values(Category.Category1, Category.Category2, Category.Category3, Category.Category4)] Category objCategory,
)
{
MyElement element = new MyElement(objType, objCategory);
}
}
I am getting JSON data from a webservice. it is providing me with FORM DATA with different questions and answers. every answer is a different c# object. I am trying to find the best way to map the ANSWERS to correct c# object.
for example if Question Id is "37" Then its a Address Object.
I have JSON String like in this format below
"answers": {
"37": {
"name": "yourAddress37",
"order": "6",
"sublabels": "{\"cc_firstName\":\"First Name\",\"cc_lastName\":\"Last Name\",\"cc_number\":\"Credit Card Number\",\"cc_ccv\":\"Security Code\",\"cc_exp_month\":\"Expiration Month\",\"cc_exp_year\":\"Expiration Year\",\"addr_line1\":\"Street Address\",\"addr_line2\":\"Street Address Line 2\",\"city\":\"City\",\"state\":\"State \\/ Province\",\"postal\":\"Postal \\/ Zip Code\",\"country\":\"Country\"}",
"text": "Your Home Address:",
"type": "control_address",
"answer": {
"addr_line1": "148 east 38st ",
"addr_line2": "",
"city": "Brooklyn ",
"state": "Ny",
"postal": "11203",
"country": ""
},
"prettyFormat": "Street Address: 148 east 38st <br>City: Brooklyn <br>State / Province: Ny<br>Postal / Zip Code: 11203<br>"
},
"38": {
"name": "emergencyContact",
"order": "9",
"sublabels": "{\"prefix\":\"Prefix\",\"first\":\"First Name\",\"middle\":\"Middle Name\",\"last\":\"Last Name\",\"suffix\":\"Suffix\"}",
"text": "Emergency Contact Name:",
"type": "control_fullname",
"answer": {
"first": "Pauline ",
"last": "Sandy "
},
"prettyFormat": "Pauline Sandy "
}
}
and it MAPS to following c# property
public Dictionary<int, answer> answers{ get; set; }
Then I have a generic Answer class
public class answer
{
public string name { get; set; }
public dynamic answer { get; set; }
}
if you look at the ANSWER data from json then you will see its different for every question. for example one answer would be ADDRESS OBJECT, other answer would be FIRST & LAST NAME object.
my question is, how can i deserialize json into correct objects/properties automatically? I can create different POCO objects, such as address & ProfileName, but how would i map them automatically to correct object/property.
EDIT:
Loop through all Answers
foreach (var a in item.answers)
{
// pass the ANSWER OBJECT (dynamic data type) to function
createNewApplication(System.Convert.ToInt16(a.Key), a.Value.answer,ref app);
}
private void createNewApplication(int key, dynamic value,ref HcsApplicant app)
{
if (key == 4) // data is plain string
app.yourPhone = value;
if (key == 8)
app.yourEmail = value;
if (key==37) // data is a object
app.address = value.ToObject<address>();
}
is this approach OK? any cleaner way of doing it?
I personally don't like every option that involves custom parsing and looking directly on the questions.
You can make use of partial deserialization via JToken class.
Just declare your answers dictionary as such:
public Dictionary<int, JToken> Answers{ get; set; }
And then whenever you need the address page you can simply do Answers[37].ToObject<Address>(). How you manage to call this method, depends upon the rest of your code, but you can embed it in properties, in a big switch, in multiple methods, one for each class. One option I like is to have a static From method in each deserializable class:
public class Address
{
public string Name { get; set; }
// all the othe properties
// ....
public static Address From(Dictionary<int, JToken> answers)
{
return answers?.TryGetValue(37, out var address) ?? false
? address?.ToObject<Address>()
: null;
}
}
// so you can just write:
var address = Address.From(answers);
As a side note, remember that the default deserialization settings for Json.Net are case insensitive, so you can deserialize the name property from JSON to a more idiomatic Name property on your POCOs.
Make a constructor for each answer type that constructs by parsing a JSON object string. Make all the answers implement an interface, e.g. IAnswer. Map all constructors (as functions) to the corresponding question IDs in a dictionary. Lastly, loop through the questions, call each constructor, and maybe put them in a new dictionary.
Example code:
interface IAnswer { };
public class ExampleAnswer : IAnswer
{
public ExampleAnswer(String JSONObject)
{
// Parse JSON here
}
}
delegate IAnswer AnswerConstructor(String JSONObject);
Dictionary<int, AnswerConstructor> Constructors = new Dictionary<int, AnswerConstructor>()
{
{1234, ((AnswerConstructor)(json => new ExampleAnswer(json)))}
// Add all answer types here
};
Dictionary<int, IAnswer> ParseAnswers(Dictionary<int, String> JSONObjects)
{
var result = new Dictionary<int, IAnswer>();
foreach (var pair in JSONObjects)
result.Add(pair.Key, Constructors[pair.Key](pair.Value));
return result;
}
Edit: Look at Matt's answer for some good options for how to parse JSON.
Edit2, In response to your edit: That looks like a good way of doing it! I think it's better than my answer, since you can keep all type information, unlike my method.
The only thing I see that you might want to change is using else if or switch instead of multiple ifs. This could increase performance if you have many answers.
You have a couple of options:
Deserialize into a dynamic object using the System.Web package as per this answer or the JSON.Net package as per this answer then use conditional checks/the null propagation operator to access a property.
Automatically deserialize down to the level where there are differences, then have code to manual deserialize the properties that are different into the correct POCO types on your parent Deserialized object.
Leverage one of the Serialization Callbacks provided by JSON.Net (OnDeserializing or OnDeserialized) to handle populating the different properties into the correct types as part of the deserialization pipeline.
With approaches 2 and 3 you could write a nicer helper method on your POCO that inspected the objects properties and returned a result which would be the type that was set (I would recommend returning an Enum) e.g.:
public PropertyTypeEnum GetPropertyType(MyPocoClass myPocoClass)
{
if (myPocoClass.PropertyOne != null)
{
return PropertyTypeEnum.TypeOne;
}
else if (...)
{
return PropertyTypeEnum.TypeN
}
else
{
// probably throw a NotImplementedException here depending on your requirements
}
}
Then in your code to use the object you can use the returned Enum to switch on the logical paths of your code.
Here is my code right now. But I would like to move those "Add" out from the constructor. Can we initialize Dictionary when we new it? or you have another better idea. Basically I want to define few characters which are used in many places.
public class User
{
public enum actionEnum
{
In,
Out,
Fail
}
public static Dictionary<actionEnum, String> loginAction = new Dictionary<actionEnum, string>();
public User()
{
loginAction.Add(actionEnum.In, "I");
loginAction.Add(actionEnum.Out, "O");
loginAction.Add(actionEnum.Fail, "F");
}
.....
}
You can use C# 3's collection initializer syntax:
public static Dictionary<actionEnum, String> loginAction = new Dictionary<actionEnum, string> {
{ actionEnum.In, "I" },
{ actionEnum.Out, "O" },
{ actionEnum.Fail, "F" }
};
Note, by the way, that the dictionary is mutable; any code can add or remove values.