select only one random node from linq to xml - c#

I have an XML file and I want to select only one random node. It seems like I'm almost there but the foreach with the var is looping. How do I select only one node and return it?
XML:
<human_check>
<qa>
<q>2 + 2</q>
<a>4</a>
</qa>
<qa>
<q>1 + 2</q>
<a>3</a>
</qa>
<qa>
<q>6 + 3</q>
<a>9</a>
</qa>
<qa>
<q>3 + 5</q>
<a>7</a>
</qa>
</human_check>
C#
public class human_check
{
public static string get_q()
{
try
{
string h = string.Empty;
Random rnd = new Random();
XDocument questions = XDocument.Load(#"C:\Users\PETERS\Desktop\human_check.xml");
var random_q = from q in questions.Descendants("qa")
select new
{
question = q.Descendants("q").OrderBy(r => rnd.Next()).First().Value
};
foreach (var rq in random_q)
{
h = rq.question.ToString();
}
return h;
}
catch (Exception ex)
{
throw ex;
}
}
}
Thanks in advance,
EP

Instead of setting up an ordering you could just pick a random element.
var qas = questions.Descendants("qa");
int qaCount = qas.Count();
h = qas.ElementAt(rnd.Next(0, qaCount - 1)).Element("q").Value;

var random_q = (from q in questions.Descendants("qa")
select q).OrderBy(r => rnd.Next()).First();
h = random_q.Descendants("q").SingleOrDefault().Value.ToString();

Related

Parse() Can't Get All Element

i have smailler XML file i want to parse it .
<?xml version="1.0" encoding="utf-8"?>
<!-- GeeSuth Checker Time -->
<Geranal>
<AftrNoon Min="20" StartAftrNoon="05:00:00" EndAftrNoon="11:01:00" />
<Night Min="50" StartNight="2:00:00" EndNight="6:00:00" />
<AlFajr Min="100" StartAlfajr="9:00:00" EndAlfajr="10:00:00" />
</Geranal>
i want to get all the value in line , like
<AftrNoon Min="20" StartAftrNoon="05:00:00" EndAftrNoon="11:01:00" />
i need save the values in string paramater :.
Min
StartAftrNoon
EndAftrNoon
and save it in paramater ?
im using this :.
XmlReader ReaderXML = XmlReader.Create("Date.xml");
while (ReaderXML.Read())
{
ReaderXML.IsStartElement("Geranal");
if (ReaderXML.NodeType == XmlNodeType.Element && ReaderXML.Name == "AftrNoon")
{
//AftarNoon Fill
txt_Min_Aftrnoon.Text = ReaderXML.GetAttribute(0);
dt_Aftr_start.Text = ReaderXML.GetAttribute(1);
dt_aftar_End.Text = ReaderXML.GetAttribute(2);
}
if (ReaderXML.NodeType == XmlNodeType.Element && ReaderXML.Name == "Night")
{
txt_Min_Night.Text = ReaderXML.GetAttribute(0);
dt_Night_Start.Text = ReaderXML.GetAttribute(1);
dt_Night_end.Text = ReaderXML.GetAttribute(2);
}
if (ReaderXML.NodeType == XmlNodeType.Element && ReaderXML.Name == "AlFajr")
{
txt_Min_Fajr.Text = ReaderXML.GetAttribute(0);
dt_Fajr_Start.Text = ReaderXML.GetAttribute(1);
dt_fajar_end.Text = ReaderXML.GetAttribute(2);
}
}
It's Not get all elements value.
Just put all name/value pairs to a dictionary. Using Linq2Xml
var values = XDocument.Load(filename)
.Descendants("AftrNoon")
.First()
.Attributes()
.ToDictionary(a => a.Name, a => a.Value);
Now you can access them like
var min = values["Min"];
or
foreach(var kv in values)
{
Console.WriteLine(kv.Key + ":" + kv.Value);
}
To start with your XML has a small issue with the naming of the attributes that makes it hard to parse - the element AlFajr has a capital F, yet the attributes do not. If you can fix that then this code works nicely:
var xd = XDocument.Load("Date.xml");
var nodes =
(
from e in xd.Root.Elements()
let Min = e.Attribute("Min").Value
let Start = e.Attribute("Start" + e.Name.LocalName).Value
let End = e.Attribute("End" + e.Name.LocalName).Value
select new { e.Name, Min, Start, End, }
).ToDictionary(x => x.Name, x => new { x.Min, x.Start, x.End });
That gives me this:
Now I can use that to populate your fields very easily:
txt_Min_Aftrnoon.Text = nodes["AftrNoon"].Min;
dt_Aftr_start.Text = nodes["AftrNoon"].Start;
dt_aftar_End.Text = nodes["AftrNoon"].End;
txt_Min_Night.Text = nodes["Night"].Min;
dt_Night_Start.Text = nodes["Night"].Start;
dt_Night_end.Text = nodes["Night"].End;
txt_Min_Fajr.Text = nodes["AlFajr"].Min;
dt_Fajr_Start.Text = nodes["AlFajr"].Start;
dt_fajar_end.Text = nodes["AlFajr"].End;
Alternatively, you could also set up a dictionary for your text boxes and, using the above code, assign the values like this:
var textBoxes = new []
{
new { Name = "AftrNoon", Min = txt_Min_Aftrnoon, Start = dt_Aftr_start, End = dt_aftar_End },
new { Name = "Night", Min = txt_Min_Night, Start = dt_Night_Start, End = dt_Night_end },
new { Name = "AlFajr", Min = txt_Min_Fajr, Start = dt_Fajr_Start, End = dt_fajar_end },
};
foreach (var tb in textBoxes)
{
tb.Min.Text = nodes[tb.Name].Min;
tb.Start.Text = nodes[tb.Name].Start;
tb.End.Text = nodes[tb.Name].End;
}
Another alternative, that eliminates the need to fix the attribute naming issue, is to just do this:
var xd = XDocument.Load("Date.xml");
txt_Min_Aftrnoon.Text = xd.Root.Element("AftrNoon").Attribute("Min").Value;
dt_Aftr_start.Text = xd.Root.Element("AftrNoon").Attribute("StartAftrNoon").Value;
dt_aftar_End.Text = xd.Root.Element("AftrNoon").Attribute("EndAftrNoon").Value;
txt_Min_Night.Text = xd.Root.Element("Night").Attribute("Min").Value;
dt_Night_Start.Text = xd.Root.Element("Night").Attribute("StartNight").Value;
dt_Night_end.Text = xd.Root.Element("Night").Attribute("EndNight").Value;
txt_Min_Fajr.Text = xd.Root.Element("AlFajr").Attribute("Min").Value;
dt_Fajr_Start.Text = xd.Root.Element("AlFajr").Attribute("StartAlfajr").Value;
dt_fajar_end.Text = xd.Root.Element("AlFajr").Attribute("EndAlfajr").Value;

c# XML to linq error if null

I have a method that is parsing an XML file.
My method is:
public static List<UpperLevelGPS> ParseXml(string Document)
{
List<UpperLevelGPS> result = new List<UpperLevelGPS>();
result.Clear();
doc = XDocument.Load(Document);
result = (from n in doc.Descendants("level")
select new UpperLevelGPS()
{
CurrentLevel = int.Parse(n.Attribute("CurrentLevel").Value),
TeleNodes = (from l in n.Element("UpperLevelGPSs").Elements("UpperLevelGPS")
select new TeleNodes()
{
id = (int)(l.Attribute("id")),
UpperLevelGPSMapID = (int)l.Attribute("UpperLevelGPSMapID"),
DestinationMapID = (int)l.Attribute("DestinationMapID"),
HostelID = (int)l.Attribute("HostelID"),
x = (float)l.Attribute("x"),
y = (float)l.Attribute("y"),
z = (float)l.Attribute("z")
}).ToList()
}).ToList();
return result;
}
Node UpperLevelGPSs won't allways exists in my XML file, so above query fails. How can i catch and handle null event?
Check if it does before the linq statment:
public static List<UpperLevelGPS> ParseXml(string Document)
{
List<UpperLevelGPS> result = new List<UpperLevelGPS>();
result.Clear();
doc = XDocument.Load(Document);
var upperLevelGPSs = n.Element("UpperLevelGPSs");
if (upperLevelGPSs.Count > 0)
{
result = (from n in doc.Descendants("level")
select new UpperLevelGPS()
{
CurrentLevel = int.Parse(n.Attribute("CurrentLevel").Value),
TeleNodes = (from l in upperLevelGPSs .Elements("UpperLevelGPS")
select new TeleNodes()
{
id = (int)(l.Attribute("id")),
UpperLevelGPSMapID = (int)l.Attribute("UpperLevelGPSMapID"),
DestinationMapID = (int)l.Attribute("DestinationMapID"),
HostelID = (int)l.Attribute("HostelID"),
x = (float)l.Attribute("x"),
y = (float)l.Attribute("y"),
z = (float)l.Attribute("z")
}).ToList()
}).ToList();
}
return result;
}
Note: I have not tested this. Let me know if you have any issues.
{
id = (int)(l.Attribute("id")),
UpperLevelGPSMapID = (int?)l.Attribute("UpperLevelGPSMapID"),
DestinationMapID = (int)l.Attribute("DestinationMapID"),
HostelID = (int)l.Attribute("HostelID"),
x = (float)l.Attribute("x"),
y = (float)l.Attribute("y"),
z = (float)l.Attribute("z")
}).ToList()
}).ToList();
(Int?) Will result in null value if attribute in not present.

