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

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.

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.

Trouble getting total count using .Take() and .Skip()

I'm having some trouble implementing some paging using Linq and I've read the various questions (this and this for example) on here but I'm still getting the error;
System.InvalidOperationException: The result of a query cannot be enumerated more than once.
My (slightly obfuscated) code is;
public List<Thing> GetThings(ObjectParameter[] params, int count, int pageIndex)
{
var things = from t in Context.ExecuteFunction<Something>("function", params)
select new Thing
{
ID = t.ID
});
var pagedThings = things;
if (pageIndex == 0)
pagedThings = things.Take(count);
else if (pageIndex > 0)
pagedThings = things.Skip(count * pageIndex).Take(count);
var countOfThings = things.Count();
return pagedThings.ToList();
}
As soon as the final .ToList() is called, the error is thrown but I can't see why - are the calls to things.Count() and pagedThings.ToList() enumerating the same thing?
Edit: I'm using Entity Framework if that makes any difference
ExecuteFunction actually returns an ObjectResult if I'm not mistaken, which is... more complicated. You may get different results if you make the function composable (which would execute a separate query when you Count()), but it's been a while since I worked with low level EF so I'm not 100% sure that would work.
Since you can't get out of executing what are effectively two queries, the safest bet is to make a completely separate one for counting - and by completely separate I mean a separate function or stored procedure, which just does the count, otherwise you may end up (depending on your function) returning rows to EF and counting them in memory. Or rewrite the function as a view if at all possible, which may make it more straightforward.
You are setting pagedThings = things. So you are working on the same object. You would need to copy things to a new collection if you want to do what you're trying above, but I would recommend refactoring this code in general.
You can check out this SO post to get some ideas on how to get the count without enumerating the list:
How to COUNT rows within EntityFramework without loading contents?
In general, Linq is able to do that. In LinqPad, I wrote the following code and successfully executed it:
void Main()
{
var sampleList = new List<int>();
for (int i = 0; i < 100; i++){
sampleList.Add(i);
}
var furtherQuery = sampleList.Take(3).Skip(4);
var count = furtherQuery.Count();
var cache = furtherQuery.ToList();
}
Note, as your error mentions, this will execute the query twice. Once for Count() and once for ToList().
It must be that the Linq provider that you are representing as Context.ExecuteFunction<Something>("function", params) is protecting you from making multiple expensive calls. You should look for a way to iterate over the results only once. As written, for example, you could .Count() on the List that you had already generated.
Normally, we call them pageIndex and pageSize.
Please check pageIndex whether 0 as start index or 1 as start index depending on your requirement.
public List<Thing> GetThings(ObjectParameter[] params, int pageIndex, int pageSize)
{
if (pageSize <= 0)
pageSize = 1;
if (pageIndex < 0)
pageIndex = 0;
var source = Context.ExecuteFunction<Something>("function", params);
var total = source.Count();
var things = (from t in source select new Thing { ID = t.ID })
.Skip(pageIndex * pageSize).Take(pageSize).ToList();
return things.ToList();
}
Here is my implementation of your code. a few things to notice.
1. You can handle Skip in one statement.
2. The main method shows how to pass multiple pages into the method.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<Thing> thingList = new List<Thing>();
for (int i = 0; i < 99; i++)
{
thingList.Add(new Thing(i));
}
int count = 20;
int pageIndex = 0;
int numberPages = (int)Math.Ceiling(thingList.Count * 1.0/ (count ));
for( ; pageIndex < numberPages; pageIndex ++)
{
var myPagedThings = GetThings(thingList, count, pageIndex);
foreach( var item in myPagedThings)
{
Console.WriteLine(item.ID );
}
}
}
public static IEnumerable<Thing> GetThings(List<Thing> myList, int count, int pageIndex)
{
var things = (
from t in myList
select new Thing{ID = t.ID}).ToList();
return things.Skip(count * pageIndex).Take(count);
}
}
public class Thing
{
public int ID
{ get; set; }
public Thing (){}
public Thing(int id)
{ this.ID = id; }
}
As it happens, ExecuteFunction causes the enumeration to occur immediately, ultimately meaning the code could be re-ordered and the copying of the list was not required - it now looks like the below
public ThingObjects GetThings(ObjectParameter[] params, int count, int pageIndex)
{
var things = from t in Context.ExecuteFunction<Something>("function", params)
select new Thing
{
ID = t.ID
}).ToList();
var countOfThings = things.Count;
if (pageIndex >= 0)
things = things.Skip(count * pageIndex).Take(count);
return new ThingObjects(things, countOfThings);
}

Iterate over unknown nested lists

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

Speed up search and insert to the concurrentbag c#

