I have a function that loads a large selectlist for ASP.NET MVC.
This functions has a methodsize of 354 rows.
I want to refactor to more functions or to a local field so that each function will be less than 40 lines.
Here is the code snippet:
public static SelectList CreateShutterSpeedList()
{
var shutterSpeedList = new List<CameraSettingItem>();
var secNotationPostfix = "\"";
shutterSpeedList.Add(new CameraSettingItem
{
Id = ShutterSpeedDefaultValue,
Description = string.Empty
});
shutterSpeedList.Add(new CameraSettingItem
{
Id = 1,
Description = "30" + secNotationPostfix
});
etc
Maybe a private list as a variable ? Or loading from file ? Or else...?
If IDs above ShutterSpeedDefaultValue are assigned sequentially, you could create an array of descriptions first, and then convert it to CameraSettingItem list with LINQ:
var descriptions = new[] {
string.Empty
, "30" + secNotationPostfix
, ...
};
shutterSpeedList = descriptions
.Select((d,i) => new CameraSettingItem {
Id = i==0 ? ShutterSpeedDefaultValue : i
, Description = d
})
.ToList();
You could also create a list of CameraSettingItems outside of your method's body, like this:
private const string secNotationPostfix = "\"";
private static IList<CameraSettingItem> shutterSpeedList = new List<CameraSettingItem> {
new CameraSettingItem {
Id = ShutterSpeedDefaultValue,
Description = string.Empty
},
new CameraSettingItem {
Id = 1,
Description = "30" + secNotationPostfix
},
...
};
public static SelectList CreateShutterSpeedList() {
return new SelectList(shutterSpeedList, "Id", "Description");
}
You can store items that you need in JSON or XML files and deserialize them when you need using JavaScriptSerializer or Json.NET for example:
public static SelectList CreateShutterSpeedList(
{
var json = File.ReadAllText(#"\ShutterSpeedList.json");
var shutterSpeedList = JsonConvert.DeserializeObject<List<CameraSettingItem>>(json);
// Convert shutterSpeedList to SelectList and return
}
Alternatively you can reduce number of lines by using collection initializer (as #dasblinkenlight pointed out) and constructors with optional parameters/object initializers if you have access to CameraSettingItem code:
public static SelectList CreateShutterSpeedList()
{
var secNotationPostfix = "\"";
var shutterSpeedList = new List<CameraSettingItem>
{
new CameraSettingItem(id: ShutterSpeedDefaultValue),
new CameraSettingItem(id: 1, description: "30" + secNotationPostfix),
...
};
// Convert shutterSpeedList to SelectList and return
}
Related
Using Axis Communications VAPIX WSDL APIs - I'm setting up a NewActionConfiguration which takes a number of parameters that I have saved in a List but the way the API documents have the implementation I cant loop through my parameter list XML objects while defining the newAction object.
//This is how the API docs say to do it:
NewActionConfiguration newAction = new NewActionConfiguration
{
TemplateToken = overlayToken,
Name = "Overlay Text",
Parameters = new ActionParameters
{
Parameter = new[]
{
new ActionParameter { Name = "text", Value = "Trigger:Active" },
new ActionParameter { Name = "channels", Value = "1" },
new ActionParameter { Name = "duration", Value = "15" }
}
}
};
//This is what I need to do:
NewActionConfiguration newAction = new NewActionConfiguration
{
Name = xmlPrimaryAction["Name"].InnerText,
TemplateToken = xmlPrimaryAction["ActionTemplate"].InnerText,
Parameters = new[]
{
foreach (ActionParameter actionParameter in actionParameterList)
{
new ActionParameter { Name = actionParameter.Name, Value = actionParameter.Value };
}
}
};
The API will not allow me to just do a: newAction.Parameters.Parameter.Add(actionParameter) or the like. Anyone got any ideas?
So first things, you cannot declare a foreach block inside an object instantiating scope block. What you need to do is declare a variable before, in the function scope and then attribute the Parameter property to it. Like so:
var actionParameters = new List<ActionParameter>();
foreach (ActionParameter actionParameter in actionParameterList)
{
actionParameters.Add(new ActionParameter { Name = actionParameter.Name, Value = actionParameter.Value });
}
NewActionConfiguration newAction = new NewActionConfiguration
{
Name = xmlPrimaryAction["Name"].InnerText,
TemplateToken = xmlPrimaryAction["ActionTemplate"].InnerText,
Parameters = actionParameters.ToArray()//Use System.Linq here to convert the list into an array
};
Found it! Thanks for the help #Vitor you were close but learned how to cast my list as my object after i found this: Convert List to object[]
Here's what finally worked:
var actionParameterList = new List<ActionParameter>();
foreach (XmlNode xmlParameter in xmlActionParameters)
{
ActionParameter actionParameter = new ActionParameter();
actionParameter.Name = xmlParameter["Name"].InnerText;
actionParameter.Value = xmlParameter["Value"].InnerText;
actionParameterList.Add(new ActionParameter { Name = actionParameter.Name, Value = actionParameter.Value });
}
NewActionConfiguration newAction = new NewActionConfiguration
{
Name = xmlPrimaryAction["Name"].InnerText,
TemplateToken = xmlPrimaryAction["ActionTemplate"].InnerText,
Parameters = new ActionParameters
{
Parameter = actionParameterList.Cast<ActionParameter>().ToArray()
}
};
I am trying to split a string to get json object value - I have text values with numerous lines in the format:
new Car() { Id = 1, Year = 1926, Make = "Chrysler", Model = "Imperial", ImageUrl = "{"data":{"images":[{"thumb_url":"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRPe4CygIW-MuZL5jl77wlgXXK5_ANyC9l1X4QqLizCOkaVAlRe","image_url":"http://imperialclub.org/Yr/1926/photos/Phaeton2Big.jpg","width":1632,"height":1032}]},"error_code":0,"error":false,"message":"1 images(s) available"}" },
new Car() { Id = 2, Year = 1950, Make = "Hillman", Model = "Minx Magnificent", ImageUrl = "{"data":{"images":[{"thumb_url":"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcScVsGEeRBh6xZYXr6Gm35Sk5ecSlk_ax3qZmoGRAtBbZC8vJZ9","image_url":"http://i.ebayimg.com/images/g/gcIAAOSwKadXPeLs/s-l300.jpg","width":300,"height":225}]},"error_code":0,"error":false,"message":"1 images(s) available"}" },
new Car() { Id = 3, Year = 1954, Make = "Chevrolet", Model = "Corvette", ImageUrl = "{"data":{"images":[{"thumb_url":"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSdZntu4tgWrZrxwqeuKlteCP9vJGnqUlmNq5JF1bBCf-EJy5r8","image_url":"http://momentcar.com/images/chevrolet-corvette-1954-1.jpg","width":1000,"height":600}]},"error_code":0,"error":false,"message":"1 images(s) available"}" },
What I would really like is to get them in the format:
new Car() { Id = 1, Year = 1926, Make = "Chrysler", Model = "Imperial", ImageUrl = "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcRPe4CygIW-MuZL5jl77wlgXXK5_ANyC9l1X4QqLizCOkaVAlRe" },
new Car() { Id = 2, Year = 1950, Make = "Hillman", Model = "Minx Magnificent", ImageUrl = "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcScVsGEeRBh6xZYXr6Gm35Sk5ecSlk_ax3qZmoGRAtBbZC8vJZ9" },
new Car() { Id = 3, Year = 1954, Make = "Chevrolet", Model = "Corvette", ImageUrl = "https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcSdZntu4tgWrZrxwqeuKlteCP9vJGnqUlmNq5JF1bBCf-EJy5r8" },
I know I can use JObject.Parse(data); to parse the json value - but just tring to get to it is becoming a bit of a nightmare. Is there a better way of doing this?
What I have so far:
static void Main(string[] args)
{
using (StreamWriter writer = new StreamWriter(#"c:\Data\temp\output.txt")) // file to write to
{
using (StreamReader reader = new StreamReader(#"c:\Data\temp\test.txt")) //file to read from
{
string line;
while (reader.ReadLine() != null)
{
line = reader.ReadLine();
string[] words = JsonSplitString(line);
string json = words[1];
writer.WriteLine("{0}", json);
}
}
}
}
static string[] JsonSplitString(string data)
{
return data.Split(new string[] { "ImageUrl" }, StringSplitOptions.None);
}
However I am getting a NullReferenceException - even though a string is being passed in to the JsonSplitString method.
You are calling reader.Readline() twice: once for the comparison and then again inside your loop. You are actually skipping every other line. And what is probably happening is that you are reaching the end of your file and then calling reader.Readline() again, which is null. Try this instead:
line = reader.ReadLine();
while (line != null)
{
string[] words = JsonSplitString(line);
string json = words[1];
writer.WriteLine("{0}", json);
line = reader.ReadLine();
}
using System;
using Newtonsoft.Json.Linq;
namespace JsonExperiments
{
class Program
{
static void Main(string[] args)
{
ExecuteEmployeeSearch();
Console.ReadLine();
}
static void ExecuteEmployeeSearch()
{
// mockup JSON that would be returned from API
string sampleJson = "{\"results\":[" +
"{\"employeename\":\"name1\",\"employeesupervisor\":\"supervisor1\"}," +
"{\"employeename\":\"name2\",\"employeesupervisor\":\"supervisor1\"}," +
"{\"employeename\":\"name3\",\"employeesupervisor\":[\"supervisor1\",\"supervisor2\"]}" +
"]}";
// Parse JSON into dynamic object, convenient!
JObject results = JObject.Parse(sampleJson);
// Process each employee
foreach (var result in results["results"])
{
// this can be a string or null
string employeeName = (string)result["employeename"];
// this can be a string or array, how can we tell which it is
JToken supervisor = result["employeesupervisor"];
string supervisorName = "";
if (supervisor is JValue)
{
supervisorName = (string)supervisor;
}
else if (supervisor is JArray)
{
// can pick one, or flatten array to a string
supervisorName = (string)((JArray)supervisor).First;
}
Console.WriteLine("Employee: {0}, Supervisor: {1}", employeeName, supervisorName);
}
}
}
}
Consider the following object:
Controller controller = new Controller()
{
Name = "Test",
Actions = new Action[]
{
new Action() { Name = "Action1", HttpCache = 300 },
new Action() { Name = "Action2", HttpCache = 200 },
new Action() { Name = "Action3", HttpCache = 400 }
}
};
How can I map this object to a dictionary of the following form?
#key# -> #value#
"Test.Action1" -> 300
"Test.Action2" -> 200
"Test.Action3" -> 400
That is, a Dictionary<string, int>.
I am interested in a LINQ solution but I can't manage to work around it.
I am trying to map each action to a KeyValuePair, but I don't know how to get each action's parent controller's Name property.
The main thing is the controller is still in scope in the lambda:
var result = controller.Actions.ToDictionary(
a => string.Format("{0}.{1}", controller.Name, a.Name),
a => a.HttpCache);
The LINQ approach is to project the Actions list, using the Select method, into a dictionary. Since you're calling it on a Controller instance, you have access to the Controller's Name as well:
myController.Actions.ToDictionary(
/* Key selector - use the controller instance + action */
action => myController.Name + "." + action.Name,
/* Value selector - just the action */
action => action.HttpCache);
If you want to make one large dictionary from several controllers, you can use SelectMany to project each Controller's items into a list of Controller+Action, then convert that list into a dictionary:
var namesAndValues =
controllers.SelectMany(controller =>
controller.Actions.Select(action =>
{
Name = controller.Name + "." + action.Name,
HttpCache = action.HttpCache
}));
var dict = namesAndValues.ToDictionary(nav => nav.Name, nav => nav.HttpCache);
You can try this :
var dico = controller.Actions
.ToDictionary(a => $"{controller.Name}.{a.Name}",
a => a.HttpCache);
The first lambda expression target the key whereas the second target the value of the dictionary entry.
Assuming that you have multiple controllers in a collection, not just the one controller variable in your example code, and want to put all of their actions into a single dictionary, then you could do something like this:
var httpCaches = controllers.SelectMany(controller =>
controller.Actions.Select(action =>
new
{
Controller = controller,
Action = action
})
)
.ToDictionary(
item => item.Controller.Name + "." + item.Action.Name,
item => item.Action.HttpCache);
This would be for the case where you have your data set up like this:
var controllers = new[] {
new Controller()
{
Name = "Test1",
Actions = new Action[] {
new Action { Name = "Action1", HttpCache = 300 },
new Action { Name = "Action2", HttpCache = 200 },
new Action { Name = "Action3", HttpCache = 400 },
}
},
new Controller()
{
Name = "Test2",
Actions = new Action[] {
new Action { Name = "Action1", HttpCache = 300 },
new Action { Name = "Action2", HttpCache = 200 },
new Action { Name = "Action3", HttpCache = 400 },
}
},
};
i have searched for something similiar in stackoverflow and couldnt find anything which will give me some hint.
i have following code:
DATA val1 = new DATA();
val1.Name = "KeyValue";
val1.Value = "805373069";
DATA val2 = new DATA();
val2.Name = "Tel";
val2.Value = "0123456789";
DATA val3 = new DATA();
val3.Name = "TargetID";
val3.Value = "43301";
DATA val4 = new DATA();
val4.Name = "ServiceLevel";
val4.Value = "Y";
DATA val5 = new DATA();
val5.Name = "TypeId";
val5.Value = "13505";
DATA val6 = new DATA();
val6.Name = "DateTime";
val6.Value = System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt");
DATA val7 = new DATA();
val7.Name = "DbDateTime";
val7.Value = System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt");
and once all the objects are populated i put them in Single array.
i.e. to be used somewhere else
DATA[] array = {val1,val2,val3,val4,val5,val6,val7};
and Proxy class which i cant change is:
public partial class DATA {
private string nameField;
private string valueField;
public string Name {
get {
return this.nameField;
}
set {
this.nameField = value;
this.RaisePropertyChanged("Name");
}
}
public string Value {
get {
return this.valueField;
}
set {
this.valueField = value;
this.RaisePropertyChanged("Value");
}
}
Now what i have tried and failed to make it easier is used Dictionary and also jagged array and multi dimensional array which didnt worked as i hoped.
can someone give me hint of a better solution then having 7 different objects created, as this data is dynamic i have to do this runtime data population.
suggestions please?
You could just declare the objects in-line as part of the array declaration, if all you're trying to do is avoid having the variables:
DATA[] array = {
new DATA { Name = "something", Value = "something else" },
new DATA { Name = "something", Value = "something else" },
new DATA { Name = "something", Value = "something else" },
new DATA { Name = "something", Value = "something else" }
};
Anywhere that you have a variable, you can instead have the operation which created that variable. The order of operations will result in evaluating to the same thing. Where you'd need a variable is where you want to use the same instance of something multiple times, or the same value without having to re-calculate it.
Put all data in a dictionary if you want to make sure names must not be duplicated:
var data = new Dictionary<string, string>();
// fill dictionary:
data.Add("name1", /*value*/);
data.Add("name2", /*value*/);
data.Add("name3", /*value*/);
data.Add("name4", /*value*/);
Then convert it to array:
return data.Select(d => new Data(){ Name = d.Key, Value = d.Value}).ToArray();
Make sure you have included using System.Linq in top.
UPDATE:
As #LukeH suggested, You can simply use collection initializer like this:
var data = new Data[]
{
new Data(){ Name = "Sylvester", Value = /*value*/ },
new Data(){ Name = "Whiskers", Value = /*value*/ },
new Data(){ Name = "Sasha", Value = /*value*/ }
};
Which doesn't prevent duplicate names for Data type instances.
You can create extension method something like this to overcome the problem of assign properties values rather then duplication,
static class Extensions
{
public static void AddDataObject(this List<DATA> dataList, params string[] values)
{
dataList.Add(new DATA() { Name = values[0], Value = values[1] });
}
}
and passing that values as per given below,
List<DATA> dataList = new List<DATA>();
dataList.AddDataObject("KeyValue", "805373069");
dataList.AddDataObject("Tel", "0123456789");
Here in above example I used List instead of array, you can change according to your requirements
You could initialize an anonymous object and then convert to an array of data like this:
var data = new {
KeyValue="805373069",
Tel="0123456789",
TargetID="43301",
ServiceLevel="Y",
TypeId="13505",
DateTime=System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt"),
DbDateTime=System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt")
};
var array = data.GetType()
.GetProperties()
.Select(x=>new DATA{Name=x.Name,Value=(string)x.GetValue(data)})
.ToArray();
You could also do it like this:
var data = new {
KeyValue="805373069",
Tel="0123456789",
TargetID="43301",
ServiceLevel="Y",
TypeId="13505",
DateTime=System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt"),
DbDateTime=System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt")
};
var array=System.Web.Mvc.HtmlHelper.AnonymousObjectToHtmlAttributes(data)
.Select(x=>new DATA {Name=x.Key,Value=(string)x.Value})
.ToArray();
If you need to take an array of data and convert it back into a class object (not anonymous), you can do the first method, just in reverse as well. Or put extension methods on it to convert from/to your data array.
static class Extensions
{
public static DATA[] ToDataArray(this object data)
{
return data.GetType()
.GetProperties()
.Select(x=>new DATA{Name=x.Name,Value=(string)x.GetValue(data)})
.ToArray();
}
}
var data = new {
KeyValue="805373069",
Tel="0123456789",
TargetID="43301",
ServiceLevel="Y",
TypeId="13505",
DateTime=System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt"),
DbDateTime=System.DateTime.Now.ToString("ddMMyyyyHHmmssffftt")
};
var array=data.ToDataArray();
However, David's answer is better.
I want to use a string array stored in the web.config to easily change its values, this is in the format: full_w=670|small_w=100,q=low|tiny_h=30,c=true. Each template is split by the | (pipe) and then each of those sets comprises of a name (left of _) and its corresponding values (right of _), the values can be several and each separated by the , (comma). I think this possibly qualifies for a 3D array, I just can't seem to get an easy way to read this in a sensible manner. Any ideas or solutions as to the best way to read/manage the data from this string?
Basically, in the end I want to be able to call the template small and read its values which in this case are width=100 and quality=low.
Here's the function I wrote to parse one of these settings strings:
public static Dictionary<string, Dictionary<string, string>> getSettings(string settingsStr)
{
return settingsStr.Split('|').ToDictionary(
template => template.Split('_')[0],
template => template.Split('_')[1].Split(',').ToDictionary(
setting => setting.Split('=')[0],
setting => setting.Split('=')[1]));
}
It just uses a lot of string .Splitting and .ToDictionarying.
Here's the test, showing that it works:
var result = getSettings("full_w=670|small_w=100,q=low|tiny_h=30,c=true");
/*
result = {
[ "full" => [ "w" => "670" ] ]
[ "small" => [ "w" => "100", "q" => "low" ] ]
[ "tiny" => [ "h" => "30", "c" => "true" ] ]
}
*/
To read the values w and q from template small, you can do this:
int width = int.Parse(result["small"]["w"]);
string quality = result["small"]["q"];
Edit: As an added bonus, if you want to convert the Dictionary<string, Dictionary<string, string>> back into a single settings sting, you can use this method:
public static string getSettingsStr(Dictionary<string, Dictionary<string, string>> settings)
{
return string.Join("|",
settings.Select(kvp =>
kvp.Key + "_" + string.Join(",",
kvp.Value.Select(setting =>
setting.Key + "=" + setting.Value))));
}
Use:
string settingsStr = getSettingsStr(result);
// settingsStr = "full_w=670|small_w=100,q=low|tiny_h=30,c=true"
If you want to check that a specific template or setting exists, then use the .ContainsKey() method:
// If I have "Dictionary<string, Dictionary<string, string>> settings;"
int width = -1;
string quality = null;
if (settings.ContainsKey("small"))
{
if (settings["small"].ContainsKey("w"))
width = int.Parse(settings["small"]["w"]);
if (settings["small"].ContainsKey("q"))
quality = settings["small"]["q"];
}
Have you considered using plain old XML Serialization with your own plain old C# objects. Here is an example:
public class Program
{
static void Main(string[] args)
{
var data = new MyConfig[2];
for (int i = 0; i < 2; i++)
{
data[i] = new MyConfig { Name = "Name" + i };
data[i].Properties = new MyConfigAttribute[]
{
new MyConfigAttribute { Name = "Property Name " + i, Value = "Property Value " + i },
new MyConfigAttribute { Name = "2nd Property Name " + i, Value = "2nd Property Value " + i },
};
}
var serializer = new XmlSerializer(typeof(MyConfig[]));
using (StreamWriter tw = File.CreateText(#"c:\temp\myconfig.xml"))
{
serializer.Serialize(tw, data);
}
using (StreamReader tw = File.OpenText(#"c:\temp\myconfig.xml"))
{
var readBack = serializer.Deserialize(tw);
}
Console.ReadLine();
}
[XmlRoot("MY_CONFIG")]
public class MyConfig
{
[XmlElement("NAME")]
public string Name { get; set; }
[XmlArray]
[XmlArrayItem(typeof(MyConfigAttribute))]
public MyConfigAttribute[] Properties { get; set; }
}
[XmlRoot("MY_CONFIG_ATTRIBUTE")]
public class MyConfigAttribute
{
[XmlElement("ATTRIBUTE_NAME")]
public string Name { get; set; }
[XmlElement("ATTRIBUTE_VALUE")]
public string Value { get; set; }
}
}
Basically, you create a class to store your individual attributes (MyConfigAttribute in this case), wrap it in another class to provide your name for a group of related attributes (MyConfig in this case), then use normal XML Serialization to write the settings out to an individual XML file, like this section of the code
var serializer = new XmlSerializer(typeof(MyConfig[]));
using (StreamWriter tw = File.CreateText(#"c:\temp\myconfig.xml"))
{
serializer.Serialize(tw, data);
}
You can read it back to objects again using this section of the code:
using (StreamReader tw = File.OpenText(#"c:\temp\myconfig.xml"))
{
var readBack = serializer.Deserialize(tw);
}
The advantage of this is:
It is simple to understand and use
You can add features to your custom class, e.g. to add values to the array of properties, thereby lending itself to wrapping a custom screen around it.
Look up C# XML Serialization on Google!