I am trying to achieve ranking functionality as below:
Name Points rank
ram 9 1
kamal 9 1
preet 8 2
lucky 7 3
kishan 6.5 4
devansh 6 5
neha 6 5
I have used below code to achieve this:
finalResult = finalResult.OrderByDescending(i => i.points).ThenBy(i => i.academy).ToList();
finalResult = finalResult.AsEnumerable() // Client-side from here on
.Select((player, index) => new RankingEntity()
{
competitorid = player.competitorid,
firstname = player.firstname,
lastname = player.lastname,
academy = player.academy,
points = player.points,
place = player.place,
eventId = player.eventId,
eventname = player.eventname,
categoryname = player.categoryname,
Rank = index + 1
}).ToList();
var t = (from i in finalResult
let rank = finalResult.First(x => x.points == i.points)
select new
{
Col1 = i,
Rank = rank.Rank
}).ToList();
List<RankingEntity> ttt = new List<RankingEntity>();
foreach (var item in t)
{
var a = item.Col1;
var row = new RankingEntity();
row.competitorid = a.competitorid;
row.firstname = a.firstname;
row.lastname = a.lastname;
row.academy = a.academy;
row.points = a.points;
row.place = a.place;
row.eventId = a.eventId;
row.eventname = a.eventname;
row.categoryname = a.categoryname;
row.Rank = item.Rank;
ttt.Add(row);
}
And i am getting result like below:
Please help what i am doing wrong.
What you are trying to achieve is a ranking of a "group" so group the results by the points and then order the groups. For each item in the group give the same rank.
finalResult.GroupBy(item => item.Points) // Group by points
.OrderDescendingBy(g => g.Key) // Order the groups
.Select((g, index) => new { Data = g, GroupRank = index + 1}) // Rank each group
.SelectMany(g => g.Data.Select(item => new RankingEntity
{
/* properties of each item */
Rank = g.GroupIndex
}); // Flatten groups and set for each item the group's ranking
The problem in your method is that you give the ranking for individual items and not the group. Then when you retrieve the rank for the group (from i in finalResult let rank = finalResult.First(x => x.points == i.points)...) you actually set for each item in the group the ranking of one of the elements in it. Therefore, if you first got the last item of the group - that will be the Rank value of each item in it.
Also notice that in the first line of your code you use ToList. Therefore there is not need to use AsEnumerable in the line under it - it is already a materialized in memory collection.
Related
Im making a delegate Func inside my method to check if schedualCode fits in a certain place in a list, where the limit is 3.
i want to count the distinct values of schedualCode in my list. my problem is that schedualCodeCount returns 1. when it should return 2.
this is my code
Func<string, bool> CheckTimeLimit = delegate (string schedualCode)
{
// check enrolled period count (where limit is 3)
//int periodCount = currentEnrollments.GroupBy(t => t.Times)
//.Select(t => t.Key.Select(key => key.PeriodCode == time.PeriodCode).Distinct()).Count();
var allTimes = currentEnrollments.SelectMany(key => key.Times).ToList();
List<string> schedualCodes = allTimes.Where(key => key.SchedualCode == schedualCode && key.ViewOnSchedual)
.Select(key => key.SchedualCode).ToList();
//schedualCodes List returns a list of count = 2 , and 2 strings exactly the same of value = "A1"
// Getting the distinct count of "A1"
int schedualCodeCount = schedualCodes.Distinct().Count();
// schedualCodeCount gets the value = 1, where it should be 2
// time fits if true
return schedualCodeCount < 3;
};
You are misunderstanding what Distinct does. You have two identical items, Distinct will remove the duplicates leaving you with 1. What you probably want to do is Group and then get the counts of each group.
For example:
var list = new List<string>() { "A1", "A1" };
Console.WriteLine(list.Count); // 2, obviously
var distinct = list.Distinct(); // select only the *distinct* values
Console.WriteLine(distinct.Count()); // 1 - because there is only 1 distinct value
var groups = list.GroupBy(s => s); // group your list (there will only be one
// in this case)
foreach (var g in groups) // for each group
{
// Display the number of items with the same key
Console.WriteLine(g.Key + ":" + g.Count());
}
I am sorting a list of strings by the order of another list of strings
List<string> lstCategories = new List<string>() { "string1", "string2", "string3"};
var groups = lstActivity.OrderBy(x => x.codeAC.Text).ThenBy(x => x.text).GroupBy(x => lstCategories.IndexOf(x.codeAC.Text));
foreach(var group in groups)
{
var slg = new SelectListGroup() { Name = group.Key.ToString() }; //problem is here
foreach(codeAC activity in group)
{
SelectListItem item = new SelectListItem() { Text = activity.text, Value = activity.ID.ToString(), Group = slg };
lstAssignments.Add(item);
}
}
The output of this is:
- 2 // I need this to be the text & not the index position
* text1
* text2
- 1
* text3
* text4
- 3
* text5
* text6
// so on and so on
The group is the literal index position of the list lstCategories.. How do I get it to Select the text and use that as the group and not the index position?
Thank you.
You are mixing grouping with ordering. After grouping you can order groups as well as group elements as you wish. In your sample not only the group key is not what you want, but also the result is not ordered (2, 1, 3).
Here is the query that does what you want:
var groups = lstActivity.OrderBy(x => x.codeAC.Text).ThenBy(x => x.text)
.GroupBy(x => x.codeAC.Text)
.OrderBy(g => lstCategories.IndexOf(g.Key));
and the of course use simply
var slg = new SelectListGroup() { Name = group.Key };
I have 5 datas in my list.
MyList
So i want to know , how can i get selected Id's Row Number ?
var MyProductId= 135;
var SelectedDataRowNumber = context.SP_MyList.ToList()
.Where(s=>s.Id==MyProductId)
.FirstOrDefault();
As instance,
My List has 5 datas like below,
Id-Name
6,Computer
135,KeyBoard
68,Mouse
98,Telephone
213,Laptop,
MyProductId is 135 so it matchs with Keyboard.It is row count number( index )must be "1" because 0 is Computer.
How can i get selected Id's row count(index) number ?
You can get it directly the element by Index if your list has implemented indexer. You can find that How to implement Indexer.
Another way could be as described in Here.
The above link shows it like:
COPIED From Above link:
Person agedTwenty = myList.Where<Person>( x => return x.Age == 20; ).Single<Person>();
int index = myList.IndexOf(agedTwenty);
or alternatively
int index = myList.Where<Person>( x => return x.Age == 20; ).Select<Person,int>( x =>
myList.IndexOf(x)).Single<int>();
In case there can be more than one result you'd do this:
IEnumerable<Person> allAgedTwenty = myList.Where<Person>( x => return x.Age == 20; );
IEnumerable<int> indices = allAgedTwenty.Select<Person,int>( x => myList.IndexOf(x) );
The first case will get you only one int and the second case will leave you with a list of ints.
If you want the count you need to groupBy
var SelectedDataRowNumber = context.SP_MyList.ToList()
.GroupBy(x = x.Id)
.Where(s=>s.Key == MyCustomerId )
.Select(x => new {Id = x.Key, Count = x.Count()});
This gives you the output
Id = 135
Count = 3 //depending on the actual count
alternative solution, if you want to output id, name and count:
//"items" is my custom list I created to reflect your data
var SelectedDataRowNumber = from x in items
group x by new {x.Id, x.Name} into g
where g.Key.Id == 135
select new
{
ID = g.Key.Id,
Name = g.Key.Name,
Count = g.Count()
};
Regarding your comment: is this the result you are expecting: http://abload.de/img/so1j5rta.jpg ?
I have table 4 columns.
JobId StateId Salary Expense
1 1 35,000 31,000
1 1 33,000 25,000
1 2 28,000 26,000
2 2 7,000 16,000
2 2 6,000 20,000
2 1 9,000 22,000
2 1 15,000 29,000
By using LINQ in C#, i want to group by JobId and StateId combination.For each combination i want an array of Salary and array of Expense.
I can get one column as a array by for each combination, by using this
(from r in myTable.AsEnumerable()
group r by new {
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
}).ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l=> (from i in l select i.Field<double>("Salary")).AsEnumerable()
);
How can i have Salary and Expense in two array for each group??
My goal is to find average Salary and average Expense for each combination and do some other operation. Or at least tell me how can select multiple columns as separate array.
Note: I don't want collection of anonymous objects for each combination.
To select two different columns as collections in your query you can do this:
var result =
(from r in myTable.AsEnumerable()
group r by new
{
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
} into g
select new
{
g.Key,
Salaries = g.Select(x => x.Field<double>("Salary")),
Expenses = g.Select(x => x.Field<double>("Expense"))
})
.ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l => new { l.Salaries, l.Expenses }
);
Then you can compute the averages fairly easily:
var averageSalary = result[...].Salaries.Average();
var averageExpense = result[...].Expenses.Average();
But if all you really need is the averages, this will work:
var result =
(from r in myTable.AsEnumerable()
group r by new
{
jobId = r.Field<int>("JobId"),
stateId = r.Field<int>("StateId")
} into g
select new
{
g.Key,
AverageSalary = g.Average(x => x.Field<double>("Salary")),
AverageExpense = g.Average(x => x.Field<double>("Expense"))
})
.ToDictionary(
l => Tuple.Create(l.Key.jobId, l.Key.stateId),
l => new { l.AverageSalary, l.AverageExpense }
);
Do not use LINQ for this kind of statement. If you need to count the groupings and the compute some sort of average salary/expense you could try a list:
List<myType> myList = new List<myType>();
//add stuff to myList
List<myType> JobID1 = new List<myType();
List<myType> JobID2 = new List<myType();
foreach(var item in myList)
{
if(item.JobID == 1)
JobID1.add(item);
if(item.JobID == 2)
JobID2.add(item);
}
int avgSalOne;
foreach(var item in JobID1)
{
avgSalOne += item.Salary;
}
avgSalOne = avgSaleOne / JobID2.Count;
//Note that you get Job Id 2 average salary the same way, and also the Expense by changing item. Salary to item.Expense
I have a table with data similar to below:
Group TimePoint Value
1 0 1
1 0 2
1 0 3
1 1 3
1 1 5
I want to project a table as such:
Group TimePoint AverageValue
1 0 2
1 1 4
EDIT: The data is in a datatable.
Anybody any ideas how this can be done with LINQ or otherwise?
Thanks.
You need to perform Group By
The linq you need is something like:
var query = from item in inputTable
group item by new { Group = item.Group, TimePoint = item.TimePoint } into grouped
select new
{
Group = grouped.Key.Group,
TimePoint = grouped.Key.TimePoint,
AverageValue = grouped.Average(x => x.Value)
} ;
For more Linq samples, I highly recommend the 101 Linq samples page - http://msdn.microsoft.com/en-us/vcsharp/aa336747#avgGrouped
Here's a more function-oriented approach (the way I prefer it). The first line won't compile, so fill it in with your data instead.
var items = new[] { new { Group = 1, TimePoint = 0, Value = 1} ... };
var answer = items.GroupBy(x => new { TimePoint = x.TimePoint, Group = x.Group })
.Select(x => new {
Group = x.Key.Group,
TimePoint = x.Key.TimePoint,
AverageValue = x.Average(y => y.Value),
}
);
You can do:
IEnumerable<MyClass> table = ...
var query = from item in table
group item by new { item.Group, item.TimePoint } into g
select new
{
g.Key.Group,
g.Key.TimePoint,
AverageValue = g.Average(i => i.Value)
};
Assuming a class like this:
public class Record
{
public int Group {get;set;}
public int TimePoint {get;set;}
public int Value {get;set;}
}
var groupAverage = from r in records
group r by new { r.Group, r.TimePoint } into groups
select new
{
Group = groups.Key.Group,
TimePoint = groups.Key.TimePoint,
AverageValue = groups.Average(rec => rec.Value)
};