Get the positions of unique elements in a string[]

I have an xml file that I am accessing to create a report of time spent on a project. I'm returning the unique dates to a label created dynamically on a winform and would like to compile the time spent on a project for each unique date. I have been able to return all of the projects under each date or only one project. Currently I'm stuck on only returning one project. Can anyone please help me?? This is what the data should look like if it's correct.
04/11/15
26820 2.25
27111 8.00
04/12/15
26820 8.00
04/13/15
01det 4.33
26820 1.33
27225 4.25
etc.
This is how I'm retrieving the data
string[] weekDateString = elementDateWeekstring();
string[] uniqueDates = null;
string[] weeklyJobNumber = elementJobNumWeek();
string[] weeklyTicks = elementTicksWeek();
This is how I'm getting the unique dates.
IEnumerable<string> distinctWeekDateIE = weekDateString.Distinct();
foreach (string d in distinctWeekDateIE)
{
uniqueDates = distinctWeekDateIE.ToArray();
}
And this is how I'm creating the labels.
try
{
int dateCount;
dateCount = uniqueDates.Length;
Label[] lblDate = new Label[dateCount];
int htDate = 1;
int padDate = 10;
for (int i = 0; i < dateCount; i++ )
{
lblDate[i] = new Label();
lblDate[i].Name = uniqueDates[i].Trim('\r');
lblDate[i].Text = uniqueDates[i];
lblDate[i].TabIndex = i;
lblDate[i].Bounds = new Rectangle(18, 275 + padDate + htDate, 75, 22);
targetForm.Controls.Add(lblDate[i]);
htDate += 22;
foreach (string x in uniqueDates)
{
int[] posJobNumber;
posJobNumber = weekDateString.Select((b, a) => b == uniqueDates[i].ToString() ? a : -1).Where(a => a != -1).ToArray();
for (int pjn = 0; pjn < posJobNumber.Length; pjn++)
{
if (x.Equals(lblDate[i].Text))
{
Label lblJobNum = new Label();
int htJobNum = 1;
int padJobNum = 10;
lblJobNum.Name = weeklyJobNumber[i];
lblJobNum.Text = weeklyJobNumber[i];
lblJobNum.Bounds = new Rectangle(100, 295 + padJobNum + htJobNum, 75, 22);
targetForm.Controls.Add(lblJobNum);
htJobNum += 22;
htDate += 22;
padJobNum += 22;
}
}
}
}
}
I've been stuck on this for about 3 months. Is there anyone that can describe to me why I'm not able to properly retrieve the job numbers that are associated with a particular date. I don't believe that these are specifically being returned as dates. Just a string that looks like a date.
I really appreciate any help I can get. I'm just completely baffled. Thank you for any responses in advance. I truly appreciate the assistance.
EDIT: #Sayka - Here is the xml sample.
<?xml version="1.0" encoding="utf-8"?>
<Form1>
<Name Key="4/21/2014 6:51:17 AM">
<Date>4/21/2014</Date>
<JobNum>26820</JobNum>
<RevNum>00000</RevNum>
<Task>Modeling Secondary</Task>
<Start>06:51 AM</Start>
<End>04:27 PM</End>
<TotalTime>345945089017</TotalTime>
</Name>
<Name Key="4/22/2014 5:44:22 AM">
<Date>4/22/2014</Date>
<JobNum>26820</JobNum>
<RevNum>00000</RevNum>
<Task>Modeling Secondary</Task>
<Start>05:44 AM</Start>
<End>06:56 AM</End>
<TotalTime>43514201221</TotalTime>
</Name>
<Name Key="4/22/2014 6:57:02 AM">
<Date>4/22/2014</Date>
<JobNum>02e-n-g</JobNum>
<RevNum>00000</RevNum>
<Task>NET Eng</Task>
<Start>06:57 AM</Start>
<End>07:16 AM</End>
<TotalTime>11706118875</TotalTime>
</Name>
....
</Form1>
This is how I'm getting the information out of the xml file and returning a string[].
public static string[] elementDateWeekstring()
{
//string datetxtWeek = "";
XmlDocument xmldoc = new XmlDocument();
fileExistsWeek(xmldoc);
XmlNodeList nodeDate = xmldoc.GetElementsByTagName("Date");
int countTicks = 0;
string[] dateTxtWeek = new string[nodeDate.Count];
for (int i = 0; i < nodeDate.Count; i++)
{
dateTxtWeek[i] = nodeDate[i].InnerText;
countTicks++;
}
return dateTxtWeek;
}
Job number and Ticks are returned in a similar fashion. I've been able to reuse these snippets throught out the code. This is a one dimensional xml file?? It will always return a position for a jobnumber that equates to a date or Ticks. I will never have more or less of any one element.
You can use Linq-to-XML to parse the XML file, and then use Linq-to-objects to group (and order) the data by job date and order each group by job name.
The code to parse the XML file is like so:
var doc = XDocument.Load(filename);
var jobs = doc.Descendants("Name");
// Extract the date, job number, and total time from each "Name" element.:
var data = jobs.Select(job => new
{
Date = (DateTime)job.Element("Date"),
Number = (string)job.Element("JobNum"),
Duration = TimeSpan.FromTicks((long)job.Element("TotalTime"))
});
The code to group and order the jobs by date and order the groups by job name is:
var result =
data.GroupBy(job => job.Date).OrderBy(g => g.Key)
.Select(g => new
{
Date = g.Key,
Jobs = g.OrderBy(item => item.Number)
});
Then you can access the data by iterating over each group in result and then iterate over each job in the group, like so:
foreach (var jobsOnDate in result)
{
Console.WriteLine("{0:d}", jobsOnDate.Date);
foreach (var job in jobsOnDate.Jobs)
Console.WriteLine(" {0} {1:hh\\:mm}", job.Number, job.Duration);
}
Putting this all together in a sample compilable console application (substitute the filename for the XML file as appropriate):
using System;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
private static void Main()
{
string filename = #"d:\test\test.xml"; // Substitute your own filename here.
// Open XML file and get a collection of each "Name" element.
var doc = XDocument.Load(filename);
var jobs = doc.Descendants("Name");
// Extract the date, job number, and total time from each "Name" element.:
var data = jobs.Select(job => new
{
Date = (DateTime)job.Element("Date"),
Number = (string)job.Element("JobNum"),
Duration = TimeSpan.FromTicks((long)job.Element("TotalTime"))
});
// Group the jobs by date, and order the groups by job name:
var result =
data.GroupBy(job => job.Date).OrderBy(g => g.Key)
.Select(g => new
{
Date = g.Key,
Jobs = g.OrderBy(item => item.Number)
});
// Print out the results:
foreach (var jobsOnDate in result)
{
Console.WriteLine("{0:d}", jobsOnDate.Date);
foreach (var job in jobsOnDate.Jobs)
Console.WriteLine(" {0} {1:hh\\:mm}", job.Number, job.Duration);
}
}
}
}
The output is like this
Create a new project
Set form size bigger.
Apply these codes.
Set the location for your XML file.
Namespaces
using System.Xml;
using System.IO;
Form Code
public partial class Form1 : Form
{
const string XML_FILE_NAME = "D:\\emps.txt";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
prepareDataGrid();
List<JOBS> jobsList = prepareXML(XML_FILE_NAME);
for (int i = 0; i < jobsList.Count; i++)
{
addDateRow(jobsList[i].jobDate.ToString("M'/'d'/'yyyy"));
for (int j = 0; j < jobsList[i].jobDetailsList.Count; j++)
dgv.Rows.Add(new string[] {
jobsList[i].jobDetailsList[j].JobNumber,
jobsList[i].jobDetailsList[j].JobHours
});
}
}
DataGridView dgv;
void prepareDataGrid()
{
dgv = new DataGridView();
dgv.BackgroundColor = Color.White;
dgv.GridColor = Color.White;
dgv.DefaultCellStyle.SelectionBackColor = Color.White;
dgv.DefaultCellStyle.SelectionForeColor = Color.Black;
dgv.DefaultCellStyle.ForeColor = Color.Black;
dgv.DefaultCellStyle.BackColor = Color.White;
dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
dgv.Width = 600;
dgv.Dock = DockStyle.Left;
this.BackColor = Color.White;
dgv.Columns.Add("Col1", "Col1");
dgv.Columns.Add("Col2", "Col2");
dgv.Columns[0].Width = 110;
dgv.Columns[1].Width = 40;
dgv.DefaultCellStyle.Font = new System.Drawing.Font("Segoe UI", 10);
dgv.RowHeadersVisible = dgv.ColumnHeadersVisible = false;
dgv.AllowUserToAddRows =
dgv.AllowUserToDeleteRows =
dgv.AllowUserToOrderColumns =
dgv.AllowUserToResizeColumns =
dgv.AllowUserToResizeRows =
!(dgv.ReadOnly = true);
Controls.Add(dgv);
}
void addJobRow(string jobNum, string jobHours)
{
dgv.Rows.Add(new string[] {jobNum, jobHours });
}
void addDateRow(string date)
{
dgv.Rows.Add(new string[] { date, ""});
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.SelectionForeColor =
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.ForeColor = Color.Firebrick;
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.Font = new Font("Segoe UI Light", 13.5F);
dgv.Rows[dgv.Rows.Count - 1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;
dgv.Rows[dgv.Rows.Count - 1].Height = 25;
}
List<JOBS> prepareXML(string fileName)
{
string xmlContent = "";
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
using (StreamReader sr = new StreamReader(fs)) xmlContent = sr.ReadToEnd();
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlContent);
List<JOBS> jobsList = new List<JOBS>();
XmlNode form1Node = doc.ChildNodes[1];
for (int i = 0; i < form1Node.ChildNodes.Count; i++)
{
XmlNode dateNode = form1Node.ChildNodes[i].ChildNodes[0].ChildNodes[0],
jobNumNode = form1Node.ChildNodes[i].ChildNodes[1].ChildNodes[0],
timeTicksNode = form1Node.ChildNodes[i].ChildNodes[6].ChildNodes[0];
bool foundDate = false;
for (int j = 0; j < jobsList.Count; j++) if (jobsList[j].compareDate(dateNode.Value))
{
jobsList[j].addJob(jobNumNode.Value, Math.Round(TimeSpan.FromTicks(
(long)Convert.ToDouble(timeTicksNode.Value)).TotalHours, 2).ToString());
foundDate = true;
break;
}
if (!foundDate)
{
JOBS job = new JOBS(dateNode.Value);
string jbnum = jobNumNode.Value;
string tbtck = timeTicksNode.Value;
long tktk = Convert.ToInt64(tbtck);
double tkdb = TimeSpan.FromTicks(tktk).TotalHours;
job.addJob(jobNumNode.Value, Math.Round(TimeSpan.FromTicks(
Convert.ToInt64(timeTicksNode.Value)).TotalHours, 2).ToString());
jobsList.Add(job);
}
}
jobsList.OrderByDescending(x => x.jobDate);
return jobsList;
}
class JOBS
{
public DateTime jobDate;
public List<JobDetails> jobDetailsList = new List<JobDetails>();
public void addJob(string jobNumber, string jobHours)
{
jobDetailsList.Add(new JobDetails() { JobHours = jobHours, JobNumber = jobNumber });
}
public JOBS(string dateString)
{
jobDate = getDateFromString(dateString);
}
public JOBS() { }
public bool compareDate(string dateString)
{
return getDateFromString(dateString) == jobDate;
}
private DateTime getDateFromString(string dateString)
{
string[] vals = dateString.Split('/');
return new DateTime(Convert.ToInt32(vals[2]), Convert.ToInt32(vals[0]), Convert.ToInt32(vals[1]));
}
}
class JobDetails
{
public string JobNumber { get; set; }
public string JobHours { get; set; }
}
}

