Iterate over unknown nested lists - c#

I need to iterate over unknown nested lists and size (subcategories.subcategories.subcategories etc..) and check if any values in my array contains the nested lists values. I might need an recursive function. How could i make this possible.
Here is my code so far (it will not check deeper then 2 level)
for (int counter = 0; counter < filteredList[0].subcategories.Count; counter++)
{
var item = filteredList[0].subcategories[counter].questionanswer;
for (int i = 0; i < item.Count; i++)
{
var results = Array.FindAll(questionIDs, s => s.Equals(item[i].id.ToString()));
if (results.Length > 0)
{
QuestionViewModel question = new QuestionViewModel();
question.formattedtext = item[i].formattedtext;
question.id = item[i].id;
question.sortorder = item[i].sortorder;
question.breadCrum.AddRange(breadCrumCategoryId);
filteredQuestions.Add(question);
}
}
}

Been writing recursive functions for 40 years. If I can't recurse, nobody can. Best way is define classes. Normally done like code below. Add properties to class like Name, Rank, Serial Number.
public class Category
{
List<Category> children { get; set; }
}​

Related

Sort List Of Objects By Properties C# (Custom Sort Order)

I currently have a list of objects that I am trying sort for a custom made grid view. I am hoping that I can achieve it without creating several customized algorithms. Currently I have a method called on page load that sorts the list by customer name, then status. I have a customized status order (new, in progress, has issues, completed, archived) and no matter which sort is used (customer, dates, so on) it should sort the status in the correct order. For example:
I have two customers with two orders each, the first customer is Betty White, the second is Mickey Mouse. Currently, Betty has a new order, and a completed order and Mickey has an order in progress and another on that has issues. So the display order should be:
Betty, New :: Betty, Completed
Mickey, In Progress :: Mickey, Has Issues
I am currently using Packages.OrderBy(o => o.Customer).ThenBy(o => o.Status). This works effectively to get the customers sorted, however this doesn't eliminate the custom sorting of the status property.
What would be the most efficient and standards acceptable method to achieve this result?
case PackageSortType.Customer:
Packages = Packages.OrderBy(o => o.Customer).ThenBy(o=>o.Status).ToList<Package>();
break;
I previously created a method that sorted by status only, however it is my belief that throwing the OrderBy into that algorithm would just jumble the status back up in the end.
private void SortByStatus() {
// Default sort order is: New, In Progress, Has Issues, Completed, Archived
List<Package> tempPackages = new List<Package>();
string[] statusNames = new string[5] { "new", "inProgress", "hasIssue", "completed", "archived" };
string currentStatus = string.Empty;
for (int x = 0; x < 5; x++) {
currentStatus = statusNames[x];
for (int y = 0; y < Packages.Count; y++) {
if (tempPackages.Contains(Packages[y])) continue;
else {
if (Packages[y].Status == currentStatus)
tempPackages.Add(Packages[y]);
}
}
}
Packages.Clear();
Packages = tempPackages;
}
Also, I'm not sure if it is relevant or not; however, the Packages list is stored in Session.
EDIT
Thanks to Alex Paven I have resolved the issue of custom sorting my status. I ended up creating a new class for the status and making it derive from IComparable, then created a CompareTo method that forced the proper sorting of the status.
For those who are curious about the solution I came up with (it still needs to be cleaned up), it's located below:
public class PackageStatus : IComparable<PackageStatus> {
public string Value { get; set; }
int id = 0;
static string[] statusNames = new string[5] { "new", "inProgress", "hasIssue", "completed", "archived" };
public int CompareTo(PackageStatus b) {
if (b != null) {
if (this == b) {
return 0;
}
for (int i = 0; i < 5; i++) {
if (this.Value == statusNames[i]) { id = i; }
if (b.Value == statusNames[i]) { b.id = i; }
}
}
return Comparer<int>.Default.Compare(id, b.id);
}
}
Use:
Packages.OrderBy(o => o.Customer).ThenBy(o => o.Status).ToList<Package>();
I'm not sure what exactly you're asking; why can't you use the Linq expressions in your first code sample? There's OrderByDescending in addition to OrderBy, so you can mix and match the sort order as you desire.

How to choose the Data Stucture

