Trying to Create an array holding a Struct? - c#

I am trying to have an Array hold a Struct with two elements, to Hold a Xml Tag name and its value.
I would like to have the array working like this:
MyArrayStruct[Count].TagName = "Bla Bla";
MyArrayStruct[Count].TagValue = "Bla Bla Bla";
Could some please help me to get this working.
public struct TagContents
{
String TagName;
String TagValue;
};
I am having problems with declaring the Array as Struct to have it working like i want, i what it to be working like the comment out code.
public void LoadXML()
{
if (File.Exists("Data.xml"))
{
//Readin XML
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("Data.xml");
XmlNodeList dataNodes = xmlDoc.SelectNodes("//FieldData");
//Count the nodes
int Max = 0;
foreach (XmlNode node in dataNodes)
{
Max = Max + 1;
}
int Count = 0;
//TagContents MyXmlPointer = new TagContents();
// MyXmlPointer[] ArrayNode;
// ArrayNode = new MyXmlPointer[Max];
foreach (XmlNode node in dataNodes)
{
// ArrayNode[Count].TagName =node.SelectSingleNode("Have not found what to put here yet but will get there").Name;
// ArrayNode[Count].TagValue =node.SelectSingleNode("Have not found what to put here yet but will get there").InnerText;
}
}
else
{
MessageBox.Show("Could not find file Data.xml");
}
}

Make fields public:
public class TagContent
{
public String TagName;
public String TagValue;
};
and use it, I suggest use generics (Like List<>):
var tags = new List<TagContent>();
tags.Add(new TagContent{TagName = "aaa", TagValue = "vvv"});
// use it:
// get value of 'TagName' of item 5:
var tagname5 = tags[5].TagName;

Related

Resetting the order in schedules

