Take(limit) list inside Groupby in Entity Framework - c#

I need to take (for example, 2), 2 messages from a conversation
I dont care about how my list looks like, but i want only 2 messages from id 1, 2 messages from id2, and go on
example:
id = idConversation
Id | MessageId | Message
---|-----------|--------
1 | 1 | "asd"
1 | 2 | "asd2"
1 | 3 | "asd3"
1 | 4 | "asd4"
2 | 5 | "asd5"
3 | 6 | "asd6"
3 | 7 | "asd7"
3 | 8 | "asd8"
3 | 9 | "asd9"
3 | 10 | "asd10"
4 | 11 | "asd11"
4 | 12 | "asd12"
4 | 13 | "asd13"
and i want that
Id MessageId Message
---|-----------|--------
1 | 1 | "asd"
1 | 2 | "asd2"
2 | 5 | "asd5"
3 | 6 | "asd6"
3 | 7 | "asd7"
4 | 11 | "asd11"
4 | 12 | "asd12"
i can grouby idConversation, but i cant limit quantity using grouby in a conversation.
var test = unitOfWork.ChatMensagemRepository.GetAll()
.Where(x => x.PessoaCodigoPessoa == codigoRemetente)
.GroupBy(x => x.ChatConversaCodigoChatConversa)
.Select(group => new
{
codigoChat = group.Key,
list = group.Select(mensagem => new
{
// do stuff
})
}).ToList();
this is ok... but dont limit my list, when i do group.take(2).Select..... give me "Subquery returns more than 1 row"
var test = unitOfWork.ChatMensagemRepository.GetAll()
.Where(x => x.PessoaCodigoPessoa == codigoRemetente)
.GroupBy(x => x.ChatConversaCodigoChatConversa)
.Select(group => new
{
codigoChat = group.Key,
list = group.Take(2).Select(mensagem => new
{
// do stuff
})
}).ToList();
error : Subquery returns more than 1 row
var test = unitOfWork.ChatMensagemRepository.GetAll()
.Where(x => x.PessoaCodigoPessoa == codigoRemetente)
.GroupBy(x => x.ChatConversaCodigoChatConversa)
.Select(group => new
{
codigoChat = group.Key,
list = group.Select(mensagem => new
{
// do stuff
}).take(2)
}).ToList();
error : Subquery returns more than 1 row

It is caused, because EF provider for MySQL or server itself can't translate this linq to SQL, so you should at first get data from server and only then group it with Take(2):
var test = unitOfWork.ChatMensagemRepository.GetAll()
.Where(x => x.PessoaCodigoPessoa == codigoRemetente)
//this section is added
.Select(x => new
{
x.ChatConversaCodigoChatConversa,
x.prop1,//specify only columns, which you need for below code with Take
x.prop2
}).ToList()
//end of section
.GroupBy(x => x.ChatConversaCodigoChatConversa)
.Select(group => new
{
codigoChat = group.Key,
list = group.Take(2).Select(mensagem => new
{
mensagem.prop1,
mensagem.prop2
}).ToList()
}).ToList();

Related

Get the specific count of each column using GROUP BY LINQ

I want to get the count of each items in columns using LINQ GroupBy, however I don't get the expected output.
Example :
NAME | Items | Items2
1.ken | asd | zxc
2.kent | null | zwe
3.ken | qwe | null
Expected output:
NAME | Items | Items2
ken | 2 | 1
kent | 0 | 1
Code:
var Data = Items.GroupBy(z => z.Name)
.Select(s =>
new {
Name = s.Key,
Items = s.Count(q => q.Items),
Items2 = s.Count(x => x.Items2)
})
.ToList();
The above code does not work.
I think the null values create issues in Count() in the expected output. So I suggest you to add a condition in Count() to exclude null values. So your query will look like:
var Data = Items.GroupBy(z => z.Name)
.Select(s =>
new
{
Name = s.Key,
Items = s.Count(q => q.Items != null),
Items2 = s.Count(x => x.Items2 != null)
})
.ToList();

Linq Lambda : Getting last user updated with group of same user using group by and count?

