Manage complex string array in C# - c#

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!

Related

How to write unescaped quoted values

Does CsvHelper not support quoted unescaped output or is there something wrong with my writer configuration?
Input:
Jake|"B" Street
CsvHelper reader configuration:
var readerConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = "|",
TrimOptions = TrimOptions.Trim,
IgnoreBlankLines = true,
Mode = CsvMode.NoEscape
};
After record is read into memory, the value with double quotes is unescaped as expected: "B" Street
However, when I write records to CSV file if ShouldQuote = args => true is used without CsvMode.NoEscape double quotes are escaped and if I add CsvMode.NoEscape the output is not quoted at all.
writerConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture)
{
ShouldQuote = args => true,
Mode = CsvMode.NoEscape
};
Output with CsvMode.NoEscape: "B" Street // Output not quoted and not escaped.
Output without CsvMode.NoEscape: """B"" Street" // Double quotes escaped.
Any ideas on how to write unescaped quoted values with CsvHelper?
This feels a little hacky, but I was able to do what I think you are trying to do with CsvMode.NoEscape and a custom string converter to add the outer quotes. Another option would be to use Convert in a ClassMap to add the outer quotes on a column by column basis.
void Main()
{
var records = new List<Foo>
{
new Foo { Id = 1, Name = "\"B\" Street" },
};
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Mode = CsvMode.NoEscape
};
using (var csv = new CsvWriter(Console.Out, config))
{
csv.Context.TypeConverterCache.AddConverter<string>(new OuterQuoteConverter());
csv.WriteRecords(records);
}
}
public class OuterQuoteConverter : StringConverter
{
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
var quoted = "\"" + (string)value + "\"";
return base.ConvertToString(quoted, row, memberMapData);
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}

How to parse JSON data to generate a C# sample code which demonstrates Request Body Object creation

I need to write many sample codes for various API's, which demonstrate how to write the code to use those particular REST API's in C# language.
Now for the API's which are HTTP POST calls, a request body is first created as a class object which is then later serialized into a JSON string and passed to the REST Client.
My Requirement is in this part: "Creating the request Body as a class object".
Following example will make the requirement crystal clear:-
Suppose i have the below JSON Data:
{
"workingDays": ["Monday","Wednesday","Friday"],
"employeeInformation": {
"employeeID": "12345",
"name": {
"firstName": "foo",
"lastName": "bar"
}
},
"joiningDate":"23061984"
}
I need to parse the above data and generate the below code (which currently i am writing manually):
// Create Main Request Body Object
var requestBodyObj = new RequestBody();
// Array Case
var workingDaysObj = new List<string>();
workingDaysObj.Add("Monday");
workingDaysObj.Add("Wednesday");
workingDaysObj.Add("Friday");
requestBodyObj.workingDays = workingDaysObj;
// Nested Object Case
var employeeInformationObj = new employeeInformation();
employeeInformationObj.employeeID = "12345";
var nameObj = new name();
nameObj.firstName = "foo";
nameObj.lastName = "bar";
employeeInformationObj.name = nameObj;
requestBodyObj.employeeInformation = employeeInformationObj;
// Simple Name/Value Pair
requestBodyObj.joiningDate = "23061984";
So as per the above example the JSON Data can be in one of the following 2 forms as well (apart from simple name/value pairs):
Array
Nested Object
And both these cases should be handled as shown in the above code.
Note: User will not be provided with a JSON file so i can't write any code which directly reads a JSON file, deserializes it and assigns the values to a class object using (for example) a NewtonSoft function:
// read file into a string and deserialize JSON to a type
Movie movie1 = JsonConvert.DeserializeObject<Movie>(File.ReadAllText(#"c:\movie.json"));
// deserialize JSON directly from a file
using (StreamReader file = File.OpenText(#"c:\movie.json"))
{
JsonSerializer serializer = new JsonSerializer();
Movie movie2 = (Movie)serializer.Deserialize(file, typeof(Movie));
}
I just want a simple "JSON parser and C# Code generator" (Preferably written in C# language itself).
Any suggestions or pointers will be appreciated.
Edit Update
Pascal Case setting for variable names
Json Object names can be mapped to Project model classes
Write the output to text file for easy copy
Updated Code Below:-
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
namespace SampleCodeCreator
{
class Program
{
// Declaring the variables
static string jsonFilePath = #"[Your File Path]";
static string outputFilePath = #"[Your File Path]";
static string jsonData;
static Dictionary<string, string> classMap = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
static void Main(string[] args)
{
// Initializing Class map which is used to map the json object to Project Model Class
InitializeClassMap();
// clear current data in the output file
using (System.IO.StreamWriter file = new System.IO.StreamWriter(outputFilePath, false))
{
file.Write(String.Empty);
}
// read the json data file and store the data in a simple string
using (StreamReader r = new StreamReader(jsonFilePath))
{
jsonData = r.ReadToEnd();
}
// Call the method for the whole json data
PrintJsonObject("RequestBody", jsonData);
}
static void PrintJsonObject(string parentObject, string jsonData)
{
// if the parent object has any mapped class, then set the object name
parentObject = MappedClass(parentObject);
Console.WriteLine("var {0}Obj = new {1}();", ToCamelCase(parentObject), parentObject);
SetOutput("var " + ToCamelCase(parentObject) + "Obj = new " + parentObject + "();");
Console.WriteLine("");
SetOutput("");
// Deserialize the Json data and iterate through each of its sub-sections
var jsonSubData = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonData);
foreach (var data in jsonSubData)
{
var dataKey = data.Key;
var dataValue = data.Value;
// array case (if the sub element is an array)
if (dataValue.ToString().Contains("["))
{
PrintArrayCase(dataKey, dataValue);
Console.WriteLine("{0}Obj.{1} = {1}Obj;", ToCamelCase(parentObject), dataKey);
SetOutput(ToCamelCase(parentObject) + "Obj." + dataKey + " = " + dataKey + "Obj;");
Console.WriteLine("");
SetOutput("");
}
// nested object case (if the sub element itself contains another json format body)
else if (dataValue.ToString().Contains("{"))
{
// Recursive Call
PrintJsonObject(dataKey, dataValue.ToString());
Console.WriteLine("{0}Obj.{1} = {1}Obj;", ToCamelCase(parentObject), dataKey);
SetOutput(ToCamelCase(parentObject) + "Obj." + dataKey + " = " + dataKey + "Obj;");
Console.WriteLine("");
SetOutput("");
}
// simple key value pair case
else
{
PrintKeyValuePairCase(parentObject, dataKey, dataValue.ToString());
}
}
}
static void PrintArrayCase(string key, object obj)
{
Console.WriteLine("var {0}Obj = new List<string>();", key);
SetOutput("var " + key + "Obj = new List<string>();");
// The array value is split into its values
// e.g. [abc, def, ghi] -> [abc] [def] [ghi]
var valueString = obj.ToString();
var valueSplitArray = valueString.Split(',');
for (int k = 0; k < valueSplitArray.Count(); k++)
{
string listValue = "";
if (k != valueSplitArray.Count() - 1)
{
var startIndex = valueSplitArray[k].IndexOf("\"");
listValue = valueSplitArray[k].Substring(startIndex + 1, valueSplitArray[k].Length - startIndex - 2);
}
else
{
var startIndex = valueSplitArray[k].IndexOf("\"");
listValue = valueSplitArray[k].Substring(startIndex + 1, valueSplitArray[k].Length - startIndex - 5);
}
// each value is then added to the main list object
Console.WriteLine(#"{0}Obj.Add(""{1}"");", ToCamelCase(key), listValue);
SetOutput(#""+ToCamelCase(key)+#"Obj.Add("""+listValue+#""");");
}
Console.WriteLine("");
SetOutput("");
}
static void PrintKeyValuePairCase(string parentObj, string key, string value)
{
Console.WriteLine("{0}Obj.{1} = \"{2}\";", ToCamelCase(parentObj), key, value);
SetOutput("" + ToCamelCase(parentObj) + "Obj." + key + " = \"" + value + "\";");
}
static string ToCamelCase(string str)
{
if (!String.IsNullOrEmpty(str))
{
return str.Substring(0, 1).ToLower() + str.Substring(1, str.Length - 1);
}
else
{
return null;
}
}
static string MappedClass(string str)
{
if (classMap.ContainsKey(str))
return classMap[str];
else
return str;
}
static void InitializeClassMap()
{
classMap.Add("employeeInformation", "EmployeeInfo");
}
static void SetOutput(string str)
{
using (System.IO.StreamWriter file = new System.IO.StreamWriter(outputFilePath, true))
{
file.WriteLine(str);
}
}
}
}

