Reputation: 5150
I have a list I am populating with total sales from a team.
lstTeamSales.OrderBy(x => x.TotalSales);
This list has an int userID
and a decimal totalSales
.
I order it by totalSales
. How can I at that point figure out the rank for the person logged in?
I know I can compare the person who is logged in by his userID to that of the userID in the list. If he is #3 in sales I need to return an int of his rank which would be Rank 3.
Upvotes: 1
Views: 1426
Reputation: 1938
The question can be rephrased to "How do I get index of element in IEnumerable". Here is the answer: How to get index using LINQ? Here is how to use it:
int rank = lstTeamSales.OrderBy(x => x.TotalSales).FindIndex(x => x.userID == currentUserID);
And this will be slightly more efficient than Select
based approaches.
It appears .FindIndex is not supported for LINQ. Any idea how to implement that functionality?
I may have figured it out testing it now. I just added .ToList() after the ORderBy().
No-no-no-no! It kills the whole idea :( The idea is to add extension method FindIndex
to IEnumerable. And then use it. See example:
static class FindIndexEnumerableExtension
{
public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate)
{
if (items == null) throw new ArgumentNullException("items");
if (predicate == null) throw new ArgumentNullException("predicate");
int retVal = 0;
foreach (var item in items)
{
if (predicate(item)) return retVal;
retVal++;
}
return -1;
}
}
class YourClass
{
void YourMethod()
{
lstTeamSales.OrderBy(x => x.TotalSales).FindIndex(x => x.UserID == currentUserID);
}
}
After you define class FindIndexEnumerableExtension
with FindIndex
extension method, you can use this method anywhere in your code. All you need is just add using
directive with module where FindIndexEnumerableExtension
is defined. This is, basically, how LINQ works.
If you don't want to go with this solution then, at least, convert lstTeamSales to List before sorting it. And sort it using List<>.Sort()
method.
Upvotes: 2
Reputation: 7724
The Select((item, index) => ...)
form allows for this (as shown by Simon), however as DMac mentions you probably want to consider duplicates. To incorporate this in a Select
, you could use GroupBy:
lstTeamSales
.OrderByDescending(x => x.TotalSales).GroupBy(x => x.TotalSales)
.Select((group, i) => new {
Rank = i + 1,
Users = group.Select(x => x.UserId)
})
This would provide you with a list of ranks along with the lists of users who have that rank. Or you could flatten this with SelectMany, to get each user with its rank:
lstTeamSales
.OrderByDescending(x => x.TotalSales).GroupBy(x => x.TotalSales)
.SelectMany((x, i) => new {
Rank = i + 1,
User = x.UserId
})
You could filter this sequence to find users, but if you only want to look up a specific user's rank, then DMac's solution is the most direct. The above would be more useful for example if you wanted to list the top 5 sellers (see Take).
Upvotes: 0
Reputation: 5290
Given a list of total sales, lstTeamSales
and a number representing the sales you wish to find the rank for, userSales
, what you'll need is the number of total sales in lstTeamSales
that exceed userSales
. If it's rank you want, then you'd probably want to exclude ties in the rank (i.e. if the top two sales numbers are both 1000, then they'd both be ranked 1)
You can do this simply by projecting only the sales numbers with Select
, remove ties with a Distinct
call, then use Count
:
lstTeamSales.Select(x => x.TotalSales).Distinct().Count(x => x > userSales)
That would give you the total number of sales that are higher than the current user. From there, the rank of the current user is one above that number:
var rank = 1 + lstTeamSales.Select(x => x.TotalSales).Distinct().Count(x => x > userSales)
Upvotes: 1
Reputation: 14870
You can use the select extenstion that takes a Func<TSource, Int32, TResult>
(or the Expression equivalent) like so:
var userId = /* the userId */;
lstTeamSales.OrderBy(x => x.TotalSales).Select((x, i) => new
{
x.UserId,
x.TotalSales,
Rank = i + 1
}).FirstOrDefault(x => x.UserId == theUserId);
This will return an object with the user id, the total sales and the rank where the user id is fixed. It will return null
if there is no entity where UserId = theUserId
in the collection.
The index (i
in the example) is 0-based. Adjust as needed.
Upvotes: 1