How can order my list using Linq equals rank() over in SQL ?
For example rank is my List<Player>
class Player
{
public int Id;
public int RankNumber;
public int Points;
public int Name;
}
Original Rank list:
RankNumber Points Name Id
1 100 James 01
2 80 Mike 50
3 80 Jess 22
4 50 Jack 11
5 50 Paul 03
6 10 Monik 13
I need this Rank:
RankNumber Points Name Id
1 100 James 01
2 80 Mike 50
2 80 Jess 22
4 50 Jack 11
4 50 Paul 03
6 10 Monik 13
I don't think there is a good way to convert this directly to Linq to SQL but you could do this:
var rankedPlayers = players
.OrderByDescending(p => p.Points)
.Select((p, r) => new Player
{
Id = p.Id,
RankNumber = players.Where(pl => pl.Points > p.Points).Count() + 1,
Points = p.Points,
Name = p.Name
});
It gives you the correct output but will convert horribly and inefficiently to SQL. So I would suggest this modification which materialises the data to a list before creating the ranks:
var rankedPlayers = players
.OrderByDescending(p => p.Points)
.ToList() //<--- Add this
.Select((p, r) => new Player
{
Id = p.Id,
RankNumber = players.Where(pl => pl.Points > p.Points).Count() + 1,
Points = p.Points,
Name = p.Name
});
You can try below expression:
var newData = players
.OrderByDescending(x => x.Points)
.GroupBy(x => x.Points)
.SelectMany((x, index) => x.Select(y => new Player
{
Name = y.Name,
Points = y.Points,
RankNumber = index + 1,
Id = y.Id
}));
players contains IEnumerable of objects of type Player and newData contains ordered data with rank.
Related
The tables I included in my query are not included in the query after the groupby.
but if I do a tolist before groupby, the problem is solved, but it didn't seem like a very good solution. Is it possible for me to solve this problem in a different way?
[HttpGet]
public async Task<IActionResult> GetMostPurchased()
var mostRequestUsers = await dbContext.UserProducts
.Include(x => x.Products).Include(x => x.User)
.GroupBy(x => new { x.UserId, x.ProductId })
.Select(g => new
{
MostPurchased = g.Key.ProductId,
UserId = g.Key.UserId,
Count = g.Count(),
// how can i get the following data fields? After grouping, I cannot access the
"UserProducts" and "Products" table.
ProductName = g.Select(x => x.Product.Name),
ProductPrice = g.Select(x => x.Product.Price),
ProductDesc = g.Select(x => x.Product.Desc),
UserFirstName = g.Select(x => x.User.UserFirstName),
UserLastName = g.Select(x => x.User.UserLastName ),
UserPhoneNumber = g.Select(x => x.User.UserPhoneNumber)
//
})
.GroupBy(x => x.UserId)
.Select(g => g.OrderByDescending(t => t.Count).First());
return mostRequestUsers;
// UserProducts
ID UserId ProductId
1 10 1
2 10 2
3 10 3
4 11 4
5 11 5
6 11 6
6 11 4
6 11 3
6 11 2
6 12 1
6 12 4
6 12 5
// User
ID FirstName LastName
1 Tom Jack
2 Brad Pitt
3 John Rock
// Product
ID ProductName Price
1 Apple 20
2 Dell 30
3 Lenovo 40
my purpose: I want to list the most purchased products by users by user ID.
UserId: 10, MostPurchased: 1, Count: 2, ProductName: null, ProductPrice : null, UserFirstName: null, UserLastName : null
UserId: 11, MostPurchased: 2, Count: 3, ProductName: null, ProductPrice : null, UserFirstName: null, UserLastName : null
UserId: 12, MostPurchased: 3, Count: 2, ProductName: null, ProductPrice : null, UserFirstName: null, UserLastName : null
Include do not work with GroupBy and can be omitted. Just add needed field to grouping key:
var users = dbContext.User
.GroupBy(x => new { x.UserId, x.CarId, x.Car.Name })
.Select(g => new
{
UserId = g.Key.UserId,
CarId = g.Key.CarId,
CarName = g.Key.Name
};
Or rewrite query to use Distinct()
var users = dbContext.User
.Select(x => new
{
x.UserId,
x.CarId,
CarName = x.Car.Name
})
.Distinct();
I am using c#.net desktop application
I have gridview that looks like this
ibengin iend code preferredText Affirmation tag codeScheme Value
1 10 Kitkat Yes Choc
11 15 Mars Yes Choc
16 20 Bounty Yes Choc
21 27 A1 Kitkat Yes Choc USA
28 32 Bounty Yes Choc
33 47 Bounty No Choc
48 61 A1 Kitkat Yes Choc USA
62 65 B7 Mars Yes Choc UK
66 77 Kitkat Yes Choc USA
78 81 Kitkat Yes Choc
I want it to be grouped as follows
Affirmation PreferredText Count Value CodingScheme Code Positions
Yes Kitkat 5 USA A1 1:10,21:27,48:61,66:77,78:81
Yes Mars 2 UK B7 11:15,62:65
Yes Bounty 2 16:20,28:32
No Bounty 1 33:47
I have almost finished it, but one problem remained.
I want to use the first value of codeScheme and code as shown above
Here is the code that I have done so far.
appreciate any help
var lstInfo = grdBreakDown.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow) // either..
.Where(x => x.Cells["Tag"].Value.ToString() == Tag) //..or or both
.GroupBy(x => new
{
grpAffirmation = x.Cells["Affirmation"].Value.ToString(),
grpPreferredText = x.Cells["preferredText"].Value.ToString(),
grpValue = (cbxValue.Checked ? x.Cells["Value"].Value.ToString() : "")
})
.Select(y => new
{
Affirmation = y.Key.grpAffirmation,
PreferredText = y.Key.grpPreferredText,
Count = y.Count(),//.ToString(),
Value = y.Key.grpValue,
Positions = string.Join(",", y.Select(i => i.Cells["ibegin"].Value.ToString() + ":" + i.Cells["iend"].Value.ToString()))
})
.OrderByDescending(y => y.Count)
.ToList();
I found the answer
var lstInfo = grdBreakDown.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow) // either..
.Where(x => x.Cells["Tag"].Value.ToString() == Tag) //..or or both
.GroupBy(x => new
{
grpAffirmation = x.Cells["Affirmation"].Value.ToString(),
//grpCodingScheme = x.Cells["codingScheme"].Value.ToString(),
//grpCode = x.Cells["code"].Value.ToString(),
grpPreferredText = x.Cells["preferredText"].Value.ToString(),
grpValue = (cbxValue.Checked ? x.Cells["Value"].Value.ToString() : "")
})
.Select(y => new
{
Affirmation = y.Key.grpAffirmation,
CodingScheme = string.Join(",", y.Select(i => i.Cells["codingScheme"].Value.ToString()).Distinct()).TrimEnd(','),
Code = string.Join(",", y.Select(i => i.Cells["code"].Value.ToString()).Distinct()).TrimEnd(','),
PreferredText = y.Key.grpPreferredText,
Count = y.Count(),//.ToString(),
Value = y.Key.grpValue,
Positions = string.Join(",", y.Select(i => i.Cells["ibegin"].Value.ToString() + ":" + i.Cells["iend"].Value.ToString()))
})
.OrderByDescending(y => y.Count)
.ToList();
I'm writing a method that selects customer records based on a group by with the most recent purchase total equal (passed to the method).
I did some search and nothing showed up that helps me to achieve the desired output.
customerList.GroupBy(x=> x.CustomerID },
(key, result) =>
{
var orderedList = result.OrderByDescending(c => c.Date).ToList();
return new Customer
{
CustomerID = orderedList.First().CustomerID,
PurchaseID = orderedList.First().PurchaseID,
Price = orderedList.First().Price,
};
});
CUSTOMERID
PURCHACEID
PRICE
DATE
1
11
235
01-03-2021
1
12
230
01-03-2021
1
14
235
05-04-2021
1
15
535
06-04-2021
1
16
230
07-04-2021
If I'm looking for most recent total purchase price of 1000, I should get
CUSTOMERID
PURCHACEID
PRICE
DATE
1
14
235
05-04-2021
1
15
535
06-04-2021
1
16
230
07-04-2021
You probably need to produce a separate list with the cumulative sums. You can then use TakeWhile to take items until some condition is reached
var list = new[] { 1, 2, 3, 4, 5 };
var sum = 0;
var cummulativeSums = list.Select(v => sum += v).ToList();
var result= list.TakeWhile((_, index) => cummulativeSums[index] < 7).ToList();
// 1, 2, 3
Or you could do it all in one go by creating intermediate pair of values.
var list = new[] { 1, 2, 3, 4, 5 };
var sum = 0;
var result = list.Select(value => (sum += value, value))
.TakeWhile(pair => pair.Item1 < 7)
.Select(pair => pair.value)
.ToList();
I am using ASP.NET MVC and have a many-to-many table that as follows:
custID | objID
================
1 2
1 3
2 5
2 2
3 2
Both userID and objID are Foreign Keys linking to other tables. What I would like to do is get the highest count based on objID. The table above will yield the following results:
objID | objName | Price
=======================
2 | Chicken | 10
The custID does not matter in this scenario as I just want to get the objID with the highest count.
I've tried the following but i'm stuck here:
//retrieve many-to-many table
var retrieved = db.Customer.Include(c => c.Objects)
var topID = retrieved.GroupBy(q => q.objID)
.OrderByDescending(g => g.Count())
Should do the trick.
var topID = retrieved.GroupBy(q => q.objID)
.Select(g => new { Id = g.Key, Total = g.Count() })
.OrderByDescending(g => g.Total).First().Id;
List<int> lst = new List<int>() { 1, 3, 4, 5, 1, 2, 3, 4, 5, 2, 3, 2 };
var count = lst.GroupBy(q => q).OrderByDescending(s => s.Count())
.First().Key;
var lstKeyWithCount lst.GroupBy(i => i).Select(g => new { Key=g.Key,Count=g.Count() });
in lstKeyWithCount variable you get key with count.
in count variable you get most times repeated value.
I need to return a chronological list of alerts. The method 1 below does this.
I also need to grab alerts of a certain alert type such that if the AlertTypeID = {1,2,3,6,8,9,x}, then return those alerts by CreateDate and order them by month.
The method 2 below does this.
Now, I need to insert the results of method 2 into the results of method 1.
Even though the items returned by method 2 are organized by month, we only
care about the most recent. Only the most recent subgroup item from method 2 need to follow the chronological order of the items returned from method 1. This subgroup
item will determine where the rest of the monthly items from subgroup 2 are placed.
A final requirement is that duplicates must be removed. If an item is being returned
from the subgroup alerts, it cannot also exist in the primary group alerts.
Below I have provided an illustration of the effect I am trying to achieve:
december alert 1 (12/23/2012)
december alert 2 (12/21/2012)
december alert 3 (12/20/2012)
december subalert 1 (12/19/2012)
december subalert 2 (12/18/2012)
december subalert 3 (12/04/2012)
december subalert 4 (12/01/2012)
december alert 4 (12/18/2012)
december alert 5 (12/12/2012)
november alert 1 (11/22/2012)
november alert 2 (11/16/2012)
november subalert 1 (11/14/2012)
november subalert 2 (11/08/2012)
november alert 3 (11/12/2012)
Code:
All Alerts By DateTime
List<Alert> result = new List<Alert>();
using(NeuroLabLinqDataContext dc = conn.GetContext())
{
IEnumerable<Alert> alerts = (from a in dc.Alerts
where a.AccountID == AccountID
orderby a.CreateDate descending
select a).Take(40);
result = alerts.ToList();
}
return result;
Alert Types By Month
List<Alert> result = new List<Alert>();
int[] alertTypes = {1,2,3,4,5,6,7,8,9};
using (NeuroLabLinqDataContext dc = conn.GetContext())
{
IEnumerable<Alert> alerts = (from a in dc.Alerts
where a.AccountID == AccountID &&
alertTypes.Contains(a.AlertTypeID)
orderby ((DateTime)a.CreateDate).Month ascending
select a).ToList();
}
return result;
The grouping for the final select statement should look something like this:
select new { Date = alerts.CreateDate,
Message = alerts.Message,
Type = alert.AlertTypeID,
RecentActivity = [if from method 2, then true] };
UPDATE: Updated Method
public List<Alert> GetAlertsByAccountID(Int32 AccountID, params int[] alertTypes)
{
List<Alert> result = new List<Alert>();
using (NeuroLabLinqDataContext dc = conn.GetContext())
{
var all = (from a in dc.Alerts
where a.AccountID == AccountID
orderby a.CreateDate descending
select a);
int abc = all.Count();
var first = all
.Where(a => a.AccountID == AccountID) && !alertTypes.Contains(a.AlertTypeID))
.OrderByDescending(a => a.CreateDate)
.GroupBy(a => a.CreateDate.Date)
.ToDictionary(g => g.Key);
var firstKeys = first.Keys.Cast<DateTime>()
.ToList().OrderBy(k => k);
var second = all
.Where(a => a.AccountID == AccountID) && alertTypes.Contains(a.AlertTypeID))
.OrderBy(a => a.CreateDate.Month)
.GroupBy(a => a.CreateDate.Month)
.ToDictionary(g => firstKeys
.First(k => k > g.OrderByDescending(a => a.CreateDate)
.FirstOrDefault().CreateDate));
var combined = first
.GroupJoin(
second,
fk => fk.Key,
sk => sk.Key,
(d, l) => d.Value
.Union(l.SelectMany(i => i.Value).ToArray()))
.SelectMany(i => i);
result = combined.ToList();
}
return result;
}
Thanks to John, I am much further along. At the moment, I am getting the following error:
Sequence contains no matching element
at this line (I am pretty sure):
.First(k => k > g.OrderByDescending(a => a.CreateDate)
For what it is worth, here is what the data in my Alerts table looks like.
AlertID AccountID CreateDate Timestamp AlertTypeID Message
122 5 2008-03-11 20:48:07.983 0x00000000000128FB 9 sdfs
123 1 2008-03-11 20:48:39.957 0x00000000000128FE 8 sdfsd
124 5 2008-03-11 20:48:39.977 0x00000000000128FF 8 sdfs
125 5 2008-03-11 20:48:40.017 0x0000000000012901 8 asdfa
126 1 2008-03-12 22:57:42.160 0x00000000000130B3 4 sfsf
127 5 2008-03-12 22:57:42.337 0x00000000000130B4 4 sdfsd
128 5 2008-03-13 09:42:14.237 0x0000000000013889 4 sdfsd
129 5 2008-03-13 09:42:31.957 0x000000000001388B 4 sdfsd
130 5 2008-03-13 09:42:45.397 0x000000000001388D 5 asdfsdf
131 1 2008-03-16 14:52:17.197 0x0000000000014822 9 asdfsdf
132 1 2008-04-12 15:25:17.330 0x000000000001B582 3 sfasdf
133 5 2008-04-12 15:25:17.700 0x000000000001B583 3 dfsfds
134 6 2008-04-14 08:37:03.273 0x000000000001BD87 3 aasfsd
135 6 2008-04-14 08:37:15.270 0x000000000001BD89 3 fhfsdf
136 6 2008-04-14 08:38:45.120 0x000000000001BD8B 2 ghsdgd
137 6 2008-04-14 08:41:30.407 0x000000000001BD9A 4 fghsdfg
138 6 2008-04-14 08:42:30.800 0x000000000001BD9C 4 gfsdf
139 6 2008-04-14 08:42:43.763 0x000000000001BD9E 5 sdfsdf
140 6 2008-04-14 08:49:25.450 0x000000000001BDAA 9 sdfasdfa
141 6 2008-04-14 08:49:34.237 0x000000000001BDAC 9 sdfasdf
142 1 2008-04-14 08:50:23.380 0x000000000001BDAF 8 sdfhdfhsg
143 6 2008-04-14 08:50:23.567 0x000000000001BDB0 8 dgasdf
144 5 2008-04-14 08:50:23.690 0x000000000001BDB1 8 dgasdf
145 6 2008-04-14 08:50:23.747 0x000000000001BDB2 8 dgasdf
147 1 2008-06-24 14:22:41.183 0x00000000000222E6 14 dgasdf
148 5 2008-06-24 14:22:41.617 0x00000000000222E7 14 dgasdf
149 6 2008-06-24 14:22:41.623 0x00000000000222E8 14 dgasdf
150 1 2008-06-24 20:11:57.757 0x0000000000022AB3 13 dgasdf
151 5 2008-06-24 20:11:57.947 0x0000000000022AB4 13 dgasdf
152 6 2008-06-24 20:11:57.953 0x0000000000022AB5 13 dgasdf
153 1 2008-07-03 18:41:51.067 0x0000000000028888 14 dgasdf
154 5 2008-07-03 18:41:51.230 0x0000000000028889 14 dgasdf
155 6 2008-07-03 18:41:51.237 0x000000000002888A 14 dgasdf
156 1 2008-07-03 18:46:17.873 0x000000000002888D 14 dgasdf
157 5 2008-07-03 18:46:17.937 0x000000000002888E 14 dgasdf
158 6 2008-07-03 18:46:17.940 0x000000000002888F 14 dgasdf
The key is breaking the two groups down into dictionaries, using the dates from the first list as the keys to the dictionary, and picking the closest key after the second list item date as the key for the second dictionary.
Once you have the two dictionaries, each using a common key value for the types and the subtypes you can just do a GroupJoin and SelectMany to get the results in a sorted list.
(* Note that the answer is based on a slightly different, earlier version of the question, I'm not going to take the time to update the answer because I think the fundamental problem is illustrated and solved in this answer already)
update 2
I realize the problem you're seeing with the First() call is that some of your subalert items might be newer than any other alert item which would cause your exception. I addressed that by adding a 'surrogate' key to the first dictionary using DateTime::MaxValue, and then I no longer filter out the subalerts from the first list, I just use .Distinct() on the final result to remove duplicates
Using linqpad I mocked up this question and solved it using dictionaries and GroupJoin
var all = new []{
new {date = DateTime.Parse("2012-12-23"), type = "alert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-12-21"), type = "alert", value = 2, accountId = 333 },
new {date = DateTime.Parse("2012-12-20"), type = "alert", value = 3, accountId = 333 },
new {date = DateTime.Parse("2012-12-18"), type = "alert", value = 4, accountId = 333 },
new {date = DateTime.Parse("2012-12-12"), type = "alert", value = 5, accountId = 333 },
new {date = DateTime.Parse("2012-11-22"), type = "alert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-11-16"), type = "alert", value = 2, accountId = 333 },
new {date = DateTime.Parse("2012-11-12"), type = "alert", value = 3, accountId = 333 },
new {date = DateTime.Parse("2012-12-19"), type = "subalert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-12-18"), type = "subalert", value = 2, accountId = 333 },
new {date = DateTime.Parse("2012-12-04"), type = "subalert", value = 3, accountId = 333 },
new {date = DateTime.Parse("2012-12-01"), type = "subalert", value = 4, accountId = 333 },
new {date = DateTime.Parse("2012-11-14"), type = "subalert", value = 1, accountId = 333 },
new {date = DateTime.Parse("2012-11-08"), type = "subalert", value = 2, accountId = 333 },
/*add*/ new {date = DateTime.Parse("2012-12-25"), type = "subalert", value = 9, accountId = 333 },
};
var first = all
.Where(a=>a.accountId == 333 /* removed && type != "alert" */)
.OrderByDescending(a=>a.date)
.GroupBy(a=>a.date.Date)
.ToDictionary(g=>g.Key);
var firstKeys = first.Keys
.Cast<DateTime>()
.Union(new []{DateTime.MaxValue}) /* added this 'surrogate' key */
.OrderBy(k=>k)
.ToArray();
var second = all
.Where(a=>a.accountId == 333 && a.type == "subalert")
.OrderBy(a=>a.date.Month)
.GroupBy(a=>a.date.Month)
.ToDictionary(g=>firstKeys.First(k=>k > g.OrderByDescending(a=>a.date).FirstOrDefault().date));
var combined = first
.GroupJoin(
second,
fk=>fk.Key,
sk=>sk.Key,
(d,l)=>d.Value
.Union(l.SelectMany(i=>i.Value).ToArray()))
.SelectMany(i=>i)
.Distinct(); /* Added this to remove duplicates */
combined.Dump();
Which yields: