How to sort following list - c#

I have following var result = new List<Tuple<int, List<ProductionReportEntry>, int>>();
How I can sort it by the last integer , that the result will be from high to Low .
Thanks for help.

The easiest way is to use LINQ's OrderByDescending() extension method, it will sort your list in descending order from high to low and push it back into the list:
result = result.OrderByDescending(t => t.Item3).ToList();
That's assuming you want to store it back int the original reference, of course you could assign it to another variable, etc...
Alternatively, you could do an in-place sort using List.Sort()'s overload that takes a Comparisson<T> delegate:
// does descending since we put r on the lhs and l on the rhs...
result.Sort((l, r) => r.Item3.CompareTo(l.Item3));
Alternatively, you could build a custom IComparer<T>, of course for your Tuple<int, List<PropertyReportEntry>, int> but that gets pretty ugly looking...

Related

Distinct function on a list does not change the list [duplicate]

Having issues with the OrderBy clause not having any impact on the sort. I have walked through this in the debugger and insuring this is a case that the sort line of the code is being hit and reviewing the results after it the order by has not been applied.
public static IEnumerable<DDLOptions<TValueType>> GetDDLOptionsViewModel<TClass, TValueType>(
IEnumerable<TClass> list,
Func<TClass, TValueType> value,
Func<TClass, string> displayText,
bool sort = true
)
{
List<DDLOptions<TValueType>> ddlOptions;
ddlOptions = list.Select(
l => new DDLOptions<TValueType>
{
Value = value(l),
DisplayText = displayText(l)
}
).ToList(); <========== Works if I put the Order By here.
if (sort)
{
ddlOptions.OrderBy(l => l.DisplayText); <===== Does NOT work here.
}
return ddlOptions;
}
OrderBy returns a query that would perform the ordering: it does not modify the original list (whereas something like List<T>.Sort would modify the original)
Instead try something like:
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText).ToList();
You might want to play around with the type of ddlOptions or where/how you return the data as we're doing an extra ToList than probably necessary, but that's probably a minor/non-issue for this case anyway.
Note that the same applies to other LINQ functions like GroupBy, Distinct, Concat - all return results rather than modifying the source collection.
Try:
if (sort)
{
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText); <===== Should work now.
}
As others have said, you need to assign the result of OrderBy to something as it doesn't mutate the sequence it acts on. It's easiest to make ddlOptions an IEnumerable instead of a List, so that you can assign the result to that. The ToList call on the select is also not needed:
public static IEnumerable<DDLOptions<TValueType>> GetDDLOptionsViewModel<TClass, TValueType>(
IEnumerable<TClass> list,
Func<TClass, TValueType> value,
Func<TClass, string> displayText,
bool sort = true
)
{
IEnumerable<DDLOptions<TValueType>> ddlOptions;
ddlOptions = list.Select(
l => new DDLOptions<TValueType>
{
Value = value(l),
DisplayText = displayText(l)
}
);
if (sort)
{
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText);
}
return ddlOptions;
}
Note that this version of the method will use deferred execution, and so won't actually perform the Select/OrderBy until the sequence is iterated. If you don't want to do that, you can add ToList on the return line.
You need to type:
ddlOptions = ddlOptions.OrderBy(l => l.DisplayText);
OrderBy doesn't sort a List<T> or any other IEnumerable<T>. It produces a new, sorted IEnumerable<T>. So calling ddlOptions.OrderBy(...) doesn't modify ddlOptions.
If you have a List<T> and wish to sort it, you can use the Sort method - in particular the overload that takes a Comparison<T> as a parameter. This actually sorts the list instead of returning a new IEnumerable.
Comparison<T> is a delegate representing a function that takes two of T and returns a negative number if the first is "less" than the second, a positive number if the first is "greater" than the second, and zero if one isn't sorted before or after the other.
In this case you don't have to remember that. Instead, you can just do this:
ddlOptions.Sort((x, y) => string.CompareOrdinal(x.DisplayText, y.DisplayText));
You're passing in a function that takes two items in the list and returns the comparison result of their DisplayText properties, which will be negative, 0, or positive.
Sometimes we use OrderBy because it doesn't modify the original list. But if modifying the list is what we want then we can use Sort.