I want to get last user updated with a linq lambda expression using group by and the count of the remaining users. I don't know how I can do that.
here is my data :
userid | name | datetime | isdelete
1 | abc | 16-03-2017 15:45:59 | 0
1 | abc | 16-03-2017 12:45:10 | 0
2 | xyz | 16-03-2017 15:45:59 | 0
1 | abc | 16-03-2017 10:40:59 | 0
I want the result to look like this:
userid | name | datetime | count
1 | abc | 16-03-2017 15:45:59 | 3
2 | xyz | 16-03-2017 15:45:59 | 1
Here the count for userid = 1 should be 3 as there are three records for that id in the table.
I have written this query, but it is getting all the records.
List<Users> UList = new List<Users>();
UList = db.Users.Where(a => a.isdelete == false)
.OrderByDescending(a => a.datetime)
.Skip(skip)
.Take(pageSize)
.ToList();
Anyone know how I can get the data I want? Please let me know using linq lambda expression.
You need to group by user, than sort each group and take first from each group
var UList = (db.Users
.Where(a => a.isdelete == false)
.GroupBy(a => a.UserId)
.Select(g => new MyNewClass
{
Count = g.Count(),
User = g.OrderByDescending(a => a.datetime).First()
}
))
.Skip(skip)
.Take(pageSize)
.ToList();
You forgot to group your data:
var result = db.Users.Where(a => !a.isdelete)
.GroupBy(x => x.userid)
.Select(x => new User
{
userid = x.Key,
name = x.Last().Name,
datetime = x.OrderByDescending(a => a.datetime).First().datetime,
count = x.Count()
});
EDIT: This might be not optimal considering the performance as the call to Last and OrderByAscending will both iterate the whole data. To overcome this a bit you may re-structure this query a bit:
var result = db.Users.Where(a => !a.isdelete)
.GroupBy(x => x.userid)
.Select(x => new
{
user = x.OrderByDescending(a => a.datetime).First(),
count = x.Count()
})
.Select(x => new User {
name = x.user.name,
userid = x.user.userid,
datetime = x.user.datetime,
count = x.count
});

Get unique row with many conditions

I have a history table of ordered products
+----+------------+--------+
| id | IdProduct | status |
+----+------------+--------+
| 1 | 100 | 1 |
| 2 | 100 | 2 |
| 3 | 100 | 3 |
| | | |
| 4 | 200 | 1 |
| 5 | 200 | 2 |
| | | |
| 6 | 300 | 1 |
| 7 | 300 | 2 |
+----+------------+--------+
I want to get only the products who have the status 2 but not 3
+----+------------+
| id | IdProduct |
+----+------------+
| 5 | 200 |
| | |
| 7 | 300 |
+----+------------+
How can I achieve this using a Linq request
Using linq to sql you can do:
var result = history.GroupBy(item => item.IdProduct)
.Where(grp => grp.Any(item => item.Status == 2) &&
!grp.Any(item => item.Status == 3))
.Select(grp => new {
IdProduct = grp.Key,
Id = grp.Max(item => item.Id)
});
Or:
var result = history.GroupBy(item => item.IdProduct)
.Where(grp => grp.Any(item => item.Status == 2) &&
!grp.Any(item => item.Status == 3))
.Select(grp => grp.Where(item => ite.Status == 2).FirstOrDefault());
In your case both these should return the same because the max(id) correleted with the result you wanted
If you know each status exists only once then you can try the following. The idea is that status 3 items equal -1, 2 equals 1 and the rest 0. Only groups that have status 2 but not 3 will have the result of 1
var result = history.Select(item => new { Item = item, Valid = item.Status == 2 ? 1 : item.Status == 3 ? -1 : 0 })
.GroupBy(item => item.Item.IdProduct)
.Where(grp => grp.Sum(item => item.Valid) == 1)
.Select(item => item.Item);
I didnt read the last line in question How can I achieve this using a Linq request may be that reason for downvote..
Will keep this answer if someone is looking to solve it in SQL
Here is one way using Group by and Having clause
SELECT *
FROM yourtable
WHERE IdProduct IN (SELECT IdProduct
FROM Yourtable
GROUP BY IdProduct
HAVING Count(CASE WHEN status = 3 THEN 1 END) = 0
AND Count(CASE WHEN status = 2 THEN 1 END) > 0)
AND Status = 2
Count(CASE WHEN status = 3 THEN 1 END) = 0
This condition is to make sure status = 3 does not exist any row for each ID
Count(CASE WHEN status = 2 THEN 1 END) > 0
This condition is to make sure alteast one row with status = 2 for each ID
Declare #YourTable table (id int,IdProduct int,status int)
Insert Into #YourTable values
( 1 , 100 , 1 ),
( 2 , 100 , 2 ),
( 3 , 100 , 3 ),
( 4 , 200 , 1 ),
( 5 , 200 , 2 ),
( 6 , 300 , 1 ),
( 7 , 300 , 2 )
Select Id,IdProduct
From (
Select Id
,IdProduct
,MaxStatus = max(Status) over (Partition By IdProduct)
From #YourTable
Where Status in (2,3)
) A
Where MaxStatus = 2
Returns
Id IdProduct
5 200
7 300