I have a table of data like this.
I want to perform numerous operations on it like
How many times was PLAY 'no' when it was sunny
How many times was WINDY 'true' when PLAY was 'yes'
What data structure should I use?
right now I have simple array which is coming out to be one heck of a task to control.
string[,] trainingData = new string[noOfRecords,5];
using (TextReader reader = File.OpenText("input.txt"))
{
int i =0;
string text = "";
while ((text = reader.ReadLine()) != null)
{
string[] bits = text.Split(' ');
for (int j = 0; j < noOfColumnsOfData; j++)
{
trainingData[i, j] = bits[j];
}
i++;
}
}
To widen #Doan cuong's anwer,
I would use an enumarable list of objects.
each object can be calle: Record and the collection can be called Table.
(Table is IEnumarable).
Here is a simple example:
static void Main(string[] args)
{
Table table = new Table();
int count1 = table.records.Where(r => r.Play == false && r.Outlook.ToLower() == "sunny").Count();
}
public class Record
{
public bool Play;
public string Outlook;
}
public class Table
{
//This should be private and Table should be IEnumarable
public List<Record> records = new List<Record>();
}
this is a highly uncomfortable question because it's about opinion more then a valid programing question. a lot of programmers would say this or that. for the table you are showing there is no problem using array as you did for simple queries as you mentioned. for more complex data and queries i would suggest you'll take the time and effort to study LINQ
Create a class and write the values to properties. I.e.:
public class Weather
{
public string Outlook {get;set;}
...
}
And then store them into a List<Weather> collection (during your loop). Like already said, you can run LINQ queries on it. The internet is full of example how to use LINQ.

What is the most effective way to 'align' two separate lists by ordinal in a single ItemsControl?

Over-simplifying our model for the purposes of this example, let's say we have two lists of data, ListA and ListB, both of which are of type List<string>. From a data perspective, they are not related. ListA and ListB can be added to, removed from, or otherwise updated independently.
What we're trying to do is display them both at the same time in the same list, aligned by ordinal position.
Our first approach was to create a new ListMapping object as follows:
public class ListMapping
{
public int Index{ get; set; }
public string StringA{ get; set; }
public string StringB{ get; set; }
}
then create a List<ListMapping> relating the strings at ordinal position 'x' of ListA and ListB and we'd initialize it like this:
var MappedList = new List<ListMapping>();
var maxItems = Math.Max(ListA.Count, ListB.Count);
for(int index = 0; index < maxItems; index++)
{
var lm = new ListMapping(){
Index = index,
StringA = (index < ListA.Count) ? ListA[index] : null;
StringB = (index < ListB.Count) ? ListB[index] : null;
}
MappedList.Add(lm);
}
The problem with this approach is we had to manually manage this new list of ListMap objects. If an item is deleted from ListB, then we need to manually shift all the ListMapping.StringB properties up one position to 'realign' with the new ListMapping.StringA. Same thing with Insert, etc.
Our next approach was to not actually store the string values in ListMapping, just the index, and make the getters return the value directly from the underlying lists, like this...
public class ListMapping
{
public int Index{ get; set; }
public string StringA{ get{ (Index < ListA.Count) ? ListA[Index] : null; } }
public string StringB{ get{ (Index < ListB.Count) ? ListB[Index] : null; } }
}
And then we'd initialize the List<ListMapping> object like this...
var MappedList = new List<ListMapping>();
var maxItems = Math.Max(ListA.Count, ListB.Count);
for(int index = 0; index < maxItems; index++)
{
var lm = new ListMapping(){
Index = index
}
MappedList.Add(lm);
}
Using this design, we'd simply need to trigger property changed notifications for the StringA and StringB properties of any ListMapping with an index that would have been affected by an operation on either ListA or ListB. Definitely cleaner and no held references to the source objects, but now they had to have a reference to the List objects themselves. Plus, we still need to manually manage the overall list of ListMapping items to ensure there's at least 'maxItems' items at all times. Better, but not ideal.
I can't help but wonder if there's a way to construct an ItemsControl to have two ItemsSource properties then do something clever with its layout panel and ItemContainerGenerator, but that just seems like I'd be doing in the UI what I'm already doing in the data.
So, any thoughts on a way to solve this issue?

C# List<GenericClass>(100) Construction Principles

