C# Sort Object[] Array within ArrayList? - c#

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);
}
);

Related

Why is the return is List<char>?

I am trying to pull file names that match the substring using "contains" method. However, return seem to be List<char> but I expect List<string>.
private void readAllAttribues()
{
using (var reader = new StreamReader(attribute_file))
{
//List<string> AllLines = new List<string>();
List<FileNameAttributeList> AllAttributes = new List<FileNameAttributeList>();
while (!reader.EndOfStream)
{
FileNameAttributeList Attributes = new FileNameAttributeList();
Attributes ImageAttributes = new Attributes();
Point XY = new Point();
string lineItem = reader.ReadLine();
//AllLines.Add(lineItem);
var values = lineItem.Split(',');
Attributes.ImageFileName = values[1];
XY.X = Convert.ToInt16(values[3]);
XY.Y = Convert.ToInt16(values[4]);
ImageAttributes.Location = XY;
ImageAttributes.Radius = Convert.ToInt16(values[5]);
ImageAttributes.Area = Convert.ToInt16(values[6]);
AllAttributes.Add(Attributes);
}
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
List<string>var unique_reference_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"ref")).FirstOrDefault().ImageFileName.ToList();
foreach (var unique_raw_filename in unique_raw_filenames)
{
var raw_attributes = AllAttributes.Where(x => x.ImageFileName == unique_raw_filename).ToList();
}
}
}
Datatype class
public class FileNameAttributeList
{ // Do not change the order
public string ImageFileName { get; set; }
public List<Attributes> Attributes { get; set; }
public FileNameAttributeList()
{
Attributes = new List<Attributes>();
}
}
Why is FirstOrDefault() does not work ? (It returns List<char> but I am expecting List<string> and fails.
The ToList() method converts collections that implement IEnumerable<SomeType> into lists.
Looking at the definition of String, you can see that it implements IEnumerable<Char>, and so ImageFileName.ToList() in the following code will return a List<char>.
AllAttributes.Where(x =>
x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
Although I'm guessing at what you want, it seems like you want to filter AllAttributes based on the ImageFileName, and then get a list of those file names. If that's the case, you can use something like this:
var unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).Select(y=>y.ImageFileName).ToList();
In your code
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).FirstOrDefault().ImageFileName.ToList();
FirstOrDefault() returns the first, or default, FileNameAttributeList from the list AllAttributes where the ImageFileName contains the text non.
Calling ToList() on the ImageFileName then converts the string value into a list of chars because string is a collection of char.
I think that what you are intending can be achieved by switching out FirstOrDefault to Select. Select allows you to map one value onto another.
So your code could look like this instead.
List<string> unique_raw_filenames = AllAttributes.Where(x => x.ImageFileName.Contains(#"non")).Select(x => x.ImageFileName).ToList();
This then gives you a list of string.

How to remove duplicates from object list based on that object property in c#

I've got a problem with removing duplicates at runtime from my list of object.
I would like to remove duplicates from my list of object and then set counter=counter+1 of base object.
public class MyObject
{
MyObject(string name)
{
this.counter = 0;
this.name = name;
}
public string name;
public int counter;
}
List<MyObject> objects_list = new List<MyObject>();
objects_list.Add(new MyObject("john"));
objects_list.Add(new MyObject("anna"));
objects_list.Add(new MyObject("john"));
foreach (MyObject my_object in objects_list)
{
foreach (MyObject my_second_object in objects_list)
{
if (my_object.name == my_second_object.name)
{
my_object.counter = my_object.counter + 1;
objects_list.remove(my_second_object);
}
}
}
It return an error, because objects_list is modified at runtime. How can I get this working?
With a help of Linq GroupBy we can combine duplicates in a single group and process it (i.e. return an item which represents all the duplicates):
List<MyObject> objects_list = ...
objects_list = objects_list
.GroupBy(item => item.name)
.Select(group => { // given a group of duplicates we
var item = group.First(); // - take the 1st item
item.counter = group.Sum(g => g.counter); // - update its counter
return item; // - and return it instead of group
})
.ToList();
The other answer seem to be correct, though I think it will do scan of the whole list twice, depending on your requirement this might or might not be good enough. Here is how you can do it in one go:
var dictionary = new Dictionary<string, MyObject>();
foreach(var obj in objects_list)
{
if(!dictionary.ContainsKey(obj.name)
{
dictionary[obj.name] = obj;
obj.counter++;
}
else
{
dictionary[obj.name].counter++;
}
}
Then dictionary.Values will contain your collection

Bad Performance with ExpandoObject

im having a terrible problem with a List of ExpandoObject reading their data with a Incoming Products, I cant use a normal class cause im using a list of products, and some products use size as number(20,21,22...), other as letter(S, M, L, XL....) i put this on a Grid, in each size column i put a quantity that each store receives from supplier
this is my list:
listaGrade = new List<dynamic>();
foreach(product p in BD.Proc)
{
foreach(store s in BD.Stores)
{
dynamic itemGradeDinamic = new ExpandoObject();
itemGradeDinamic.MODELDES = p.MODELDES;
itemGradeDinamic.TIPO = p.TIPO;
itemGradeDinamic.DESCRIPTION = p.DESCRIPTION;
itemGradeDinamic.STORE = s.CODE;
itemGradeDinamic.STOREDES = s.NAME;
itemGradeDinamic.SUBTOTAL = 0;
foreach (string size in p.sizes)
{
((IDictionary<String, Object>)itemGradeDinamic).Add(size, 0);
}
listaGrade.add(itemGradeDinamic);
}
}
Getting data:
object description, modelDes = "";
object qtde, store = 0;
foreach (ExpandoObject obj in listaGrade)
{
foreach (string size in "products".sizes)
{
if (((IDictionary<string, object>)obj).TryGetValue(size, out qtde))
{
if (Convert.ToDecimal(qtde) > 0)
{
((IDictionary<string, object>)obj).TryGetValue("DESCRIPTION", out description);
((IDictionary<string, object>)obj).TryGetValue("MODELDES", out modelDes);
((IDictionary<string, object>)obj).TryGetValue("TIPO", out tipo);
((IDictionary<string, object>)obj).TryGetValue("SHOP", out store);
}
}
}
}
The ExpandoObject class does not replace static types.
In this particular case, you have to discard the ExpandoObject in favour of the custom static class. If you need something that contains a dynamic data structure, use Dictionary<string,string> or Dictionary<string,object> depending on the task.

convert foreach loop to linq code

the below code is working as expected but I'm looking to convert the below code to use Linq?
any suggestions?
string[] selections = "Men,Women,Boys".Split(',');
int _chkboxId = 0;
int _chkboxTextId = 1;
try
{
string id = "lstchk_" + _chkboxId;
while (!driver.FindElement(By.Id(id)).Equals(null))
{
string checkboxId = String.Format("lstchk{0}", _chkboxTextId);
string checkboxName = driver.FindElement(By.Id(checkboxId)).Text;
foreach (string match in selections)
{
if (checkboxName == match.Trim())
{
//matched... do more work here...
}
}
}
foreach (string match in selections.Where(match => checkboxName == match.Trim()))
{
//matched... do more work here...
}
If your selections list contains only distinct values, than you could use
if(selections.Any(match=>match.Trim().Equals(checkboxName)))
{
//Do work
}
instead of your loop. Same if your list may contain non-distinct values, but the work should be done only once for each checkboxName
switch(checkboxName) {
case "Men":
case "Women":
case "Boys":
// do more work here
default:
// no match, bail
}
Sometimes, there's just a different way to write the code.
How would you convert this simple foreach to a linq statement?
public List<string> GetItems()
{
var items = new List<string>();
foreach (Ranorex.ListItem item in ranorexComboBox.Items)
{
items.Add(item.Text);
}
return items;
}

Trying to Create an array holding a Struct?

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;

Categories