Collection in C# Class Library

I wrote a simple .dll class library C#. My file takes a values of my main program and create a html file with this values. Look at my code: (I am describing it so that you can check if I have a mistake)
public class MyClass1 { public void HTMLGen(int number, string name)
{
var html1 = string.Format("<p>This is number: {0} and this is name: {1}</p>", number, name);
var xDocument = new XDocument(
new XDocumentType("html", null, null, null),
new XElement("html",
new XElement("head"),
new XElement("body",
XElement.Parse(html1))));
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true,
IndentChars = "\t"
};
using (var writer = XmlWriter.Create(#"C:\Users\Desktop\test.html", settings))
{
xDocument.WriteTo(writer);
}
} }
Okay, this code works well. so I need add to collection. I create a new class-file in this .dll. This is my new class:
public class Collection : IList {
public int value_one { get; set; }
public int value_two { get; set; }}
and I added this parameter to my class:
public class MyClass1 { public void HTMLGen(int number, string name, IList<Collection> collection)
so I create a new 'var html2' and I want to add this to XElement.Parse. This is my whole code:
public class MyClass1 { public void HTMLGen(int number, string name, IList<Collection> collection) {
var html1 = string.Format("<p>This is number: {0} and this is name: {1}</p>", number, name);
var html2 = string.Format("<p> /* value_one and value_two from Collection */ </p>");
var xDocument = new XDocument(
new XDocumentType("html", null, null, null),
new XElement("html",
new XElement("head"),
new XElement("body",
XElement.Parse(html1), XElement.Parse(html2))));
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true,
IndentChars = "\t"
};
using (var writer = XmlWriter.Create(#"C:\Users\Desktop\test.html", settings))
{
xDocument.WriteTo(writer);
} } }
and please, look at my comment. I don't know, how Can I add this values of my collection to this var html2.
Firstly, I don't understand why you would need to create a class named Collection with two integers. You can either use a int Array or list. Or may be a structure would be the best thing to use if you have not just int but other datatypes.
Lets suppose you need that class, then why should it inherit IList? I don't see any usage of it at all.
Anyway, just to answer your question:
var html2 = string.Format("<p> /* {0} and {1} from Collection */ </p>", collection.value_one, collection.value_two);
Or, if you have different types for your values, just convert it to string to be safe.
var html2 = string.Format("<p> /* {0} and {1} from Collection */ </p>", collection.value_one.ToString(), collection.value_two.ToString());
You can use Interpolated Strings to avoid long String.Format method usage like below.
var html1 = $"<p>This is number: {number} and this is name: {name}</p>");
var html2 = $"<p> This is first value: {collection[0]} and this is second value: {collection[1]}</p>";
Maybe you are trying to achive this?
public class MyClass1
{
public void HTMLGen(int number, string name, IList<int> collection)
{
var html1 = string.Format("<p>This is number: {0} and this is name: {1}</p>", number, name);
string html2 = "";
foreach (var item in collection)
{
html2 += item + " ";
}
html2 = "<p>" + html2 + "</p>";
//The rest of the code
}
}
usage:
MyClass1 myClass1 = new MyClass1();
List<int> numbers = new List<int> { 4, 3, 21, 123, 6 };
myClass1.HTMLGen(10, "name", numbers);

CSV Delimited to XML - Folder Hierarchy

This is my first time posting so I apologize for any ignorance or failed use of examples.
I have a console app project to create where I have been given a fair few CSV files and I need to create some kind of Parent/Child/Grandchild relationship out of them (XML? maybe? - then I can use that to do the uploads and writes to the DMS with minimal calls - I don't want to be querying if a folder exists over and over)
I am a little out of my depth on this one
I need to know the best way to do this without 3rd party library dependencies, pure C#, using the OLEDB JET provider is most likely required as it will handle the parsing required, there is no order to the CSV files in regards to date, previous years could appear down the list and vice versa.
Here's an example of the CSV output
"DESCRIPTION1","8, 5/8\" X 6.4MM","STRING","filename001.pdf","2016-09-19","1"
"DESCRIPTION2","12, 3/4\" X 6.4MM","STRING","filename001.pdf","2016-09-19","1"
"DESCRIPTION3","12, 3/4\" X 6.4MM","STRING","filename001.pdf","2016-09-19","1"
"another description 20# gw","1","388015","Scan123.pdf","2015-10-24","1"
"another description 20# gw","3","385902","Scan456.pdf","2015-04-14","1"
"STRINGVAL1","273.10 X 9.27 X 6000","45032-01","KHJDWNEJWKFD9101529.pdf","2012-02-03","1"
"STRINGVAL2","273.10 X 21.44 X 6000","7-09372","DJSWH68767681540.pdf","2017-02-03","1"
The end output will be (YEAR/MONTH/FILENAME + (Attributes for each file - these are for eventually updating columns inside a DMS))
Year and Month retrieved from the column with the date
If the YEAR alread exists then it will not be created again
If the month under that year exists it will not be created again
If the filename already exists under that YEAR/MONTH it will not be created again BUT the additional ATTRIBUTES for that FileName will be added to the attributes - "line seperated?"
Required Output:
I have attempted a Linq query to begin to output the possible required XML for me to progress but it outputs every row and does no grouping, I am not familiar with Linq at the moment.
I also ran into issues with the basic escaping on the .Split(',') doing it this way (see original CSV examples above compared to me using TAB separation in my test file and example below) which is why I want the Oledb provider to handle it.
string[] source = File.ReadAllLines(#"C:\Processing\In\mockCsv.csv");
XElement item = new XElement("Root",
from str in source
let fields = str.Split('\t')
select new XElement("Year", fields[4].Substring(0, 4),
new XElement("Month", fields[4].Substring(5, 2),
new XElement("FileName", fields[3]),
new XElement("Description",fields[0]),
new XElement("Length", fields[1]),
new XElement("Type", fields[2]),
new XElement("FileName", fields[3]),
new XElement("Date", fields[4]),
new XElement("Authorised", fields[5]))
)
);
I also need to log every step of the process so I have setup a Logger class
private class Logger
{
private static string LogFile = null;
internal enum MsgType
{
Info,
Debug,
Error
}
static Logger()
{
var processingDetails = ConfigurationManager.GetSection(SECTION_PROCESSINGDETAILS) as NameValueCollection;
LogFile = Path.Combine(processingDetails[KEY_WORKINGFOLDER],
String.Format("Log_{0}.txt", StartTime.ToString("MMMyyyy")));
if (File.Exists(LogFile))
File.Delete(LogFile);
}
internal static void Write(string msg, MsgType msgType, bool isNewLine, bool closeLine)
{
if (isNewLine)
msg = String.Format("{0} - {1} : {2}", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"), msgType, msg);
if (closeLine)
Console.WriteLine(msg);
else
Console.Write(msg);
if (String.IsNullOrEmpty(LogFile))
return;
try
{
using (StreamWriter sw = new StreamWriter(LogFile, true))
{
if (closeLine)
sw.WriteLine(msg);
else
sw.Write(msg);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Used as such
Logger.Write(String.Format("Reading records from csv file ({0})... ",
csvFile), Logger.MsgType.Info, true, false);
Try following. If you are reading from a file use StreamReader instead of StringReader :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Text.RegularExpressions;
namespace ConsoleApplication74
{
class Program
{
static void Main(string[] args)
{
string input =
"\"DESCRIPTION1\",\"8, 5/8 X 6.4MM\",\"STRING\",\"filename001.pdf\",\"2016-09-19\",\"1\"\n" +
"\"DESCRIPTION2\",\"12, 3/4 X 6.4MM\",\"STRING\",\"filename001.pdf\",\"2016-09-19\",\"1\"\n" +
"\"DESCRIPTION3\",\"12, 3/4 X 6.4MM\",\"STRING\",\"filename001.pdf\",\"2016-09-19\",\"1\"\n" +
"\"another description 20# gw\",\"1\",\"388015\",\"Scan123.pdf\",\"2015-10-24\",\"1\"\n" +
"\"another description 20# gw\",\"3\",\"385902\",\"Scan456.pdf\",\"2015-04-14\",\"1\"\n" +
"\"STRINGVAL1\",\"273.10 X 9.27 X 6000\",\"45032-01\",\"KHJDWNEJWKFD9101529.pdf\",\"2012-02-03\",\"1\"\n" +
"\"STRINGVAL2\",\"273.10 X 21.44 X 6000\",\"7-09372\",\"DJSWH68767681540.pdf\",\"2017-02-03\",\"1\"\n";
string pattern = "\\\"\\s*,\\s*\\\"";
string inputline = "";
StringReader reader = new StringReader(input);
XElement root = new XElement("Root");
while ((inputline = reader.ReadLine()) != null)
{
string[] splitLine = Regex.Split(inputline,pattern);
Item newItem = new Item() {
description = splitLine[0].Replace("\"",""),
length = splitLine[1],
type = splitLine[2],
filename = splitLine[3],
date = DateTime.Parse(splitLine[4]),
authorized = splitLine[5].Replace("\"", "") == "1" ? true : false
};
Item.items.Add(newItem);
}
foreach(var year in Item.items.GroupBy(x => x.date.Year).OrderBy(x => x.Key))
{
XElement newYear = new XElement("_" + year.Key.ToString());
root.Add(newYear);
foreach(var month in year.GroupBy(x => x.date.Month).OrderBy(x => x.Key))
{
XElement newMonth = new XElement("_" + month.Key.ToString());
newYear.Add(newMonth);
newMonth.Add(
month.OrderBy(x => x.date).Select(x => new XElement(
x.filename,
string.Join("\r\n", new object[] {
x.description,
x.length,
x.type,
x.date.ToString(),
x.authorized.ToString()
}).ToList()
)));
}
}
}
}
public class Item
{
public static List<Item> items = new List<Item>();
public string description { get; set; }
public string length { get; set; }
public string type { get; set; }
public string filename { get; set; }
public DateTime date { get; set; }
public Boolean authorized { get; set; }
}
}

Refactor to smaller function, how?

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
}

Categories