c# linq join on a sublist in a list

I have a list: var_assets_root that has a nested sub list: contents. I can do a join on the root list and augment it with extra items from the two other lists lst_invtypes_assets and lst_stations_composite_retribution by doing two joins on index items in var_assets_root. But I also want to do a join on the items that are within the sublist var_assets_root-->contents\b.contents which the code below is not doing so far.
Image>>structure of var_assets_root in watch window
mysql mysql_object = new mysql();
List<mysql.invtypes_asset> lst_invtypes_assets = mysql_object.dbmysql_invtypes_asset_to_list();
List<mysql.stations_composite_retribution> lst_stations_composite_retribution = mysql_object.dbmysql_select_stastations_composite_retribution_to_list();
var lst_assets_list = new AssetList("134399", "343434SxSFX7qO81LqUCberhS1OQtktMvARFGED0ZRSN5c4XP230SA", "434367527");
lst_assets_list.Query();
var var_assets_root = lst_assets_list.assets.ToList();
var var_assets_root_typeid_station
= from b in var_assets_root
join c in lst_invtypes_assets on b.typeID equals c.typeID
join d in lst_stations_composite_retribution on b.locationID equals d.stationID
select new {b.contents, b.flag, b.itemID, b.locationID, d.solarSystemName, d.security, d.securityClass, d.regionName, b.quantity, b.singleton, b.typeID, c.groupID, c.marketGroupID, c.volume,
c.typeName, c.description};
I have done a solution of running a linq query for every content sub list and assigning values accordingly.
private void btn_evenet_Click(object sender, EventArgs e)
{
Stopwatch stopwatch1 = new Stopwatch();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch1.Start();
mysql mysql_object = new mysql();
List<mysql.invtypes_asset> lst_invtypes_assets = mysql_object.dbmysql_invtypes_asset_to_list();
List<mysql.stations_composite_retribution> lst_stations_composite_retribution = mysql_object.dbmysql_select_stastations_composite_retribution_to_list();
AssetList lst_assets_list = new AssetList("2312099", "J&VNM14RFUkSxSFX7qAAAAAA1OQtktMvYTVZZBhkO23235c4Z&HJKODPQLM", "123231527");
lst_assets_list.Query();
var var_assets_root = lst_assets_list.assets.ToList();
var var_assets_root_typeid_station
= from b in var_assets_root
join c in lst_invtypes_assets on b.typeID equals c.typeID
join d in lst_stations_composite_retribution on b.locationID equals d.stationID
select new { b.contents, b.flag, b.itemID, b.locationID, d.solarSystemName, d.security, d.securityClass, d.regionName, b.quantity, b.singleton, b.typeID, c.groupID, c.marketGroupID, c.volume,
c.typeName, c.description};
var lst_assets_root_typeid_station = var_assets_root_typeid_station.ToList(); // Using .ToArray() is about 200ms faster than .ToList()
stopwatch2.Start();
for (Int32 a = 0; a < lst_assets_root_typeid_station.Count(); a++)
{
if (lst_assets_root_typeid_station[a].contents.Count() >= 0)
{
for (Int32 b = 0; b < lst_assets_root_typeid_station[a].contents.Count(); b++)
{
var var_row_invtypes_assets = lst_invtypes_assets.Where(x => x.typeID == lst_assets_root_typeid_station[a].contents[b].typeID).ToArray();
lst_assets_root_typeid_station[a].contents[b].groupID = var_row_invtypes_assets[0].groupID;
lst_assets_root_typeid_station[a].contents[b].typeName = var_row_invtypes_assets[0].typeName;
lst_assets_root_typeid_station[a].contents[b].description = var_row_invtypes_assets[0].description;
lst_assets_root_typeid_station[a].contents[b].volume = var_row_invtypes_assets[0].volume;
lst_assets_root_typeid_station[a].contents[b].marketGroupID = var_row_invtypes_assets[0].marketGroupID;
if (lst_assets_root_typeid_station[a].contents[b].contents.Count() != 0)
{
for (Int32 c = 0; c < lst_assets_root_typeid_station[a].contents[b].contents.Count(); c++)
{
var_row_invtypes_assets = lst_invtypes_assets.Where(x => x.typeID == lst_assets_root_typeid_station[a].contents[b].contents[c].typeID).ToArray();
lst_assets_root_typeid_station[a].contents[b].contents[c].groupID = var_row_invtypes_assets[0].groupID;
lst_assets_root_typeid_station[a].contents[b].contents[c].typeName = var_row_invtypes_assets[0].typeName;
lst_assets_root_typeid_station[a].contents[b].contents[c].description = var_row_invtypes_assets[0].description;
lst_assets_root_typeid_station[a].contents[b].contents[c].volume = var_row_invtypes_assets[0].volume;
lst_assets_root_typeid_station[a].contents[b].contents[c].marketGroupID = var_row_invtypes_assets[0].marketGroupID;
}
}
}
}
}
stopwatch2.Stop();
stopwatch1.Stop();
lbl_stopwatch1.Text = "Everything: " + stopwatch1.Elapsed.ToString("mm\\:ss\\.ff"); // 1.53 seconds no debugging
lbl_stopwatch2.Text = "contents sublists: " + stopwatch2.Elapsed.ToString("mm\\:ss\\.ff"); // contents sublists take 1.03 seconds no debugging
}
}
Once I figured that 'yield' pops one content node from a stack of content nodes when getAllContents is called I adapted my code and your function so that now all content nodes get recursed into and have their empty values filled from a linq query. Also the new code that is after the linq creation of var_assets_root_typeid_station is now almost twice as fast. Thank you.
private void btn_evenet_Click(object sender, EventArgs e)
{
Stopwatch stopwatch1 = new Stopwatch();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch1.Start();
mysql mysql_object = new mysql();
List<mysql.invtypes_asset> lst_invtypes_assets = mysql_object.dbmysql_invtypes_asset_to_list();
List<mysql.stations_composite_retribution> lst_stations_composite_retribution = mysql_object.dbmysql_select_stastations_composite_retribution_to_list();
AssetList lst_assets_list = new AssetList("12345678", "ABCDEFFGHIKL01235kokJDSD213123", "12345678");
lst_assets_list.Query();
var var_assets_root = lst_assets_list.assets.ToList();
var var_assets_root_typeid_station
= from b in var_assets_root
join c in lst_invtypes_assets on b.typeID equals c.typeID
join d in lst_stations_composite_retribution on b.locationID equals d.stationID
select new
{b.contents, b.flag, b.itemID, b.locationID, d.solarSystemName, d.security, d.securityClass, d.regionName, b.quantity, b.singleton, b.typeID, c.groupID, c.marketGroupID, c.volume,
c.typeName, c.description};
var lst_assets_root_typeid_station = var_assets_root_typeid_station.ToList(); // Using .ToArray() is about 200ms faster than .ToList()
stopwatch2.Start();
for (Int32 a = 0; a < lst_assets_root_typeid_station.Count(); a++)
{
if (lst_assets_root_typeid_station[a].contents.Count() > 0)
{
for (Int32 z = 0; z < lst_assets_root_typeid_station[a].contents.Count(); z++)
{
foreach (AssetList.Item contentnode in getAllContents(lst_assets_root_typeid_station[a].contents[z]))
{
var var_row_invtypes_assets = lst_invtypes_assets.Where(x => x.typeID == contentnode.typeID).ToArray();
contentnode.groupID = var_row_invtypes_assets[0].groupID;
contentnode.typeName = var_row_invtypes_assets[0].typeName;
contentnode.description = var_row_invtypes_assets[0].description;
contentnode.volume = var_row_invtypes_assets[0].volume;
contentnode.marketGroupID = var_row_invtypes_assets[0].marketGroupID;
}
}
}
}
stopwatch2.Stop();
stopwatch1.Stop();
lbl_stopwatch1.Text = "Everything: " + stopwatch1.Elapsed.ToString("mm\\:ss\\.ff"); // 1.16 seconds no debugging
lbl_stopwatch2.Text = "contents sublists: " + stopwatch2.Elapsed.ToString("mm\\:ss\\.ff"); // contents sublists take 0.63 seconds no debugging
}
IEnumerable<AssetList.Item> getAllContents(AssetList.Item contentNode)
{
if (contentNode.contents.Count == 0)
{
yield return contentNode;
}
else
{
foreach (AssetList.Item subContentNode in contentNode.contents)
{
yield return subContentNode;
foreach (AssetList.Item subSubContentNode in getAllContents(subContentNode))
yield return subSubContentNode;
}
}
}
Aren't you just looking for SelectMany?
var contents = lst_invtypes_assets.contents.SelectMany(x => x.contents);
What I think you're getting at is that you want to access the outer .contents property, and if it contains any contents, then access those, too, and so on... recursively.
While you could do this by defining a recursive Func<AssetList.Item, IEnumerable<AssetList.Item>> and a line of LINQ using .Aggregate, it will probably be easier/more readable to define your own recursive method.
IEnumerable<AssetList.Item> getAllContents(AssetList.Item station) {
foreach (AssetList.Item subStation in station.contents) {
yield return subStation;
foreach (AssetList.Item subSubStation in getAllContents(substation))
yield return subSubStation;
}
}
You can then do a simple foreach(var station in getAllContents(parentStation)), or modify this to include the original station, make it an extension method, etc.

