I have a query that's returning some grouped data, and I'm drawing a blank on how to "flatten" it so that top level information is available at each group.
Account A
- Group A
- Group B
- Group C
- etc
Account B
- Group B
- Group C
What I want to do is "flatten" this to:
Account A | Group A | Sum(Group)
Account A | Group B | Sum(Group)
Account A | Group C | Sum(Group)
Account B | Group B | Sum(Group)
Account B | Group C | Sum(Group)
Here's what I have so far, but I'm not totally sure where to go
var transactions = Accounts.Where(clause)
.Select(x => new
{
Account = x,
TransGroupedByBucket = x.TransactionLogs.GroupBy(tl => tl.TransactionType.BucketTypeID)
})
//pseudocode
//Select new {
// Account,
// One_TransactionLog_Group
//}
//will run for each group:
.Select(x => new
{
AccountInfo = x.Account.ID
Sum = One_TransactionLog_Group.Sum(tl => tl.Amount)
})
;
I think I'm just having a brain fart, can someone point me in the right direction? One idea I had was to flip it to start at the TransactionLog level and traverse to the Account, but there might be 0 TransactionLogs for an Account, so that might not work properly.
You could use SelectMany:
Accounts.SelectMany(x => x.TransactionLogs.
Select(y => new { Account = x.ID,
TransactionLog = y,
Sum = ... });
Related
Im trying to do an loop in my MVC5 app controller to get all timeline post and if Picture was uploaded in the post display the picture(s).
The code below get all the post and images but if i uploaded 3 pictures same post it will be looped out 3 times.
Is this not the correct way to go?
var query = (from i in db.Timeline
join u in db.Users on i.UserId equals u.Id
join f in db.UserFiles on i.Id equals f.TimelineId into ps
from f in ps.DefaultIfEmpty()
orderby i.PostDate descending
select new { i.Id, i.UserId, i.Post, i.PostDate, u.FirstName, u.ProfilePic, FileName = f == null ? "No image(s)" : f.FileName + "_thumb." + f.FileExtension }).ToList();
List<TimelineLoop> cModel = new List<TimelineLoop>();
foreach (var item in query)
{
cModel.Add(new TimelineLoop
{
Id = item.Id,
UserId = item.UserId,
Post = item.Post,
PostDate = item.PostDate,
Name = item.FirstName,
ProfilePic = item.ProfilePic,
FileName = item.FileName
});
}
return cModel;
If I look at this particular part of your query, it would produce:
from i in db.Timeline
join u in db.Users on i.UserId equals u.Id
timelinePost1 | user1
timelinePost2 | user1
timelinePost3 | user2
=> 1 user can have more than 1 timeline post.
After joining with the query below, the result in 1 will be joined to UserFiles. I assume that one timeline post can have many pictures. If you upload 3 pictures to the same post, you would expect something like this:
join f in db.UserFiles on i.Id equals f.TimelineId into ps
from f in ps.DefaultIfEmpty()
It produces:
timelinePost1 | user1 | photo1
timelinePost1 | user1 | photo2
timelinePost1 | user1 | photo3
timelinePost2 | user1 |
timelinePost3 | user2 |
Do you get this pattern in the output?
EDIT:
if you want to have 1 object per post in your result, you may need to convert the query list into dictionary, which can be done similar to query below.
List<MyObject> list = ...;
var map = list
.GroupBy(x => x.KeyedProperty)
.ToDictionary(x => x.Key, x => x.ToList());
Reference: LINQ - Convert List to Dictionary with Value as List
My database has a sales table with entries like so:
_____________________________________
| id | title_id | qty |
-------------------------------------
| 0 | 6 | 10 |
-------------------------------------
| 1 | 5 | 5 |
-------------------------------------
| 2 | 6 | 2 |
-------------------------------------
Title_id is Foreign key pointing to Titles table which is as follows:
_____________________________________
| id | title_id | title |
-------------------------------------
| 0 | 5 | Soda |
-------------------------------------
| 1 | 6 | Coffee |
-------------------------------------
I want to find top 5 sold products wich means i need to calculate the qty value for each product for all it's entried in sales table then order the result by qty in descending order and limit the select to 5.
However I'm new to C# ASP.NET and somewhat new to SQL. I dont know how to do this with LINQ.
This is my code so far:
var getIds = (from sale in db.sales
join tit in db.titles on sale.title_id equals tit.title_id
group sale by sale.qty into result
orderby result.Sum(i => i.qty) descending
select new Publication
{
PubID = sales.title_id, Title = tit.title
}
).Take(5);
Assuming you have a navigation property Sale.Title, something like this should do:
var tops =
db.Sales
.GroupBy( o => o.Title )
.Select( o => new { Title = o.Key, Sum = o.Sum( x => x.Quantity ) } )
.OrderByDescending( o => o.Sum )
.Take( 5 )
.ToList();
tops is then a list of an anonymous type with two properties: the Title object and the sum of the quantities.
You can then get the values like this:
foreach( var top in tops )
{
int titleId = top.Title.title_id;
string title = top.Title.title;
int sumOfQuantities = top.Sum;
...
If you just want the top Title objects, can can select them like this:
List<Title> topTitles = tops.Select( o => o.Title ).ToList();
var result= (from p in sales
let k = new
{
Name = p.Name
}
group p by k into t
orderby Name descending
select new
{
Name = t.Name,
Qty = t.Sum(p => p.Qty)
}).Take(5);
If the entries in the Sales table are more than one per item (ie: in your example you have 'Soda' 10 + 'Soda' 2, then you need to GroupBy(), using the name as the key (or it's related id if it's in another table), but not the qty.
var topSales = db.sales.GroupBy(x => x.title)
.Select(g => new
{
Title = g.Key,
Qty = g.Sum(x => x.qty)
})
.OrderByDescending(x => x.Qty)
.Select(x => new Publication
{
PubID = x.Title.title_id,
Title = x.Title.title1
})
.Take(5)
.ToList();
Note that I've omitted the join statement assuming that you have a foreign key between sales.title_id -> title.id, and you are using LINQ to SQL. Also note that I've avoided using the query syntax in favor of the chained method syntax, I think it's much clear in this use case (although not always true, ie: cross-joins).
Also, SQL and LINQ have some similarities but don't let the names of clauses/methods fool you, LINQ is not SQL, IMHO, Microsoft just tried to make people comfortable by making it look similar ;)
EDIT: fixed GroupBy()
var result= (from p in sales
let k = new
{
Name = p.Name
}
group p by k into t
select new
{
Name = t.Name,
Qty = t.Sum(p => p.Qty)
}).OrderByDescending(i => i.Qty).Take(5);
You need to look at GroupBy; this will give you what you need
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
I've currently got this sample table of data:
ID | Policy ID | History ID | Policy name
1 | 1 | 0 | Test
2 | 1 | 1 | Test
3 | 2 | 0 | Test1
4 | 2 | 1 | Test1
Out of this, I want to group by the Policy ID and History ID (MAX), so the records I want to be kept are ID's 2 and 4:
ID | Policy ID | History ID | Policy name
2 | 1 | 1 | Test
4 | 2 | 1 | Test1
I've tried to do this in LINQ and stumbling on the same issue every time. I can group my entities, but always into a group where I have to re-define the properties, rather than have them kept from my Policy objects. Such as:
var policies = _context.Policies.GroupBy(a => a.intPolicyId)
.Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
});
This simply just brings out a list of objects which have "Policy ID" and "History ID" within them. I want all the properties returned from the Policies object, without having to redefine them all, as there are around 50+ properties in this object.
I tried:
var policies = _context.Policies.GroupBy(a => a.intPolicyId)
.Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
PolicyObject = group;
});
But this errors out.
Any ideas?
Group by composite key
_context.Policies.GroupBy(a => new {a.intPolicyId, *other fields*}).Select(
group=> new {
PolicyId = group.Key.intPolicyId,
HistoryId = group.Max(intHistoryId),
*other fields*
}
);
Another way - grab histories, than join back with the rest of the data, something like this (won't work out of the box, will require some refining)
var historyIDs = _context.Policies.GroupBy(a=>a.intPolicyId).Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
});
var finalData = from h in historyIDs
join p in _context.Policies on h.intPolicyId equals p.intPolicyId
select new {h.HistoryId, *all other policy fields*}
And yet another way, even simpler and not require a lot of typing :):
var historyIDs = _context.Policies.GroupBy(a=>a.intPolicyId).Select(group => new {
PolicyID = group.Key,
HistoryID = group.Max(a => a.intHistoryID)
});
var finalData = from h in historyIDs
join p in _context.Policies on h.PolicyId equals p.intPolicyId && h.HistoryId equals p.HistoryId
select p
Basically it's somewhat equivalent to the following SQL query:
select p.*
from Policy p
inner join (
select pi.policyId, max(pi.historyId)
from Policy pi
group by pi.policyId
) pp on pp.policyId = p.policyId and pp.historyId = p.historyId
In LINQ to Objects, I'd do this as
var policies = _context.Policies
.GroupBy(a => a.intPolicyId)
.Select(g => g.OrderByDescending(p => p.intHistoryID).First());
but your _context impleis there might be a database involved and I'm not 100% sure this will translate.
Basically it groups by the policy ID as you'd expect, then within each group orders by history ID and from each group selects the row with the highest history ID. It returns exactly the same type as is found in Policies.
I am using LINQ to SQL in a project and I am having an issue doing both a join and a group by to do a comparison between the two fields that are in each table.
Here is what my query looks like:
var q =
(from ii in
(from a in table1
join b in table2 on a.BudgetUnitID equals b.BudgetUnitID
select new { BT = a.Amount, BA = b.Amount, BUID = a.BudgetUnitID, BU = a.BudgetUnit.BudgetUnitName })
group ii by new {ii.BUID} into g
select new
{
BudgetUnit = g.Key,
Budget = g.Sum(x => x.BA),
Actual = g.Sum(x => x.BT),
Variance = g.Sum(x => x.BA) - g.Sum(x => x.BT)
}).ToList();
I am am going to bind this to a grid view on a web page. My problem is that I am not getting the totals correct on one of the columns.
Your help is much appreciated.
Your problem is likely that the Budget amount is incorrect, because you are summing them for every combination. For example, if you had budget data looking like this:
Budget | Amount
Dev | 2500
And line items like this:
Budget | Amount
Dev | 1000
Dev | 750
Then you are combining them together in your inner query like this:
BA | BT
2500 | 1000
2500 | 750
And then summing them together, which gives you
Budget | Actual
5000 | 1750
In this case, the budget is twice as large as it should be, because you are adding all the entries together per combination. You want to sum the individual tables first, before joining the results together so that you don't have this "duplication" issue in your sub-query data:
var q =
(from ii in
(from a in table1 group a by a.BudgetUnitID into g
select new { BudgetUnitID = g.Key, Amount = g.Sum(x => x.Amount) })
join jj in
(from b in table2 group b by b.BudgetUnitID into g
select new { BudgetUnitID = g.Key, Amount = g.Sum(x => x.Amount) })
on ii.BudgetUnitID equals jj.BudgetUnitID
select new { Actual = ii.Amount, Budget = jj.Amount, Variance = jj.Amount - ii.Amount, BUID = ii.BudgetUnitID }
).ToList();
I have the following model:
Schools with Many Majors
Majors with Many Offered Degrees (or just Degrees for Short).
+------+--------+--------+
|School| Major | Degree |
+------+--------+--------+
| UCLA |CompSci | B |
| UCLA |CompSci | M |
| UCLA |CompSci | D |
| UCLA |Math | B |
+------+--------+--------+
I'd like to retrieve all the degrees offered by a school, grouped by Majors (so majors is not repeated for each degree returned). How might I do that? I have the following code so far, but now I'm stuck.
var query = from school in schools
where school.Id == Id
select new
{
name = s.Name
majors = (from major in school.Majors
select new
{
majorname = major.Name
}).Distinct()
};
I'm not quite sure I know how to return the degrees for each distinct major.
I was able to solve this by checking out similar situations on SO and by using the group/by/into keywords.
var query = from school in schools
where school.Id == id
select new
{
name = school.Name,
majors = ( from major in school.Majors
group major.Degree by major.Name into sub
select new
{
m = sub.Key,
d = (from degree in sub
select degree.Name)
} )
};
Thanks so much everyone.
What about the following?
var query = schools
.Where(school => school.Id == Id)
.Select(school => new
{
Name = school.Name,
Majors = school.Majors.Select(major => major.Name).Distinct()
})
.GroupBy(obj => obj.Majors);
The only change to your code, other than desugaring the query syntax, is to change the Majors field to an IEnumerable<string> and to add a GroupBy call.
Simply make a Group By
var groupedBy= list.Where(c=> c.Id==Id).GroupBy(c=> c.Major);
foreach(var item in groupedBy )
{
var v=item.Select(c=> new {Major=item.Key,Degree=c.Degree });
}