C# OrderByDescending on value from list in another list - c#

I'm trying to order a list (will refer to this list as result) by a value in another list (Redeems).
The result list contains the Redeems list, and I want to order result by the field "SumChosenYear" in the "Redeems" list and get top 20. This is what I've managed to get, and in theory I think it should work.
result = result
.OrderByDescending(input => input.Redeems
.Select(input2 => input2.SumChosenYear)
.ToList())
.Take(20)
.ToList();
However it throws an exception saying "Atleast one object implement IComparable". Why does this happen?

This line:
input.Redeems
.Select(input2 => input2.SumChosenYear)
.ToList()
returns you a List and because List does not implement IComparable you cannot put this lambda inside this overload of OrderByDescending extension.
You have basically two options:
First option
Cretae your custom implementation of IComparer for this list (assuming SumChosenYear property is an int for this purpose):
public class SumChosenYearListComparer : IComparer<List<int>>
{
public int Compare(List<int> x, List<int> y)
{
//Your custom comparison...
}
}
and then use it with this overload of OrderByDescending extension:
var result = result
.OrderByDescending(input => input.Redeems
.Select(input2 => input2.SumChosenYear)
.ToList(), new SumChosenYearListComparer())
.Take(20)
.ToList();
Second option
You can choose which item in this list you want to use in the comparison(maybe the max, min, first or last value or maybe even the sum of all the items).
Assuming you want to comapre using the max value in the list your code could look something like this:
var result = result
.OrderByDescending(input => input.Redeems
.Max(input2 => input2.SumChosenYear))
.Take(20)
.ToList();

Related

Sort list by string field of class

I have a list of objects, each with time data, id numbers, and a string descriptor in the type field. I wish to pull all the values to the front of the list with a certain string type, while keeping the order of those list elements the same, and the order of the rest of the list elements the same, just attached to the back of those with my desired string.
I've tried, after looking for similar SE questions,
list.OrderBy(x => x.type.Equals("Auto"));
which has no effect, though all other examples I could find sorted by number rather than by a string.
List Objects class definition:
public class WorkLoad
{
public long id;
public DateTime timestamp;
...
public String type;
}
...create various workload objects...
schedule.Add(taskX)
schedule.OrderBy(x => x.type.Equals("Manual"));
//has no effect currently
If you already have a sorted list I think the fastest way to resort it by "having a type of auto or not" without losing the original order (and without having to resort all over again) could be this:
var result = list.Where(x => x.type.Equals("Auto"))
.Concat(list.Where(x => !x.type.Equals("Auto")))
.ToList();
Update:
You commented that "everyting else should be sorted by time", so you can simply do this:
var result = list.OrderByDescending(x => x.type.Equals("Auto"))
.ThenBy(x => x.Time).ToList();
You can use multiple orderings in a sequence:
list.OrderBy(x => x.type == "Auto" ? 0 : 1).ThenBy(x => x.type);

LINQ How retrieve anonymous property

I have an array of integers. I can GroupBy, Sort, and Take the less repeat element:
int [] list = new int[] {0, 1, 1, 0, 2};
var result = list
.GroupBy(a => a)
.Select(g => new {
Number = g.Key,
Total = g.Count()
})
.OrderBy(g => g.Total).Take(1);
Inside a foreach I can retrieve the result, I even have Intellisense for group properties {Number, Total}
foreach (var group in result)
{
// Display key and its values.
Console.WriteLine("Number = {0} Total: {1}", group.Number, group.Total);
}
But I don't like to do a foreach to do that, I prefer to do something like
result.Number or
result[0].Number
But doesn't work. What should be the right way to do it?
You are using Take(1) which still returns an IEnumerable. You probably want to use FirstOrDefault() which does not return an IEnumerable.
.OrderBy(g => g.Total).Take(1);
Then you can use it like result.Number and result.Total.
Your problem is that Take returns IEnumerable, so if you want to fetch only first element, in that case use FirstOrDefault:-
var result = list.GroupBy(a => a)
.Select(g => new
{
Number = g.Key,
Total = g.Count()
})
.OrderBy(g => g.Total).FirstOrDefault();
Then, you can simply say: - result.Number. Please note FirstOrDefault may return null so better check for nulls before accessing any property otherwise you may get Null Reference Exception.
Also, if you are looking for any specific index then you can use ElementAtOrDefault like this:-
var result = list.GroupBy(.....)..OrderBy(g => g.Total);
int SecondNumber = result.result.ElementAtOrDefault(1).Number;
But again be aware of NRE.
Check these Methods:
.FirstOrDefault(); //gets the first element
.Take(n); //gets the first n elements
.Skip(n).FirstOrDefault(); //gets the (n+1)th element
.Skip(n).Take(m); //gets the first m elements after n elements
An anonymous type does not implement IEnumerable<T>, there for you cannot access it via index.
If you only want the very first object you can use First() or FirstOrDefault. If you cast it to an array, you can use an index and then have support for your properties again:
result.FirstOrDefault().Number;
result.ToArray()[1].Number;