Linq to XML parses files in a folder

So I have this code building with no errors but I need to alter how its opening the xml documents. Right now it can open a single xml documents what I need it to do is open up a folder on my c: and parse through all the xml files in the folder. Any help?
static void Main(string[] args)
{
XDocument doc = XDocument.Load(#"c:\.cfg"); //Change here
var query = from x in doc.Descendants("X")
select new
{
Max1 = x.Attribute("Max").Value,
Min2 = x.Attribute("Min").Value
};
foreach (var x in query) ;
Console.WriteLine("X");
var query2 = from x in doc.Descendants("Y")
select new
{
Max3 = x.Attribute("Max").Value,
Min4 = x.Attribute("Min").Value
};
foreach (var x in query2)
Console.WriteLine("Y");
var query3 = from x in doc.Descendants("ZA")
select new
{
Max5 = x.Attribute("Max").Value,
Min6 = x.Attribute("Min").Value
};
foreach (var x in query3)
Console.WriteLine("Z");
}
You should loop through Directory.EnumerateFiles(#"C:\Something", "*.xml").
... A slightly more "declarative" manner:
// Program.cs
class Program
{
static void Main(string[] args)
{
const string path = #"C:\stuff";
Parallel.ForEach(Directory.EnumerateFiles(path, "*.xml"), x => Walk(XDocument.Load(x)));
}
static IEnumerable<Calib> MapItem(IEnumerable<XElement> elements)
{
return elements.Select(x => new Calib
{
Max = x.Attribute("Max").Value,
Min = x.Attribute("Min").Value
});
}
static void Walk(XDocument doc)
{
var xitems = MapItem(doc.Descendants("XaxisCalib"));
xitems.Iter(x => Console.WriteLine("(XaxisCalib) X: Min = {0} | Max = {1}", x.Min, x.Max));
var yitems = MapItem(doc.Descendants("YAxisCalib"));
yitems.Iter(x => Console.WriteLine("(YaxisCalib) Y: Min = {0} | Max = {1}", x.Min, x.Max));
var zitems = MapItem(doc.Descendants("ZAxisCalib"));
zitems.Iter(x => Console.WriteLine("(ZaxisCalib) Z: Min = {0} | Max = {1}", x.Min, x.Max));
}
}
// Exts.cs
public static class Exts
{
public static void Iter<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
}
}
// Calib.cs
public class Calib
{
public string Max { get; set; }
public string Min { get; set; }
}
Rather than just writing the values out to the console, you could create a new Xml document from the values in the files and do whatever you want with from that (generate an Excel spreadsheet?):
var fileData = new XElement("root",
from file in New System.IO.DirectoryInfo("C:\Something").GetFiles()
where file.Extension.Equals(".xml", String Comparison.CurrentCultureIgnoreCase)
Let doc = XElement.Load(file.FullName)
select new XElement("File",
new XAttribute("Path", file.FullName),
select new XElement("XAxisCalibs",
from x in doc.Descendants("XAxisCalib")
select new XElement("XAxisCalib",
new XAttribute("Max", x.Attribute("Max").Value),
new XAttribute("Min", x.Attribute("Min").Value)
)
),
select new XElement("YAxisCalibs",
from y in doc.Descendants("YAxisCalib")
select new XElement("YAxisCalib",
new XAttribute("Max", x.Attribute("Max").Value),
new XAttribute("Min", x.Attribute("Min").Value)
)
),
select new XElement("ZAxisCalibs",
from z in doc.Descendants("ZAxisCalib")
select new XElement("ZAxisCalib",
new XAttribute("Max", x.Attribute("Max").Value),
new XAttribute("Min", x.Attribute("Min").Value)
)
)
);
Granted, since this is complete declarative and one long statement, it is a bit of a trick to debug if necessary.

Categories