ConcurrentDictionary.Where very slow for filtering based int array (Key field)

I have the following
var links = new ConcurrentDictionary<int, Link>();
which is populated with around 20k records, I have another array of strings (List) that I turn into int array using following.
var intPossible = NonExistingListingIDs.Select(int.Parse); //this is very fast but need to be done
which is pretty fast. but I would like to create a new list or filter out "links" only to what is actually in the intPossible array which matches the Key element of the ConcurrentDictionary.
I have the following using a where clause but it takes about 50 seconds to do the actual filtering which is very slow for what I want to do.
var filtered = links.Where(x => intPossible.Any(y => y == x.Key)).ToList();
I know intersect is pretty fast but I have a array of ints and intersect is not working with this against a ConcurrentDictionary
How can i filter the links to be a little faster instead of 50 seconds.
You need to replace your O(n) inner lookup with something more speedy like a hashset which offers O(1) complexity for lookups.
So
var intPossible = new HashSet<int>(NonExistingListingIDs.Select(int.Parse));
and
var filtered = links.Where(x => intPossible.Contains(x.Key)).ToList();
This will avoid iterating most of intPossible for every item in links.
Alternatively, Linq is your friend:
var intPossible = NonExistingListingIDs.Select(int.Parse);
var filtered =
links.Join(intPossible, link => link.Key, intP => intP, (link, intP) => link);
The implementation of Join does much the same thing as I do above.
An alternative method would be to enumerate your list and use the indexer of the dictionary...might be a little cleaner...
var intPossible = NonExistingListingIDs.Select(int.Parse);
var filtered = from id in intPossible
where links.ContainsKey(id)
select links[id];
You might want to chuck in a .ToList() in there for good measure too...
This should actually be slightly faster than #spender's solution, since .Join has to create a new HashTable, whilst this method uses the HashTable in the ConcurrentDictionary.

Sort based on function

I have a method that given 2 strings he returns a number (between 0 and 100) which represents is how alike they are, being 0 "not similar at all" and 100 "they are the same"
Now the thing is that i have a list of County (string name, GeoRef coordinates, string Mayor) which i would like to sort based on the return of my function...
im looking for something like myList.Sort(f=>MyScoreEvaluator("York",f.Name))
Can anyone tell me how to do so?
Edit1: I dont think that the method "Sort" is quite i want... Sort compare itens inside of the list... i want to compare the itens of the list against a external info and based on that result sort the items
The OrderBy and OrderByDescending are returning the same item order...
Edit2: Heres is the code of the OrderBy I'm using: aux.OrderBy(f => StringComparisonHelper.HowAlike(f.Name, countyNameSearched));
You can use OrderBy, and re-assign your list:
list = list.OrderBy(f => MyScoreEvaluator("York", f.Name))
You could just use OrderBy:
list.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or Implement a custom Comparer:
public static int SortByName(County x, County y)
{
return x.Name.CompareTo(y.Name);
}
Usage:
list.Sort(new Comparison<County>(SortByName))
There is an OrderBy in LINQ:
var sorted = myList.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or to sort descendingly:
var sortedDesc = myList.OrderByDescending(f => MyScoreEvaluator("York", f.Name))
It's very easy to use the LINQ OrderBy extension (see others' answers).
If you want to use Sort, it would be:
myList.Sort((x, y) => MyScoreEvaluator("York", x.Name)
.CompareTo(MyScoreEvaluator("York", y.Name)));
This assumes that myList is a System.Collections.Generic.List<>.
If you want the other sort direction, swap x and y on one side of the lambda arrow =>, of course.
EDIT:
Remember .Sort method on List<> modifies the same instance. The return type of Sort method is void. On the other hand, OrderBy creates a new IEnumerable<> on which you can call .ToList() to get a new list object. The old object is unchanged. You might assign the new object to the variable that held the original list. Other variables that reference the old object won't be affected by that. Example:
myList = myList.OrderBy(f => MyScoreEvaluator("York", f.Name)).ToList();
NEW EDIT:
If performance is an issue, it's not clear which of these two to use. The OrderBy method calls the MyScoreEvaluator only once per item in your original list. The Sort method as presented here, calls MyScoreEvaluator a lot more times, because it doesn't "remember" the result of each MyScoreEvaluator call (the Comparison<> delegate instance is a black box to the Sort algorithm). So if it wants to compare "Fork" and "Kork", it calls MyScoreEvaluator twice. Then afterwards if it wants to compare "Kork" and "Yorc", it does the "Kork" MyScoreEvaluator again. On the other hand, the sort algorithm of List<>.Sort is superior to that of OrderBy.