The goal of this macros (app) is to reset the order of elements inside of a schedule we have opened now. Order starts with a user chosen number. Numbers are stored inside every element's parameter that user can specify. We get IDs of elements by hitting "IDs of Selection".
Resetting the order
public void AutoNumerate()
{
Document doc = this.ActiveUIDocument.Document;
ViewSchedule vs = doc.ActiveView as ViewSchedule;
TableData tData = vs.GetTableData();
TableSectionData tsDada = tData.GetSectionData(SectionType.Body);
int startIndex = 1; //SETTING STARTING NUMBER
using (TransactionGroup tGroup = new TransactionGroup(doc,"Numeration: "+vs.Name))
{
tGroup.Start();
for (int rInd = 0; rInd < tsDada.NumberOfRows; rInd++)
{
SetNum(doc,startIndex++, );
}
tGroup.Assimilate();
}
}
public void SetNum(Document doc, int num, List<Element> Myelements)
{
using (Transaction tr = new Transaction(doc,"Creating elements based on IDs"))
{
tr.Start();
//List<ElementId> eleIds = new List<ElementId>{946164,946385,946484,946631,946708,946759,946816};
int[] eleIds = {946164,946385,946484,946631,946708,946759,946816};
foreach (int id in eleIds)
{
List<Element> MyElements = GetElement();
}
foreach (Element ele in MyElements)
{
//ele.LookupParameter("Comments").Set(num.ToString());
ele.LookupParameter("Comments").Set(num.ToString()).ToList(); //SPECIFYING PARAMETER
}
tr.Commit();
}
}
private List<Element> GetElementsOnRow(Document doc, ViewSchedule vs, int rowNumber)
{
TableData tableData = vs.GetTableData();
TableSectionData tableSectionData = tableData.GetSectionData(SectionType.Body);
To sort a schedule you need to use the ScheduleDefinition.AddSortGroupField method. Create a ScheduleSortGroupField from the ScheduleFieldId of the 'Comments' parameter to input into the method.

Elements in list are getting overwritten when adding to list

I'm reading in tags and values from an XML file for information that is needed for a functional test software. When adding the Frequencies to the list, the frequencies for all other existing list items are overwritten by the new frequencies.
public struct Configuration
{
public string Description;
public byte Mode;
public byte Deviation;
public string PartNumber;
public UInt16[] Frequencies;
public byte TxPower;
public byte PWM;
public Int16 HeaterSetpoint;
public string FirmwareVersion;
public bool Active;
public int Threshold;
} // struct Configuration
Configurations = new List<Configuration>();
ushort[] Frequencies = new ushort [7];
Configuration TestConfig = new Configuration();
/*Open the XML file, load all the tag values into XmlNodeList */
//Pull in all the XML Tag Values Sorted by Tag Name
XmlNodeList xml_description = ConfigFile.GetElementsByTagName("description");
XmlNodeList xml_mode = ConfigFile.GetElementsByTagName("mode");
XmlNodeList xml_deviation = ConfigFile.GetElementsByTagName("deviation");
XmlNodeList xml_partnumber = ConfigFile.GetElementsByTagName("partnumber");
XmlNodeList xml_channel0 = ConfigFile.GetElementsByTagName("channel0");
XmlNodeList xml_channel1 = ConfigFile.GetElementsByTagName("channel1");
XmlNodeList xml_channel2 = ConfigFile.GetElementsByTagName("channel2");
XmlNodeList xml_channel3 = ConfigFile.GetElementsByTagName("channel3");
XmlNodeList xml_channel4 = ConfigFile.GetElementsByTagName("channel4");
XmlNodeList xml_channel5 = ConfigFile.GetElementsByTagName("channel5");
XmlNodeList xml_channel6 = ConfigFile.GetElementsByTagName("channel6");
XmlNodeList xml_txpower = ConfigFile.GetElementsByTagName("txpower");
XmlNodeList xml_pwm = ConfigFile.GetElementsByTagName("pwm");
XmlNodeList xml_hsp = ConfigFile.GetElementsByTagName("hsp");
XmlNodeList xml_firmware = ConfigFile.GetElementsByTagName("firmware");
XmlNodeList xml_active = ConfigFile.GetElementsByTagName("active");
XmlNodeList xml_threshold = ConfigFile.GetElementsByTagName("threshold");
/*Use LINQ to check that the XmlNodeLists are all the same length. This way we don't try and load invalid data*/
if(!listsaresamelength)
{
/*Alert user there is a problem with the XML file and close the application*/
}
//Loop to add values to the List
for(i = 0; i < NumberofXmlValues; i++)
{
/*check that the description, Mode, Deviation are valid and add them to DummyConfig*/
try
{
Frequencies[0] = UInt16.Parse(xml_channel0[i].InnerText);
Frequencies[1] = UInt16.Parse(xml_channel1[i].InnerText);
Frequencies[2] = UInt16.Parse(xml_channel2[i].InnerText);
Frequencies[3] = UInt16.Parse(xml_channel3[i].InnerText);
Frequencies[4] = UInt16.Parse(xml_channel4[i].InnerText);
Frequencies[5] = UInt16.Parse(xml_channel5[i].InnerText);
Frequencies[6] = UInt16.Parse(xml_channel6[i].InnerText);
//Move the frequencies to the list
TestConfig.Frequencies = Frequencies;
}
catch
{
/*Problem with the XML file. Alert the user and close the application*/
}
/*check that the heater set point, PWM set point, partnumber, Txpower, and Threshold are valid and add them to DummyConfig*/
Configurations.Add(TestConfig)
}
The first iteration of the for loop works fine; here's the result:
first loop iteration
second loop iteration
I've tried a few different ways to change how I'm writing to the list, but they all generate the same result. I feel like I'm missing something obvious.
You need to move your TestConfig and Frequencies variables' instantiation into the for loop so that you're working with a different object each time.
for(i = 0; i < NumberofXmlValues; i++)
{
TestConfig = new Configuration();
Frequencies = new ushort [7];
I'd personally recommend making these locally-declared variables rather than persistent fields. That pattern will help you avoid mistakes like this in the future.
for(i = 0; i < NumberofXmlValues; i++)
{
/*check that the description, Mode, Deviation are valid*/
...
try
{
var testConfig = new Config
{
Description = description
...
Frequencies = new [] {xml_channel0, xml_channel1, ...}
.Select(c => UInt16.Parse(c[i].InnerText))
.ToArray()
};
Configurations.Add(testConfig);
}
...

Dynamically create variables from splitting string c#

I am making a web service for an app with Tesseract Ocr 3.02.
I want to create variables on depending of how many informations I get on the business card and after that classify information from a string.
For example:
Tel. +496123456789$Mobil +49123456789$kai.kalsbach#gmail.com$www.google.com$Kai Kalsbach$Muster Str 1a$40599 Düsseldorf$"
And then like this:
-Telephone number
-First Name
-Last Name
-Email
-Address
That was my first idea:
string endText1 = text.Split('$')[0];
string endText2 = text.Split('$')[1];
string endText3 = text.Split('$')[2];
string endText4 = text.Split('$')[3];
string endText5 = text.Split('$')[4];
string endText6 = text.Split('$')[5];
string endText7 = text.Split('$')[6];
string endText8 = text.Split('$')[7];
and after that i would classify the variables.
but in many cases I get the following exception because the number of informations can vary depending of business card.
System.IndexOutOfRangeException: Index was outside the bounds of the array c#
The IndexOutOfRangeException exception is thrown because the code tries to access an item outside the array length.
My proposition : I created formattedArray with contains always 8 items and I copied the splited array to this formattedArray. With that, you have no more IndexOutOfRangeException because the item missing in text.Split('$') is null in formattedArray
var a = text.Split('$');
var formattedArray = new string[8];
Array.Copy(a, formattedArray, a.Length);
string endText1 = formattedArray [0];
string endText2 = formattedArray [1];
string endText3 = formattedArray [2];
string endText4 = formattedArray [3];
string endText5 = formattedArray [4];
string endText6 = formattedArray [5];
string endText7 = formattedArray [6];
string endText8 = formattedArray [7];
string[] Splitted = text.Split('$');
And you mentioned you want to make a decision based on the number of elements the split spits out
int Count = Splitted.Length;
switch(Count)
{ case 0: //DoStuff
break;
....
default:
break;
}
In your case, it is better to use the following:
string[] stringList = text.Split('$');
foreach(string val in stringList)
{
//your logic.
}
You can split the string once using the .Split method.
Then afterwards run it in a foreach or for loop. I believe your logic is based on the amount of strings, so you are looking for a 'for' loop.
string[] split = text.Split('$');
for (int i = 0; i < split.Length; i++)
{
var text = split[i];
// Your logic here...
switch (i) // for logic based on the index of the string
{
case 0:
// do something
break;
case 1:
// do something
break;
}
}
The IndexOutOfRangeException exception is thrown because the code tries to access the 8th item in a 7-item array :
string endText8 = text.Split('$')[7];
Indexes in .NET collections are 0-based which means 7 refers to the 8th element.
By default, String.Split will return empty fields as well. This means that either the string isn't the same as the one posted here, or that the StringSplitOptions.RemoveEmptyEntries was used
String.Split returns a string array that can be stored in a string[] variable. There's no need to repeat String.Split, or use multiple variables :
var items = text.Split(new[]{'$'},StringSplitOptions.RemoveEmptyEntries);
Creating a class from this array is simple enough that you probably don't need to create a custom parser :
class Record
{
public string Telephone {get;set;}
...
}
var items = text.Split('$');
var record=new Record
{
Telephone=items[0],
Mobile=items[1],
...
};
Another easy way to do that is to use a try, then all variables will be created until the index has reached its maximum.
string[] strArray = text.Split('$');
Try {
string endText1 = strArray[0];
string endText2 = strArray[1];
string endText3 = strArray[2];
string endText4 = strArray[3];
string endText5 = strArray[4];
string endText6 = strArray[5];
string endText7 = strArray[6];
string endText8 = strArray[7];
}
catch
{
//nothing
}
Create factory and recognizers
public class PhoneItem : IItem
{
public PhoneItem(string text)
{
// some code
}
}
public interface IRecognizer
{
IItem Recognize(int index, string text);
}
public class PhoneRecognizer : IRecognizer
{
public IItem Recognize(int index, string text)
{
return index == 0 ? new PhoneItem(text) : null;
}
}
public class ItemFactory
{
private IEnumerable<IRecognizer> _recognizers = new []
{
new PhoneRecognizer(),
new FullNameRecognizer()
};
public IItem CreateItem(int index, string text)
{
foreach (var rec in _recognizers)
{
var item = rec.Recognize(index, text);
if (item != null)
{
return item;
}
}
throw new Exception("Item not recognized");
}
}
Split string to pieces
var parts = text.Split('$');
Use the factory to create objects
var factory = new ItemFactory();
var items = new List<IItem>();
for (int i = 0; i < parts.Length; i++)
{
items.Add(factory.CreateItem(i, parts[i]));
}
// do whatever you wants

What is the easiest way to split columns from a txt file

I've been looking around a bit but haven't really found a good example with what I'm struggling right now.
I have a .txt file with a couple of columns as follows:
# ID,YYYYMMDD, COLD,WATER, OD, OP,
52,20120406, 112, 91, 20, 130,
53,20130601, 332, 11, 33, 120,
And I'm reading these from the file into a string[] array.
I'd like to split them into a list
for example
List results, and [0] index will be the first index of the columns
results[0].ID
results[0].COLD
etc..
Now I've been looking around, and came up with the "\\\s+" split
but I'm not sure how to go about it since each entry is under another one.
string[] lines = File.ReadAllLines(path);
List<Bus> results = new List<Bus>();
//Bus = class with all the vars in it
//such as Bus.ID, Bus.COLD, Bus.YYYYMMDD
foreach (line in lines) {
var val = line.Split("\\s+");
//not sure where to go from here
}
Would greatly appreciate any help!
Kind regards, Venomous.
I suggest using Linq, something like this:
List<Bus> results = File
.ReadLines(#"C:\MyFile.txt") // we have no need to read All lines in one go
.Skip(1) // skip file's title
.Select(line => line.Split(','))
.Select(items => new Bus( //TODO: check constructor's syntax
int.Parse(items[1]),
int.Parse(items[3]),
DateTime.ParseExact(items[2], "yyyyMMdd", CultureInfo.InvariantCulture)))
.ToList();
I would do
public class Foo
{
public int Id {get; set;}
public string Date {get; set;}
public double Cold {get; set;}
//...more
}
Then read the file
var l = new List<Foo>();
foreach (line in lines)
{
var sp = line.Split(',');
var foo = new Foo
{
Id = int.Parse(sp[0].Trim()),
Date = sp[1].Trim(),//or pharse the date to a date time struct
Cold = double.Parse(sp[2].Trim())
}
l.Add(foo);
}
//now l contains a list filled with Foo objects
I would probably keep a List of properties and use reflection to populate the object, something like this :
var columnMap = new[]{"ID","YYYYMMDD","COLD","WATER","OD","OP"};
var properties = columnMap.Select(typeof(Bus).GetProperty).ToList();
var resultList = new List<Bus>();
foreach(var line in lines)
{
var val = line.Split(',');
var adding = new Bus();
for(int i=0;i<val.Length;i++)
{
properties.ForEach(p=>p.SetValue(adding,val[i]));
}
resultList.Add(adding);
}
This is assuming that all of your properties are strings however
Something like this perhaps...
results.Add(new Bus
{
ID = val[0],
YYYYMMDD = val[1],
COLD = val[2],
WATER = val[3],
OD = val[4],
OP = val[5]
});
Keep in mind that all of the values in the val array are still strings at this point. If the properties of Bus are typed, you will need to parse them into the correct types e.g. assume ID is typed as an int...
ID = string.IsNullOrEmpty(val[0]) ? default(int) : int.Parse(val[0]),
Also, if the column headers are actually present in the file in the first line, you'll need to skip/disregard that line and process the rest.
Given that we have the Bus class with all the variables from your textfile:
class Bus
{
public int id;
public DateTime date;
public int cold;
public int water;
public int od;
public int op;
public Bus(int _id, DateTime _date, int _cold, int _water, int _od, int _op)
{
id = _id;
date = _date;
cold = _cold;
water = _water;
od = _od;
op = _op;
}
}
Then we can list them all in the results list like this:
List<Bus> results = new List<Bus>();
foreach (string line in File.ReadAllLines(path))
{
if (line.StartsWith("#"))
continue;
string[] parts = line.Replace(" ", "").Split(','); // Remove all spaces and split at commas
results.Add(new Bus(
int.Parse(parts[0]),
DateTime.ParseExact(parts[1], "yyyyMMdd", CultureInfo.InvariantCulture),
int.Parse(parts[2]),
int.Parse(parts[3]),
int.Parse(parts[4]),
int.Parse(parts[5])
));
}
And access the values as you wish:
results[0].id;
results[0].cold;
//etc.
I hope this helps.

C# Sort Object[] Array within ArrayList?

I am not sure how to go about sorting the ArrayList which contains an object Array which has a DateTime object and a String object. I am ultimately trying to sort the ArrayList based on the first object (DateTime) of the array within the ArrayList.
I've tried to search around for sorting but articles didn't seem to go into the level of detail or this particular use-case the application is hitting. I'm not sure if this is the best way to deal with the data either but any help suggestion will certainly be appreciative.
The goal is to read mutliple XML files and combine all the Lap data out of the all the XML files and sort them from oldest to most recent then to create a new XML file with the combined sorted information in it.
ArrayList LapsArrayList = new ArrayList();
ListBox.SelectedObjectCollection SelectedItems = lstSelectedFiles.SelectedItems;
foreach (string Selected in SelectedItems)
{
Object[] LapArray = new Object[2];
XmlDocument xDoc = new XmlDocument();
xDoc.Load(Path + #"\" + Selected);
XmlNodeList Laps = xDoc.GetElementsByTagName("Lap");
foreach (XmlElement Lap in Laps)
{
LapArray[0] = DateTime.Parse(Lap.Attributes[0].Value);
LapArray[1] = Lap.InnerXml.ToString();
LapsArrayList.Add(LapArray);
}
}
XML Data Example
<Lap StartTime="2013-06-17T12:27:21Z"><TotalTimeSeconds>12705.21</TotalTimeSeconds><DistanceMeters>91735.562500</DistanceMeters><MaximumSpeed>10.839000</MaximumSpeed><Calories>3135</Calories><AverageHeartRateBpm><Value>151</Value>.....</Lap>
This are my recommendations:
Use a class for the items you want to sort, I suggest a Tuple<T1, T2>.
Use a List<T> because it is a typed list so you avoid casts and it is more convenient in general.
We are going to use Linq to sort the array just for easy writting.
I list the code below:
//I dunno what does this has to do, but I'll leave it here
ListBox.SelectedObjectCollection SelectedItems = lstSelectedFiles.SelectedItems;
//We are going to use a List<T> instead of ArrayList
//also we are going to use Tuple<DateTime, String> for the items
var LapsList = new List<Tuple<DateTime, String>>();
foreach (string Selected in SelectedItems)
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load(Path + #"\" + Selected);
XmlNodeList Laps = xDoc.GetElementsByTagName("Lap");
foreach (XmlElement Lap in Laps)
{
var dateTime = DateTime.Parse(Lap.Attributes[0].Value);
var str = Lap.InnerXml.ToString();
//Here we create the tuple and add it
LapsList.Add(new Tuple<DateTime, String>(dateTime, str));
}
}
//We are sorting with Linq
LapsList = LapsList.OrderBy(lap => lap.Item1).ToList();
If you can't use tuples, declare you own class for the item. For example
class Lap
{
private DateTime _dateTime;
private String _string;
public Lap (DateTime dateTimeValue, String stringValue)
{
_dateTime = dateTimeValue;
_string = stringValue;
}
public DateTime DateTimeValue
{
get
{
return _dateTime;
}
set
{
_dateTime = value;
}
}
public String StringValue
{
get
{
return _string;
}
set
{
_string = value;
}
}
}
With this class you can do a easy migration of the code as follows:
//I dunno what does this has to do, but I'll leave it here
ListBox.SelectedObjectCollection SelectedItems = lstSelectedFiles.SelectedItems;
//We are going to use a List<T> instead of ArrayList
//also we are going to use the Laps class for the items
var LapsList = new List<Lap>();
foreach (string Selected in SelectedItems)
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load(Path + #"\" + Selected);
XmlNodeList Laps = xDoc.GetElementsByTagName("Lap");
foreach (XmlElement Lap in Laps)
{
var dateTime = DateTime.Parse(Lap.Attributes[0].Value);
var str = Lap.InnerXml.ToString();
//Here we create the Lap object and add it
LapsList.Add(new Lap(dateTime, str));
}
}
//We are sorting with Linq
LapsList = LapsList.OrderBy(lap => lap.DateTimeValue).ToList();
If you can't use Linq, here is a non Linq alternative:
LapsList.Sort
(
delegate(Tuple<DateTime, String> p1, Tuple<DateTime, String> p2)
{
return p1.Item1.CompareTo(p2.Item1);
}
);
Or for the case of using the Lap class:
LapsList.Sort
(
delegate(Lap p1, Lap p2)
{
return p1.DateTimeValue.CompareTo(p2.DateTimeValue);
}
);

Categories