If I do the following:
List<GenericClass> listObj = new List<GenericClass>(100);
// Do I need this part too?
for (int i = 0; i < 100; i++)
{
listObj[i] = new GenericClass();
}
Basically I am asking if the C# compiler will automatically fire the GenericClass constructor for each of the 100 GenericClass objects in the list. I searched in the MSDN documentation as well as here on StackOverflow.
Thanks for any help.
That's not how List works. When you specify a capacity, it's an initial capacity, not the number of items in the list. The list contains no elements until you add them via the Add method. Lists do not have a maximum capacity. And since you're adding objects via the Add method, yes, you would have to new them up first.
In fact, doing what you put in your question would throw an ArgumentOutOfRange exception.
For what you're doing, you'd need to use an array.
var listObj = new List<GenericClass>();
listObj[0] = new GenericClass(); // ArgumentOutOfRange exception
This will work:
for (int i=0;i<100;i++)
{
listObj.Add(new GenericClass());
}
This is what you were attempting to do:
var arrayObj = new GenericClass[100];
for (int i = 0; i < 100; i++)
{
arrayObj[i] = new GenericClass();
}
Yes you do need to create and add each instance to the list. Take a look at the remarks section for this constructor style: http://msdn.microsoft.com/en-us/library/dw8e0z9z.aspx
You are specifying how many elements you expect to put in the list so that the list does not have to resize behind the scenes every time you add a new GenericClass instance to it.
No! It specify the initial capacity.
MSDN article:
The capacity of a List is the number of elements that the List
can hold. As elements are added to a List, the capacity is
automatically increased as required by reallocating the internal
array.
Since you cannot do it directly with List you can use a helper method to have a generator and use the List(IEnumerable collection) overload.
class Program
{
static void Main(string[] args)
{
var list = new List<string>
(
Generator.New(() => new string('a', 5), 100)
);
list.ForEach((x) => Console.WriteLine(x));
}
}
public static class Generator
{
public static IEnumerable<T> New<T>(Func<T> generator, int nCount)
{
for (int i = 0; i < nCount; i++)
{
yield return generator();
}
}
public static IEnumerable<T> New<T>(Func<int,T> generator, int nCount)
{
for (int i = 0; i < nCount; i++)
{
yield return generator(i);
}
}
}
This does work but it is not so pretty as it could be if List would support this functionality out of the box. The example program will print 100 lines consisting of 5 a characters.

Why Doesn't My Anonymous Method Work in a Loop?

This function is supposed to set descending order numbers on an IEnumerable<Order>, but it doesn't work. Can anyone tell me what's wrong with it?
private void orderNumberSetter(IEnumerable<Order> orders)
{
var i = 0;
Action<Order, int> setOrderNumber = (Order o, int count) =>
{
o.orderNumber = i--;
};
var orderArray = orders.ToArray();
for (i = 0; i < orders.Count(); i++)
{
var order = orderArray[i];
setOrderNumber(order, i);
}
}
You are re-using i as loop variable and i gets modified in your setOrderNumber lambda - don't modify i - it's unclear what you meant to do, maybe the following:
Action<Order, int> setOrderNumber = (Order o, int count) =>
{
o.orderNumber = count;
};
If the above is the case you could have achieved that much, much easier though, your code seems unnecessarily complex, i.e:
for (i = 0; i < orderArray.Length; i++)
{
orderArray[i].orderNumber = i;
}
or even simpler without having to create an array at all:
int orderNum = 0;
foreach(var order in orders)
{
order.orderNumber = orderNum++;
}
Edit:
To set descending order numbers, you can determine the number of orders first then go backwards from there:
int orderNum = orders.Count();
foreach(var order in orders)
{
order.orderNumber = orderNum--;
}
Above would produce one based order numbers in descending order. Another approach, more intuitive and probably easier to maintain is to just walk the enumeration in reverse order:
int orderNum = 0;
foreach(var order in orders.Reverse())
{
order.orderNumber = orderNum++;
}
I agree with BrokenGlass, you are running into an infinite loop.
You could achieve the same thing using foreach:
private void orderNumberSetter(IEnumerable<Order> orders)
{
var count = orders.Count();
orders.ToList().ForEach(o =>
{
o.orderNumber = count--;
});
}
I would try this code instead that decrements i while it enumerates through the array
private void orderNumberSetter(IEnumerable<Order> orders)
{
int i = orders.Count();
foreach (Order order in orders.ToArray())
{
order.orderNumber = --i;
}
}
Though its hard to tell what your trying to do, its a good bet that you didn't mean to keep referring to the same variable i, which is whats causing an infinite loop.
heres another example of what I believe you wanted
IEnumerable<Order> reversed = orders.ToArray(); //To avoid editing the original
reversed.Reverse();
int orderNumber = 0;
foreach (Order order in reversed)
{
order.orderNumber = orderNumber++;
}
I suggest editing your title. Your title describes your question, and I'm sure you didn't want a Broken C# function, since you already had one :P. Its also good to describe what your code to do in the post thoroughly, including what your expected results are, and how your current example doesn't meet them. Don't let your non working example alone explain what you want, It only showed us an example of what you didn't want.

Categories