I have two classes A and B and I am converting class A to B, and there's one property that is a string but has two date times within one string value.
class A
{
public string dates { get; set; }
}
class B: A
{
public B()
{
Map(m=> m.dates)
.TypeConverter<DateTimeConverter>()
.TypeConverterOption.Format("yyyy-MM-ddTHH:mm:ssz");
}
}
The problem is that when I have one value in the dates, it's working perfectly, but when it's multiple values with a new line splitter, it gives the error.
Working Fine on 2023-01-30T01:00:00
expected output: 2023-01-30T01:00:00z (works as expected)
An issue on 2023-01-30T01:00:00\r\n2023-01-30T01:00:00\r\n
Expected output: 2023-01-30T01:00:00z\r\n2023-01-30T01:00:00z\r\n
Any solution?
you can just use linq to split your string to and, out put it in any format that you want.
class A
{
public string dates { get; set; }
}
class B : A
{
public B()
{
Map();
}
private void Map()
{
var ss = "2017-01-11T10:27:00\r\n2011-09-04T12:30:22\r\n2020-07-17T03:51:20";
var n = ss.Length;
var list = new List<string>();
var sp = ss.Split('\n');
sp.ToList().ForEach(x => list.Add(DateTime.Parse(x).ToString("yyyy-MM-ddTHH:mm:ssz")));
dates = string.Join("__", list.ToArray());
}
}
if it is Windows the "\n" it is works as "\r\n" during splitting the string, the split() method takes argument type char, therefore you can not split it using two chars "\r\n" or string.
if it is Mac change the "\n" with "\r".
Related
I'm working on my first real c# project and I have faced a problem with my way of creating List based on a Class, which I have no idea how to solve.
I’m trying to write some code, which takes an input file (txt/csv) of multiple constructions with multiple layers, put it into my program, and later write the constructions into a new txt/csv file.
When having the same numbers of layers, it works fine. But when the constructions have different numbers of layers it causes trouble and I get a “System.IndexOutOfRangeException”.
My question is: Can I make the Class which I’m basing my List on, dynamic (I don’t know if it is the technical term), so it work with different numbers of inputs? Both when Adding the construction to the program and when I write it to a new file?
My code is:
class Program
{
static void Main(string[] args)
{
// Filepath for the input and output file
string filePathIn_constructions = #"C:\Library\Constructions.txt";
string filePathOut = #"C:\Library\EPlus_Inputfile.txt";
// Creating a list of constructions based on the class. The list is made from the file "filePathIn_constructions"
List<Construction> allConstructions = new List<Construction>();
List<string> lines_constructions = File.ReadAllLines(filePathIn_constructions).ToList(); // add it to a list
// Adding all the data from the fil to the variable "allConstructions"
foreach (var line in lines_constructions)
{
string[] entries = line.Split(',');
Construction newConstruction = new Construction();
newConstruction.EIndex = entries[0];
newConstruction.Name = entries[1];
newConstruction.Layer1 = entries[2];
newConstruction.Layer2 = entries[3];
newConstruction.Layer3 = entries[4];
newConstruction.Layer4 = entries[5];
newConstruction.Layer5 = entries[6];
allConstructions.Add(newConstruction); // Add it to our list of constructions
}
List<string> output = new List<string>();
foreach (var x in allConstructions) // Printing the new
{
output.Add($"{x.EIndex}, {x.Name}, {x.Layer1}, {x.Layer2}, {x.Layer3}, {x.Layer4}, {x.Layer5}");
}
File.WriteAllLines(txtFilePathOut, output);
}
}
My Class for the Constructions is
public class Construction
{
public string EIndex { get; set; }
public string Name { get; set; }
public string Layer1 { get; set; }
public string Layer2 { get; set; }
public string Layer3 { get; set; }
public string Layer4 { get; set; }
public string Layer5 { get; set; }
}
An example of a input/output file could be
Construction,ConcreteWall,Concrete;
Construction,Brickwall1,Birck,Isulation,Brick;
Construction,Brickwall2,Birck,AirGap,Isulation,Brick;
Construction,Wood/Concrete Wall,Wood,Isulation,Concrete,Gypson;
Construction,Wood Wall,Wood,AirGap,Gypson,Isulaiton,Gypson;
I hope someone can help. Thanks.
Edit: I have to be able to excess the construction Name seperatly, because i'm using it to do some sorting of the.
public class Construction
{
public string EIndex { get; set; }
public string Name { get; set; }
public List<string> Layers { get; set; } = new List<string>();
}
foreach (var line in lines_constructions)
{
string[] entries = line.Split(',');
Construction newConstruction = new Construction();
newConstruction.EIndex = entries[0];
newConstruction.Name = entries[1];
for (int i=2; i < entries.Length; i++) {
newConstruction.Layers.Add(entries[i]);
}
allConstructions.Add(newConstruction);
}
foreach(var x in allConstuctions) {
File.AppendAllText(output, $"{x.EIndex}, {x.Name}, {string.Join(", ", x.Layers)}");
}
It is because you are trying to reach a cell of an array that doesn't exist (documentation)
In your input/output file you have lines that have between 3 and 7 values, and you are building an array entries out of those values. This means that you will have arrays with between 3 and 7 cells
The problem is that right after creating those arrays you try to access on every array the cells 0, 1, 2... up to the 7th, even for arrays that have only 3 cells!
What you could do to fix this in a simple way is to add columns to have the same number of separator on each lines (you defined the separator of your lines as column with line.Split(',')). This way, every arrays that you will create will always have 7 cells, even if the value inside is null
I'm creating a program to generate schematics based off of user input. This has to be done dynamically/by hand due to the sheer volume of different possibilities (6.8M, growing exponentially). Right now I'm working on importing some data via CSV.
Example data:
Type,TIN_pos,TIN_ID,Desc
Elect, 0, X, Manual Regulator
Elect, 0, A, Electronic Regulator
Import code:
List<TIN_Fields> values = File.ReadAllLines("C:\\Users\\User\\Desktop\\Visual Basic\\CSV_Test_1.csv")
.Skip(1)
.Select(v => TIN_Fields.FromCsv(v))
.ToList();
public class TIN_Fields
{
public string Type;
public int TIN_pos;
public string TIN_ID;
public string Desc;
public static TIN_Fields FromCsv(string csvLine)
{
string[] values = csvLine.Split(',');
TIN_Fields _Fields = new TIN_Fields();
_Fields.Type = Convert.ToString(values[0]);
_Fields.TIN_pos = Convert.ToInt16(values[1]);
_Fields.TIN_ID = Convert.ToString(values[2]);
_Fields.Desc = Convert.ToString(values[3]);
return _Fields;
}
}
Once that data is Imported, I need to do two things with it,
display the raw csv data in a ListView table, just so users can see if anything in the list needs updating.
be able to compare the items in the list to various characters in a 10-digit hexadecimal code, and spit out some results.
First and foremost, i need to run through the list that was created with the above code, make sure that:
TIN_pos value = 0
because that is the character position of the input box.
Then, with the remaining options, look for the character represented in the input in the TIN_ID field.
Once found, it should then output the Desc field.
Everywhere I have looked says to use foreach, but that requires the array name, which is the part that is confusing me. I've tried filling in basically all of the variables in the FromCSV Method and usually get an error that the class doesn't have a definition.
to hopefully clear up confusion with my explanation, here is the code I created that does the same thing, but with the CSV data hard coded into it, using switch cases and if statements.
public partial class Form1 : Form
{
public string Model_Chassis;
public string Model_Test_Type;
public int ChannelNumberVar => Convert.ToInt32(TextBox_TIN[2]);
public string Tester_Type_Selector;
public string TextBox_TIN
{
get { return TIN_Entry_TextBox.Text; }
set { TIN_Entry_TextBox.Text = value; }
}
public string Model_Data_D
{
get { return Model_Data.Text; }
set { Model_Data.Text = value; }
}
public Form1()
{
InitializeComponent();
}
//Method grabs TIN Box data and decodes it to model information.
public void Model_Select()
{
//Picks Model Chassis
switch (char.ToUpper(TextBox_TIN[0]))
{
case 'H':
{
Model_Chassis = Coding.Model1.description;
}
break;
default:
{
Model_Data_D = "INVALID TIN";
}
break;
}
//Picks Test Type
switch (char.ToUpper(TextBox_TIN[3]))
{
case '0':
{
Model_Test_Type = Test_Types.TT_PD.TT_tt;
}
break;
case '1':
{
Model_Test_Type = Test_Types.TT_PV.TT_tt;
}
break;
default:
{
Model_Test_Type = "";
}
break;
}
//Puts chassis and Test Type together
if (Model_Data_D.Equals("INVALID TIN"))
{
;
}
else if (char.ToUpper(TextBox_TIN[2]).Equals(Coding.Num_Chan_1_2.tin_id))
{
Model_Data_D = $"{Model_Chassis}-{Model_Test_Type}";
}
else
{
Model_Data_D = $"{Model_Chassis}-{TextBox_TIN[2]}{Model_Test_Type}";
}
}
public class Coding
{
public char tin_id;
public string description;
public Coding(char TIN_ID, string Desc)
{
tin_id = TIN_ID;
description = Desc;
}
public static Coding Model1 = new Coding('H', "Model1");
public static Coding Num_Chan_1_2 = new Coding('X', "Single Channel");
public static Coding Elect_Reg_F_1 = new Coding('X', "Manual Regulator");
}
}
INPUT:
HXX0X
OUTPUT
Model1-PD
Thanks in advance for the help!
You're asking quite a few questions, and providing a lot of extra details in here, but for this:
"First and foremost, i need to run through the list that was created with the above code, make sure that:
TIN_pos value = 0
because that is the character position of the input box."
(seeing as you say you need to do this 'first and foremost').
In your FromCsv method, check the value as you create the record, and throw an error if it is invalid. Like this:
public static TIN_Fields FromCsv(string csvLine)
{
string[] values = csvLine.Split(',');
TIN_Fields _Fields = new TIN_Fields();
_Fields.Type = Convert.ToString(values[0]);
_Fields.TIN_pos = Convert.ToInt16(values[1]);
if(_Fields.TIN_pos != 0){
throw new Exception("TIN_pos must be 0");
}
_Fields.TIN_ID = Convert.ToString(values[2]);
_Fields.Desc = Convert.ToString(values[3]);
return _Fields;
}
Assuming you've read in your CSV correctly, which it seems you have, then selecting the appropriate TIN from the list is a simple LINQ statement. The following code assumes that TIN IDs are unique and only a single character in length.
static void Main(string[] args)
{
string testCsv = #"C:\Users\User\Desktop\Visual Basic\CSV_Test_1.csv";
List<TIN_Fields> values = File.ReadAllLines(testCsv)
.Skip(1)
.Select(v => TIN_Fields.FromCsv(v))
.ToList();
// Simulates input received from form
string input = "HXX0X";
TIN_Fields selectedTIN = values.First(x => x.TIN_ID == Convert.ToString(input[0]));
// Insert the description as needed in your ouput.
string output = $"{ selectedTIN.Desc }-";
}
Hopefully that answers another part of the problem. The Convert.ToString() is required because the output of input[0] is a char.
I'm loading a comma delimited file into a list, no problems here. Except the last segment is separated by a semicolon of which I need to put into an array that has a property name. In this case Sequence and Rotation. The code below works except giving it a property name.
Any advice would be much appreciated. I've kept the code below to bare-bones to avoid clutter.
Example CSV File: Bar001,P02;90
class PartDetail
{
public string Description
{
get;
set;
}
public string[] BottomEdge
{
get;
set;
}
public class SpecificDetails << < I want to use this class to specify the property name. {
public string Sequence
{
get;
set;
}
public string Rotation
{
get;
set;
}
}
public PartDetail(string line)
{
string[] parts = line.Split(',');
this.Description = parts[0];
this.BottomEdge = parts[1].Split(';'); << It 's here where I am struggling.
}
}
I think this will do the trick
class PartDetail
{
....
//create an instance of your SpecificDetails class
SpecificDetails Details = new SpecificDetails();
...
public PartDetail(string line)
{
string[] parts = line.Split(',');
this.Description = parts[0];
this.BottomEdge = parts[1].Split(';');
//assign the value to the properties of the "Details" instance
this.Details.Sequence = this.BottomEdge[0];
this.Details.Rotation = this.BottomEdge[1];
}
}
Use line.Split(new char[]{ ',', ';'}) and just pick the last index of the returned array to be the closing segment of your logic.
Dot net has a built in CSV parser, so there is no need to roll your own (https://coding.abel.nu/2012/06/built-in-net-csv-parser/)
using (TextFieldParser parser = new TextFieldParser(path))
{
parser.CommentTokens = new string[] { "#" };
parser.SetDelimiters(new string[] { ";" });
parser.HasFieldsEnclosedInQuotes = true;
// Skip over header line.
parser.ReadLine();
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
}
}
Once parsed, you are left with the semicolon problem, which can easily be solved with string.Split.
So I have this code in a .cs file called SchoolData that has a class and a list.
public static List<YearGroupsData> yearGroupsDataList = new List<YearGroupsData>();
public class YearGroupsData
{
public int id { get; set; }
public int year { get; set; }
public string groupName { get; set; }
public int subject { get; set; }
}
However, I'm trying to use a loop in another .cs script that does a web connection and gets the data from the website, I haven't included the connection info or some of the script for that as this isn't the part going wrong...
private IEnumerator ViewYearGroups()
{
//Some code for connection here
yield return viewYearGroups;
string yearGroupsDataString = viewYearGroups.text;
yearGroups = yearGroupsDataString.Split(';');
foreach (string yearGroup in yearGroups)
{
YearGroupsData yearGroupsData = new YearGroupsData()
{
id = Int32.Parse(GetDataValue(yearGroup, "Id:")),
year = Int32.Parse(GetDataValue(yearGroup, "Year:")),
groupName = GetDataValue(yearGroup, "GroupName:"),
subject = Int32.Parse(GetDataValue(yearGroup, "Subject:")),
};
SchoolData.yearGroupsDataList.Add(yearGroupsData);
}
}
The GetDataValue is the part that is messing up. It gives me ArgumentOutOfRangeException and I'm not sure why. It works if I'm not using it in a loop, I've tried a for loop as well and still the same, anyone know what's happening?
public string GetDataValue(string data, string index)
{
string value = data.Substring(data.IndexOf(index) + index.Length);
if (value.Contains("|"))
{
value = value.Remove(value.IndexOf("|"));
}
return value;
}
Add a try catch in your GetDataValue() method to help with debugging. If it works without the foreach loop, then my guess is one of the string objects you are iterating over is different than what you may be expecting.
https://msdn.microsoft.com/en-us/library/system.argumentoutofrangeexception(v=vs.110).aspx
try
{
string value = data.Substring(data.IndexOf(index) + index.Length);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine(e.Message);
}
Where I used the .Split to divide the string at each semicolon was the issue. The string I was splitting had a space after the last semicolon which was creating an empty index from that. I used
yearGroups = yearGroups.Take(yearGroups.Count() - 1).ToArray();
to remove the last index that was empty. The trycatch method helped me find this, thanks.
the ArgumentOutOfRangeException happen when the value of an argument is outside the allowable range of values as defined by the invoked method
try this:
string value = data.Substring(data.IndexOf(index) + index.Length - 1 );
Can someone give me an example of the best way to return multiple comments from an if statement?
protected string CheckFacility(int FacilityId)
{
var cfacility = new List<string>();
BuildingPresenter b = new BuildingPresenter();
FunctionalAreaPresenter f = new FunctionalAreaPresenter();
if (b.GetBuildings(FacilityId) != null)
{
cfacility.Add("There are Functional Areas associated with this facility. ");
}
if (f.GetFunctionalAreas(FacilityId) != null)
{
cfacility.Add("There are Functional Areas associated with this facility. ");
}
var cfacilitystring = string.Join(",", cfacility);
I'm getting these errors.
Error 3 The best overloaded method match for 'string.Join(string, string[])' has some invalid arguments
Error 4 Argument 2: cannot convert from 'System.Collections.Generic.List' to 'string[]'
var shirtAttributes = new List<string>();
if (shirt.IsBlack)
{
shirtAttributes.Add("black");
}
if (shirt.IsLarge)
{
shirtAttributes.Add("large");
}
if (shirt.IsLongSleeve)
{
shirtAttributes.Add("long sleeve");
}
var shirtAttributesString = string.Join(",", shirtAttributes);
Output is something like: "black, long sleeve" or "black" or "large, long sleeve"
You have many ways to deal with that, you can create a class and override the ToString() method:
public class Shirt{
public Shirt(string color, string size, string sleeve)
{
Color =color;
Size=size;
Sleeve=sleeve;
}
public string Color {get;set;}
public string Size {get;set}
public string Sleeve {get;set}
public override string ToString(){
return string.Format("shirt is color :{0} , size :{1} and shleeve: {2}",Color,Size,Sleeve )
}
}
So when you run your program after initializing your class with values
Shirt myShirt = new Shirt("black","large","long");
if(myShirt.Color=="black"&& myShirt.Size=="large" && myShirt.Sleeve=="long")
{
return myShirt.ToString();
}
else{
return "no match";//or want you want
}
Hope it will help.