How to return a sorted array without storying it (immutability introduction)?

I have a dictionary that I project onto a list, which I sort. After that, I return the result. However, I'd like to know if it's possible (and if so - how) to return the sorted array without storing it first.
List<SomeType> thingies = dictionary.Select(element => element.Value).ToList();
thingies.Sort((the, thing) => the.Id.CompareTo(thing.Id));
return thingies;
The compiler nags that I can't put a void type as List type, which is understandable. But is it possible to make Sort spit out a new list to be returned (without polymorphism). Something along the lines of this.
return dictionary.Select(element => element.Value)
.ToList()
.Sort((the, thing) => { ... });
You need:
return dictionary.Select(element
=> element.Value.OrderBy(r=> r.Id).ToArray())
.ToList();
This will return an ordered List of value, without modifying the original Array.

Lambda expression to return one result for each distinct value in list

I currently have a large list of a class object and I am currently using the following lambda function to return elements that meet the condition.
var call = callList.Where(i => i.ApplicationID == 001).ToList();
This will return a list of objects that all have an id of 001.
I am now curious as to what different ApplicationIDs there are. So I would like a lambda function that will look into this list and return a list where all the element have a different ApplicationID but only fetches one of those.
If i understand your question you can try:
var list = callList.GroupBy(x => x.ApplicationID).Select(x => x.First()).ToList();
So if you have a list like:
AppID:1, AppID:1, AppID:2, AppID:2, AppID:3, AppID:3
Will return:
AppID:1 AppID:2 AppID:3
You can use either First or FirstOrDefault to get back one result
var call = callList.First(i => i.ApplicationID == 001);
If no call exisrs with an ApplicationID of 001 this will throw an exception. If this may be expected consider using:
var call = callList.FirstOrDefault(i => i.ApplicationID == 001);
Here null will be returned if no such call exists and you can handle accordingly in you code.
To find out what other ApplicationId's exist you can query:
var Ids = callList.Where(i => i.ApplicationID != 001).Select(i => i.ApplicationID).Distinct();
You are saying
I am now curious as to what different ApplicationIDs there are. So I
would like a lambda function that will look into this list and return
a list where all the element have a different ApplicationID but only
fetches one of those.
I would suggest that is never something you'd actually want. You either don't care about the elements, you care about all of them, or you care about a specific one. There are few (none?) situations where you care about a random one from the list.
Without knowing about which specific one you care, I can't give you a solution for that version. Allesandro has given you a solution for the random one.
When you only care about the distinct ID's you would end up with
callList.Select(c => c.ApplicationID).Distinct()
which just gives you all ApplicationIDs.
if you care about all of them, you'd end up with
callList.GroupBy(c => c.ApplicationID)
this will give you an IEnumerable<IGrouping<String, Thingy>> (where Thingy is the type of whatever the type of elements of callList is.)
This means you now have a collection of ApplicationID -> collection of Thingy's. For each distinct ApplicationID you'll have a "List" (actually IEnumerable) of every element that has that ApplicationID
If you care for the Thingy of that - for example - has the lowest value of property Foo you would want
callList.GroupBy(c => c.ApplicationID)
.Select(group => group.OrderBy(thingy => thingy.Foo).First()))
here you first Group them by ApplicationID, and then for each list of thingies with the sample ApplicationID you Select the first one of them if you Order them by Foo
There is a way to use the Distinct in the query, but it makes you take care about the values equality. Let's assume your type is called CallClass and try:
class CallClass : IEqualityComparer<CallClass>
{
public int ApplicationId { get; set; }
//other properties etc.
public bool Equals(CallClass x, CallClass y)
{
return x.ApplicationId == y.ApplicationId;
}
public int GetHashCode(CallClass obj)
{
return obj.GetHashCode();
}
}
Now you're able to query values distinctly:
var call = callList.Distinct().ToList();

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.

Categories

Resources