I have the following dataset for a TimeTable that needs to be displayed in a gridview. Currently a snippet of the dataset looks like this:
SessionNum TimeStart TimeStop Details
---------- --------- -------- -------
1 08:00 09:00 Math101
1 09:00 10:00 Comp102
1 11:00 12:00 Engn101
2 08:00 09:00 Comp102
2 09:00 10:00 Math101
2 10:00 11:00 Acco103
There are a total of 5 sessions, and I would like for the dataset to look like:
TimeStart TimeStop Session1 Session2 ...
--------- -------- -------- -------- ---
08:00 09:00 Math101 Comp102
09:00 10:00 Comp102 Math101
10:00 11:00 - Acco103
11:00 12:00 Engn101 -
As you will see, there are no aggregate functions required...just grouping, but for the life of me I cannot seem to wrap my head around this one. I have the following LINQ query which generates the first dataset:
List<TimeTable> list = db.Tim开发者_高级运维eTables.OrderBy(o => o.TimeStart).OrderBy(o => o.SessionNum).ToList();
This works fine, and generates the dataset sorted by SessionNum
and then TimeStart
. My attempt to solve this invlovled the following query:
var result = list.GroupBy(t => t.TimeStart).Select(s => new {
TimeStart = s.Key,
Session1 = s.Where(x => x.SessionNum == 1),
Session2 = s.Where(x => x.SessionNum == 2)
});
This ran, but unfortunately did not work. I know a GroupBy
(or a couple) is/are required, but I'm a bit lost from this point forward. I would really appreciate any help towards solving this. Thank you in advance!
You can't directly do a pivot query in LINQ. What you can do instead is create a structure like this:
var record = new
{
TimeStart = "10:00",
TimeStop = "11:00",
Sessions = new [] { "-", "Acco103", },
};
When you have a list of these records you must ensure that the Sessions
property is array that is the same length as the distinct number of sessions in your entire set of data. Then you can access the session information by indexing into the array.
This should make more sense after looking at the queries.
First, query the database for the required data:
var query =
from s in db.TimeTables
orderby s.TimeStop
orderby s.TimeStart
group s by new { s.TimeStart, s.TimeStop } into gss
select new
{
gss.Key.TimeStart,
gss.Key.TimeStop,
Sessions = gss.ToArray(),
};
Now determine the distinct set of sessions:
var sessionNums =
db.TimeTables
.Select(s => s.SessionNum)
.Distinct()
.OrderBy(n => n)
.ToArray();
Now process this data in memory (note the .ToArray()
call on query
):
var process =
from q in query.ToArray()
let lookup = q.Sessions
.ToLookup(s => s.SessionNum, s => s.Details)
select new
{
q.TimeStart,
q.TimeStop,
Sessions = sessionNums
.Select(n => String.Join(
", ",
lookup[n].DefaultIfEmpty("-")))
.ToArray(),
};
This is where the hard work is. The lookup
creates an easy way to get session detail out for any SessionNum
. Calling lookup[n].DefaultIfEmpty("-")
ensures that there is at least a single value for each session. The String.Join
ensures that if the source data had two sessions for the same session number at the same time that we end up with one value.
This result is safe no matter how many sessions there are as it will just extend the arrays.
The output of the process
query looks like this:
Then you can do this query:
var result =
from p in process
select new
{
p.TimeStart,
p.TimeStop,
Session1 = p.Sessions[0],
Session2 = p.Sessions[1],
};
This will effectively "pivot" your results, but you need to explicitly put in each "SessionX" property.
The output of the result
query looks like this:
精彩评论