I have a problem with slow "building" a list and I don't have idea how to speed it up.
Here is my code:
private static ConcurrentBag<Classe<PojedynczeSlowa>> categoryClasses = new ConcurrentBag<Classe<PojedynczeSlowa>>();
private const int howManyStudents = 20;
private static int howManyClasses;
private static EventWaitHandle[] ewhClass;
private static List<Classe<Words>> deserializeClasses;
//...
public static void CreateCategoryClasses()
{
deserializeClasses = Deserialize();
howManyClasses = deserializeClasses.Count;
ewhClass = new EventWaitHandle[howManyClasses];
for (var i = 4; i >= 0; --i)
{
categoryClasses.Add(new Classe<PojedynczeSlowa>(((Categories) i).ToString()));
}
WaitCallback threadMethod = ParseCategories;
ThreadPool.SetMaxThreads(howManyStudents, howManyClasses);
for (var i = 0; i < howManyClasses; ++i)
{
ewhClass[i] = new EventWaitHandle(false, EventResetMode.AutoReset);
ThreadPool.QueueUserWorkItem(threadMethod, i);
}
for (var i = 0; i < howManyClasses; ++i)
{
ewhClass[i].WaitOne();
}
var xmls = new XmlSerializer(typeof(List<Classe<PojedynczeSlowa>>)); //poprawić!!
using (var sw = new StreamWriter(#"categoryClasses.xml"))
{
xmls.Serialize(sw, categoryClasses.ToList());
}
}
private static void ParseCategories(object index)
{
int sum;
var i = index as int?;
if (deserializeClasses[i.Value].Category == Categories.PEOPLE.ToString())
{
foreach (var word in deserializeClasses[i.Value].Bag)
{
sum =
deserializeClasses.Count(
clas =>
clas.Bag.Where(x => clas.Category == deserializeClasses[i.Value].Category)
.Contains(word));
if (!categoryClasses.ElementAt(0).Bag.Contains(new PojedynczeSlowa(word.Word, sum)))
{
categoryClasses.ElementAt(0)
.Bag.Add(new PojedynczeSlowa(word.Word,
Convert.ToDouble(sum)/
Convert.ToDouble(deserializeClasses.Count(x => x.Category == deserializeClasses[i.Value].Category))));
}
}
}
//rest of the code which adds elements to the list on other indexes.
ewhClass[(i).Value].Set();
}
I might add that:
deserializeClasses contains about 18550 elements of class "Word", and any of this elements ("Word") contains a list of string and int, average size of this list is about 200-250 elements. I use .net 4.5.1
Thanks for help!
A couple things (I don't have enough rep to comment so my comments are coming in here too)...
1) Class definitions would be very helpful. For example, you have
if (!categoryClasses.ElementAt(0).Bag.Contains(new PojedynczeSlowa(word.Word, sum)))
which will never be true if you haven't overridden object.Equals (did you?). Also, it's much harder to know what's going on with an incomplete sample.
2) Your code
sum = deserializeClasses.Count(clas => clas.Bag.Where(x => clas.Category == deserializeClasses[i.Value].Category).Contains(word));
doesn't make use of x at all. Consider
sum = deserializeClasses.Count(clas => clas.Category == deserializeClasses[i.Value].Category && clas.Bag.Contains(word));
This avoids much potential enumeration and could speed up the average cost even though the worst case cost remains the same.
3) Dictionaries are your friend. Consider making some temp dictionaries that are indexed by whatever you're checking against. I'm having a hard time figuring out exactly what you're trying to do (see comment 1) but I'm guessing you could save quite a bit of performance cost, particularly that Contains() call, with using a Dictionary.
4) I'm not sure that multithreading is going to save you anything here. I'm guessing it will make things slower since this looks to be CPU bound and you are adding CPU overhead with thread switching.
I would help out with some code but I'm in a bit of a hurry and don't have time to guess at the rest of the missing code to get everything to compile.

Sort same value fields in list descending

How can I sort same value fields in generic lists with a descending order.
Example:
List<int> objList = new List<int>();
objList.Add(1);
-> objList.Add(0);
-> objList.Add(0);
objList.Add(2);
-> objList.Add(0);
It's my source somehow and I want to sort for example zero values in descending mode.
I use this code for sorting the numbers (actually the depths), and above example is not related to this but somehow it's same. In my generic list I have several depths which they might be same to each other and I want to order the same fields descending.
Objects.Sort(
delegate(Classes.Object.GameObject Object1, Classes.Object.GameObject Object2)
{
return Object1.Depth.CompareTo(Object2.Depth);
}
);
Answer: Might help someone in the future
// Reverse same oredered
CurrentSameOrderedFind = Objects[0].Depth;
CurrentSameOrderedID = 0;
for (int i = 1; i <= Objects.Count - 1; i++)
{
if (Objects[i].Depth != CurrentSameOrderedFind)
{
SameOrederedFound = true;
Objects.Reverse(CurrentSameOrderedID, i - 1);
CurrentSameOrderedFind = Objects[i].Depth;
CurrentSameOrderedID = i;
}
}
if (!SameOrederedFound)
{
Objects.Reverse();
}
To sort a List<int>, which your code above shows, in descending order, there's a very simple Linq solution: objList = objList.OrderByDescending(i => i).ToList();
From reading the rest of your question, do you want to sort a list of objects with a depth property? If so, this should work for you:
var myList = new List<someTypeWithDepth>();
myList = myList.OrderByDescending(o => o.depth).ToList();
If in whatever situation you're using these Lists you can get away with typing them as IEnumerable<T> instead of List<T> then you could remove those ugly ToList() calls
If I understand you correctly, you want your list sorted in descending order based on GameObject.Depth, and you've got an implementation that sorts your collection, but in ascending order rather than descending. Given that, here's the laziest answer I could come up with:
Code edited per my comment. Really, why couldn't you have said what you wanted in the question? I agree that it isn't complicated, but you won't get good help if you don't ask good questions.
List<GameObject> oldList = new List<GameObject>(Objects);
Objects.Sort(
delegate(Classes.Object.GameObject Object1, Classes.Object.GameObject Object2)
{
int compareValue = -1 * Object1.Depth.CompareTo(Object2.Depth);
if(compareValue == 0)
compareValue = oldList.IndexOf(Object2).CompareTo(oldList.IndexOf(Object1));
return compareValue;
}
);
Hardly optimal, but it's not meant to be.

Categories