Some misunderstanding about sort extensions

I have the following code example:
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
list.Add(6);
list.Add(7);
list.OrderByDescending(n=>n).Reverse();
But when I use this:
list.OrderByDescending(n=>n).Reverse();
I don't get wanted result.
If instead of the above statement, I use this one:
list.Reverse();
I get the wanted result.
Any idea why I don't get wanted result using the first statement ?
I believe I am missing something in understanding the extensions.
Thank you in advance.
The list.Reverse() method reverses the list in-place, so your original list is changed.
The .OrderByDescending() extension method produces a new list (or rather an IEnumerable<T>) and leaves your original list intact.
EDIT
To get two lists, for both sort orders:
List<int> upList = list.OrderBy(n => n).ToList();
List<int> downList = list.OrderByDescending(n => n).ToList();
Edit: So the problem seems to be that you think that the Enumerable extensions would change the original collection. No they do not. Actually they return something new you need to asign to a variable:
IEnumerable<int> ordered = list.OrderByDescending(n => n);
foreach(int i in ordered)
Console.WriteLine(i);
OrderByDescending orders descending(highest first) which is what you obviously want. So i don't understand why you reverse it afterwards.
So this should give you the expected result:
var ordered = list.OrderByDescending(n=> n);
This returns an "arbitrary" order:
list.Reverse()
since it just reverses the order you have added the ints. If you have added them in an ordered way you don't need to order at all.
In general: use OrderBy or OrderByDescending if you want to order a sequence and Reverse if you want to invert the sequence what is not necessarily an order (it is at least confusing).

Sort an int array with orderby

I would like to sort my int array in ascending order.
first I make a copy of my array:
int[] copyArray = myArray.ToArray();
Then I would like to sort it in ascending order like this:
int[] sortedCopy = from element in copyArray
orderby element ascending select element;
But I get a error, "selected" gets highligted and the error is:
"cannot implicitly convert type 'system.linq.iorderedenumerable' to 'int[]'"
You need to call ToArray() at the end to actually convert the ordered sequence into an array. LINQ uses lazy evaluation, which means that until you call ToArray(), ToList() or some other similar method the intermediate processing (in this case sorting) will not be performed.
Doing this will already make a copy of the elements, so you don't actually need to create your own copy first.
Example:
int[] sortedCopy = (from element in myArray orderby element ascending select element)
.ToArray();
It would perhaps be preferable to write this in expression syntax:
int[] sortedCopy = myArray.OrderBy(i => i).ToArray();
Note: if you don't need a copy (i.e. it is acceptable to change myArray), then a much simpler and more efficient approach is just:
Array.Sort(myArray);
This does an in-place sort of the array, exploiting the fact that it is an array to be as efficient as possible.
For more complex scenarios (for example, a member-wise sort of an object-array), you can do things like:
Array.Sort(entityArray, (x,y) => string.Compare(x.Name, y.Name));
this is the moral-equivalent of:
var sortedCopy = entityArray.OrderBy(x => x.Name).ToArray();
but again: doing the sort in-place.
We don't know what you are doing next, but maybe you don't need an array. If it's going into another linq statement, or foreach, then just keep it as it is, most simply by using var.
var sortedCopy = myArray.OrderBy(i => i);
foreach(var item in sortedCopy)
{
//print out for example
}
This allows linq to be as lazy as possible. If you always cast ToArray or ToList then it has no choice than to evaluate then and there, and allocate memory for the result.

Categories

Resources