Transform Linq Data Structure - linq

I am hoping you can help.
I currently have a data structure like so I got after group by (on name year and state) linq aggregation query (this can not be changed)
Name Year State Total
Bobby 2015 East 5
Bobby 2015 West 5
Bobby 2015 North 10
Paul 2015 East 15
How can I transform above structure using linq to below structure.
Name Year East West North Total
Bobby 2015 5 5 10 20
Paul 2015 15 0 0 15
Keeping in mind I am trying to avoid using If statements like state=East ? Add east value : 0 in my linq groupby to get desired structure.
Thanks for help in advance.

Here's one way to do a pivot in Linq:
var pivot = data
.GroupBy(d => new {d.Name, d.Year})
.Select(g => new {
Name = g.Key.Name,
Year = g.Key.Year,
East = g.Where(c => c.State == "East").Sum(c => c.Total),
West = g.Where(c => c.State == "West").Sum(c => c.Total),
North = g.Where(c => c.State == "North").Sum(c => c.Total),
South = g.Where(c => c.State == "South").Sum(c => c.Total),
Total = g.Sum(c => c.Total),
});
You can do more dynamic pivots using DataTables (so you can add columns dynamically) but if your columns are known at design-time this method is cleaner IMHO.

Related

How do you selectively join in tables with linq?

Let's say you have these two tables:
CARS
ID CAR_MODEL
11 Mustang
22 Camaro
33 F-150
PARTS
ID CAR_ID PART_NAME
1 11 Steering Wheel
2 22 Steering Wheel
3 22 Headlights
You need to create a search form. How would you you make this work:
var query = db.CARS.include(u => u.PARTS);
if(model.CarPartName != "")
query = query.where(u => u.PARTS.PART_NAME == model.CarPartName); //this doesn't work
return query.ToList();
There is currently a foreign key relationship between PARTS.CAR_ID and CARS.ID but for some reason when I attempt u.PARTS. I can't append on a column name.
Any idea how to make this optional join work?
That wouldn't work that way, because a car can have many parts, thus u.PARTS is returning a collection. There are many solutions to this problem, you can start by Cars collection (as you did), or you can start by PARTS collection. If you started with PARTS collection, it would look like:
var query = db.PARTS.Include(p => p.Car);
if(model.CarPartName != "")
query = query.Where(u => u.PART_NAME == model.CarPartName);
return query.Select(p => p.Car).Distinct().ToList();
With Cars:
var query = db.CARS.include(u => u.PARTS);
if(model.CarPartName != "")
query = query.Where(u => u.PARTS.Any( up => up.PART_NAME == model.CarPartName));
return query.ToList();
Note: I added the first one, just because I wanted to show what is going on.

multiple group by using linq

I need return just 2 lines in my query. One line with a string Today and a number of cases closed today, on my second line I need a string Last Week and a number of cases closed on the last week.
How I group with a range date?
Sum Name
----------- ----------
12 Today
33 Last Weeb
How about this:
var caseCounts = Cases
.Where(c => c.Date == today || (c.Date >= startOfLastWeek && c.Date <= endOfLastWeek))
.GroupBy(c => c.Date == today ? "Today" : "Last Week")
.Select(g => new {
Name = g.Key, Sum = g.Count()
});
You would need to define the 3 dates (today, startOfLastWeek, endOfLastWeek) before hand, but it gives you the results you are after.
GROUP BY YEARWEEK(date) should work. Depending on your dbms, you might be able to use another function, or program your own.
http://www.tutorialspoint.com/sql/sql-date-functions.htm#function_yearweek

Linq order by Area Name with one exception