Linq .GroupBy() with count

I have a table that I need to summarize in a report. This is my sample table.
Orders
_____________________________________
CustomerId | CustomerName | OrderType
___________|______________|__________
1 | Adam | Shoe
1 | Adam | Shoe
1 | Adam | Shoe
1 | Adam | Hat
1 | Adam | Hat
2 | Bill | Shoe
2 | Bill | Hat
3 | Carl | Sock
3 | Carl | Hat
I am trying to summarize this to pass back in my viewmodel without a loop. This is the result that I am attempting to achieve.
CustomerName | Shoe | Hat | Sock | Total Orders
------------ | ---- | --- | ---- | ------------
Adam | 3 | 2 | 0 | 5
Bill | 1 | 1 | 0 | 2
Carl | 0 | 1 | 1 | 2
//var resultList = dbContext.Orders.OrderBy(o => o.CustomerId);
How can I use GroupBy and Count to achieve my desired results? Would that be the best approach to take?
group clause (C# Reference)
var summary = from order in dbContext.Orders
group order by order.CustomerId into g
select new {
CustomerName = g.First().CustomerName ,
Shoe = g.Count(s => s.OrderType == "Shoe"),
Hat = g.Count(s => s.OrderType == "Hat"),
Sock = g.Count(s => s.OrderType == "Sock"),
TotalOrders = g.Count()
};
if items are fixed:
public List<OrderViewModel> GetCustOrders()
{
var query = orders
.GroupBy(c => c.CustomerName)
.Select(o => new OrderViewModel{
CustomerName = o.Key,
Shoe = o.Where(c => c.OrderType == "Shoe").Count(c => c.CustomerId),
Hat = o.Where(c => c.OrderType == "Hat").Count(c => c.CustomerId),
Sock = o.Where(c => c.OrderType == "Sock").Count(c => c.CustomerId),
Total = o.Count(c => c.CustomerId)
});
return query;
}
use SQL is one option, i tested it and get exactly what you want:
select p.*, t.total as 'Total Orders' from
(
select CustomerName, count(CustomerId) total from Orders group by CustomerName
) as t inner join
(
select * from Orders
pivot(count(CustomerId)
for OrderType in ([Shoe], [Hat], [Sock])
) as piv
)as p on p.CustomerName = t.CustomerName

Linq query summary by column with ordering

The scenario is like this
public class Test {
public string name;
public int val1;
public int val1;
}
name |val 1 |val 2|
'aa' | 10 | 4 |
'aa' | 30 | 5 |
'bb' | 14 | 4 |
'bb' | 16 | 6 |
'cc' | 5 | 5 |
'cc' | 2 | 1 |
'cc' | 1 | 1 |
What is the best way group by name and get summary val_1 ans val_2 for every name
as
name |val 1 |val 2|
'aa' | 40 | 9 |
'bb' | 30 | 10 |
'cc' | 8 | 7 |
Try this
var results =
from t in db.Tests
group t by t.name into g
orderby g.Key
select new
{
name = g.Key,
val_1 = g.Sum(x => x.val_1),
val_2 = g.Sum(x => x.val_2)
};
Or if you prefer fluent syntax:
var results = db.Tests.GroupBy(t => t.name)
.OrderBy(g => g.Key)
.Select(g => new
{
name = g.Key,
val_1 = g.Sum(x => x.val_1),
val_2 = g.Sum(x => x.val_2)
});

Categories