Hi say I have objects of type Area with Names:
Hokitika
Dunedin
Stewart Island
West Coast
Invercargill
and I want to order them by name I could go:
areas.OrderBy(a => a.Name)
to give me:
Dunedin
Hokitika
Invercargill
Stewart Island
West Coast
which is fine but what say I was wanting to make this to be an ordered list with the exception that the current users location was at the top so if they were in Invercargill the list would be:
Invercargill
Dunedin
Hokitika
Stewart Island
West Coast
Is this possible in Linq?
Sure. You can order by a bool:
areas.OrderByDescending(a => a.Equals(myArea, StringComparison.OrdinalIgnoreCase))
.ThenBy(a => a.Name);
If that doesn't work on your DB, you can try:
areas.Select(a => new { IsMyLocation = a.Equals(myArea, StringComparison.OrdinalIgnoreCase),
Area = a })
.OrderByDescending (a => a.IsMyLocation)
.Select(a => a.Area);
string currentLocation = "Invercatgill"; // for instance
var sortedresult = areas.OrderBy(a => a.Name.Equals(currentLocation) ? 0 : 1)
.ThenBy(a => a.Name);
Just another way to do it using Except
var currentLocation = new [] { user.CurrentLocation };
var otherAreas = areas.Except(currentLocation).OrderBy(x => x.Name)
var finalResult = currentLocation.Concat(otherAreas);

Linq select distinct values with comparison

I have a list of objects. E.g. List<coin> where they contain a string(denom) and int(year).
If the list contains:
"Quarter", 1954
"Quarter", 1990
"Penny", 1925
"Nickel", 1900
"Nickel", 2000
How can I get a resultant list where it contains the unique values with just the most recent year? E.g.:
"Quarter", 1990
"Penny", 1925
"Nickel", 2000
You can do this by grouping by name, then either ordering and taking the first result, or by using something like MaxBy from MoreLINQ:
var query = coins.GroupBy(x => x.Name)
.Select(g => g.MaxBy(x => x.Year));
or
var query = coins.GroupBy(x => x.Name)
.Select(g => g.OrderByDescending(x => x.Year).First());
You can do this using group by like:
var query = from coin in myList
group coin by coin.Name into grouped
select new
{
Name = grouped.Key
Year = grouped.Max(x => x.Year)
};
For another sample like this, check out "max - grouped" in the 101 Linq examples: http://msdn.microsoft.com/en-us/vcsharp/aa336747#maxGrouped
var coins = new Coin[] { ... };
var recentCoins =
from coin in coins
group coin by coin.Denom into g
select new
{
Denom = g.Key,
MostRecentYear = g.Max(c => c.Year)
};

Get the ranking of highest score with earliest timestamp

I have the following records:
name score Date
Billy 32470 12/18/2010 7:26:35 PM
Sally 1100 12/19/2010 12:00:00 AM
Kitty 1111 12/21/2010 12:00:00 AM
Sally 330 12/21/2010 8:23:34 PM
Daisy 32460 12/22/2010 3:10:09 PM
Sally 32460 12/23/2010 4:51:11 PM
Kitty 32440 12/24/2010 12:00:27 PM
Billy 32460 12/24/2010 12:11:36 PM
I want to get the leaderboard of the highest score with earliest time stamp using LINQ.
In this case, the correct one is
rank name
1 Billy
2 Daisy
3 Sally
I use the following query:
var result =
(from s in Submissions
group s by s.name into g
orderby g.Max(q => q.Score) descending,g.Min(q => q.Date) ascending
select new ScoreRecord
{
name = g.Key
Score = g.Max(q => q.Score)
}).Take(3).ToList();
I get the following wrong result:
rank name
1 Billy
2 Sally
3 Daisy
What's the correct linq query in this case?
At the moment you're getting the max score and the min date for a person but not necessarily the same record.
You need to restrict the min date to only look at those records that have the required max score... this isn't the cleanest way to do it but will work (I'm not good at Linq syntax, much prefer lambda)
var result =
(from s in Submissions
group s by s.name into g
orderby g.Max(q => q.Score) descending,
g.Where(i => i.Score == g.Max(q => q.Score)).Min(q => q.Date) ascending
select new ScoreRecord
{
name = g.Key
Score = g.Max(q => q.Score)
}).Take(3).ToList();
try something simple perhaps
var max = Submissions.Max(m => m.score);
var result = Submissions.Where(s => s.score == max).OrderBy(s => s.Date);

Resources