C# linq query from sql server into one table - c#

I have three tables. I would like to used C# linq to turn into one table.
For example:
Schedule:
+------+--------+--------+
| Name | DateId | TaskId |
+------+--------+--------+
| John | 2 | 32 |
| John | 3 | 31 |
| Mary | 1 | 33 |
| Mary | 2 | 31 |
| Tom | 1 | 34 |
| Tom | 2 | 31 |
| Tom | 3 | 33 |
+------+--------+--------+
Date:
+----+------------+
| Id | Date |
+----+------------+
| 1 | Monday |
| 2 | Tuesday |
| 3 | Wednesday |
| 4 | Thursday |
| 5 | Friday |
+----+------------+
Task:
+----+----------+
| Id | Task |
+----+----------+
| 31 | School |
| 32 | Homework |
| 33 | Break |
| 34 | Teaching |
+----+----------+
I would like to have a table like this:
+--------+----------+----------+-----------+----------+
| Person | Monday | Tuesday | Wednesday | Thursday |
+--------+----------+----------+-----------+----------+
| John | | Homework | School | |
| Mary | Break | School | | |
| Tom | Teaching | School | Break | |
+--------+----------+----------+-----------+----------+
I could not think of any good way doing this.
Any suggestion would be helpful
Thanks

Starting from this set of data:
var schedules = new[] { new { Name = "John", DateId = 2, TaskId = 32},
new { Name = "John", DateId = 3, TaskId = 31},
new { Name = "Mary", DateId = 1, TaskId = 33},
new { Name = "Mary", DateId = 2, TaskId = 31},
new { Name = "Tom", DateId = 1, TaskId = 34},
new { Name = "Tom", DateId = 2, TaskId = 31},
new { Name = "Tom", DateId = 3, TaskId = 33}
};
var dates = new[] { new { DateId = 1, Desc = "Monday"},
new { DateId = 2, Desc = "Tuesday"},
new { DateId = 3, Desc = "Wednesday"},
new { DateId = 4, Desc = "Thursday"},
new { DateId = 5, Desc = "Friday"}
};
var tasks = new[] { new { TaskId = 31, Desc = "School"},
new { TaskId = 32, Desc = "Homework"},
new { TaskId = 33, Desc = "Break"},
new { TaskId = 34, Desc = "Teaching"}
};
You can do as follows:
var result = schedules
// First you join the three tables
.Join(dates, s => s.DateId, d => d.DateId, (s, d) => new {s, d})
.Join(tasks, s => s.s.TaskId, t => t.TaskId, (sd, t ) => new { Person = sd.s, Date = sd.d, Task = t })
// Then you Group by the person name
.GroupBy(j => j.Person.Name)
// Finally you compose the final object extracting from the list of task the correct task for the current day
.Select(group => new
{
Person = group.Key,
Monday = group.Where(g => g.Date.DateId == 1).Select(g => g.Task.Desc).FirstOrDefault(),
Tuesday = group.Where(g => g.Date.DateId == 2).Select(g => g.Task.Desc).FirstOrDefault(),
Wednesday = group.Where(g => g.Date.DateId == 3).Select(g => g.Task.Desc).FirstOrDefault(),
Thursday = group.Where(g => g.Date.DateId == 4).Select(g => g.Task.Desc).FirstOrDefault(),
Friday = group.Where(g => g.Date.DateId == 5).Select(g => g.Task.Desc).FirstOrDefault()
})
.ToList();
If you want to select only some days, you can return an object containing a dictionary instead of an object with a property per day.
The dictionary will contain key-value pairs with the key representing the day and the value representing the task.
See the following code:
var filter = new[] {2, 3};
var filteredResult = schedules
.Join(dates, s => s.DateId, d => d.DateId, (s, d) => new{ s, d})
.Join(tasks, s => s.s.TaskId, t => t.TaskId, (sd, t) => new { Person = sd.s, Date = sd.d, Task = t })
.Where(x => filter.Contains(x.Date.DateId))
.GroupBy(x => x.Person.Name)
.Select(group => new
{
Person = group.Key,
TasksByDay = group.ToDictionary(o => o.Date.Desc, o => o.Task.Desc)
})
.ToList();
foreach (var item in filteredResult)
{
System.Console.WriteLine(item.Person);
foreach (var keyvaluepair in item.TasksByDay)
{
System.Console.WriteLine(keyvaluepair.Key + " - " + keyvaluepair.Value);
}
System.Console.WriteLine("---");
}

It is called "transpose".
var persons = new[] { new { name="John", dateId=2,taskId=32},
new { name="John", dateId=3,taskId=31},
new { name="Mary", dateId=1,taskId=33},
new { name="Mary", dateId=2,taskId=31},
new { name="Tom", dateId=1,taskId=34},
new { name="Tom", dateId=2,taskId=31},
new { name="Tom", dateId=3,taskId=33}
};
var dates = new[] { new { dateId=1, desc="Monday"},
new { dateId=2, desc="Tuesday"},
new { dateId=3, desc="Wednesday"},
new { dateId=4, desc="Thursday"},
new { dateId=5, desc="Friday"}
};
var tasks = new[] { new { taskId=31, desc="School"},
new { taskId=32, desc="Homework"},
new { taskId=33, desc="Break"},
new { taskId=34, desc="Teaching"}
};
var qry = from p in (from p in persons
join d in dates on p.dateId equals d.dateId
join t in tasks on (int)p.taskId equals (int)t.taskId
select new { name = p.name, monday = d.dateId == 1 ? t.desc : "", tuesday = d.dateId == 2 ? t.desc : "", wednesday = d.dateId == 3 ? t.desc : "", thursday = d.dateId == 4 ? t.desc : "", friday = d.dateId == 5 ? t.desc : "" })
group p by p.name into q
select new { q.Key, monday=q.Max(a => a.monday),tuesday=q.Max(a => a.tuesday), wednesday = q.Max(a=>a.wednesday), thursday = q.Max(a => a.thursday), friday=q.Max(a => a.friday)};
foreach ( var a in qry.ToList())
{
Console.WriteLine(String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}",a.Key, a.monday, a.tuesday, a.wednesday, a.thursday, a.friday));
}

Related

How to use Linq to create unique Collection which contains a collection

I am retrieving records from a db and creating the following object:
public class RemittanceBatchProcessingModel
{
public string FileId { get; set; }
public string SourceFileName { get; set; }
public string BatchCode { get; set; }
public string BatchType { get; set; }
public decimal PaymentAmount { get; set; }
public string BillingSystemCode { get; set; }
}
Example objects created after db read:
FileId | SourceFileName | BatchCode | BatchType | PaymentAmt |BillingCode
1 | test.file1.txt | 100 | S | 1000.00 | Exc
1 | test.file1.txt | 100 | S | 2000.00 | Exc
1 | test.file1.txt | 200 | N | 500.00 | Adc
2 | test.file2.txt | 300 | S | 1200.00 | Exc
2 | test.file2.txt | 300 | S | 1500.00 | Exc
I want to create an object that has a collection of the unique files which has a collection of each summarized batch within a file. For example,
Collection of Unique Files:
FileId | SourceFileName | BatchCode | BatchType | BatchTotal |RecordCount
1 | test.file1.txt | 100 | S | 3000.00 | 2
1 | test.file1.txt | 200 | N | 500.00 | 1
2 | test.file2.txt | 100 | S | 1700.00 | 2
I am able to create my collection of batches with no issue the problem I'm having is figuring out how to create the collection of unique files with the correct batches within them. I'm attempting this using the following:
private static RemittanceCenterFilesSummaryListModel SummarizeFiles(RemittanceCenterSummaryListModel remittanceCenterSummaryListModel)
{
var summarizedBatches = SummarizeBatches(remittanceCenterSummaryListModel);
var fileResult = remittanceCenterSummaryListModel.RemittanceBatchSummaryRecord.GroupBy(x => new { x.FileId, x.SourceFileName })
.Select(x => new RemitanceCenterFileSummarizedModel()
{
FileId = x.Key.FileId,
SourceFileName = x.Key.SourceFileName,
ScannedBatchCount = x.Count(y => y.BatchType == "S"),
ScannedBatchAmount = x.Where(y => y.BatchType == "S").Sum(y => y.PaymentAmount),
NonScannedBatchCount = x.Count(y => y.BatchType != "S"),
NonScannedBatchAmount = x.Where(y => y.BatchType != "S").Sum(y => y.PaymentAmount),
});
var summaryListModel = CreateSummaryFilesListModel(fileResult);
summaryListModel.Batches = summarizedBatches.RemittanceBatchSummary;
return summaryListModel;
}
private static RemittanceCenterFilesSummaryListModel CreateSummaryFilesListModel(IEnumerable<RemitanceCenterFileSummarizedModel> summaryModels)
{
var summaryModelList = new RemittanceCenterFilesSummaryListModel();
foreach (var summaryFileRec in summaryModels)
{
var summaryModel = new RemitanceCenterFileSummarizedModel
{
FileId = summaryFileRec.FileId.ToString(CultureInfo.InvariantCulture),
SourceFileName = summaryFileRec.SourceFileName.ToString(CultureInfo.InvariantCulture),
ScannedBatchCount = summaryFileRec.ScannedBatchCount,
ScannedBatchAmount = summaryFileRec.ScannedBatchAmount,
NonScannedBatchCount = summaryFileRec.NonScannedBatchCount,
NonScannedBatchAmount = summaryFileRec.NonScannedBatchAmount
};
summaryModelList.RemittanceFilesSummary.Add(summaryModel);
}
return summaryModelList;
}
You can group it on the 4 columns including BatchType and BatchCode as well and pick the Count and sum the Amount like :
var fileResult = remittanceCenterSummaryListModel.RemittanceBatchSummaryRecord
.GroupBy(x => new
{
x.FileId,
x.SourceFileName,
x.BatchType,
x.BatchCode
})
.Select(x => new
{
FileId = x.Key.FileId,
SourceFileName = x.Key.SourceFileName,
BatchType = x.Key.BatchType,
BatchCode = x.Key.BatchCode,
BatchTotal= x.Sum(y=>y.PaymentAmt),
RecordCount = x.Count()
});
I guess you need to GroupBy FileId & BatchType instead of FileName:-
var fileResult = remittanceCenterSummaryListModel.RemittanceBatchSummaryRecord
.GroupBy(x => new { x.FileId, x.BatchType })
.Select(x =>
{
var firstObj = x.FirstOrDefault();
return new RemitanceCenterFileSummarizedModel()
{
FileId = x.Key.FileId,
SourceFileName = firstObj.SourceFileName,
BatchCode = firstObj.BatchCode,
BatchType = x.Key.BatchType,
BatchTotal = x.Sum(z => z.PaymentAmt),
RecordCount = x.Count()
};
});
Considering FileId maps to SourceFileName & BatchCode maps to BatchType you can simply store the first set in a variable like I did in firstObj to get the relevant values which are not grouped. Please check for nulls before accessing relevant properties as it may cause NRE if no set is found.
For pure linq non fluent
var files = new[] {
new { FileId = 1, SourceFileName = "test.file1.txt" , BatchCode = 100 , BatchType = "S", PaymentAmt = 1000.00 , BillingCode = "Exc" },
new { FileId = 1, SourceFileName = "test.file1.txt" , BatchCode = 100 , BatchType = "S", PaymentAmt = 2000.00 , BillingCode = "Exc" },
new { FileId = 1, SourceFileName = "test.file1.txt" , BatchCode = 200 , BatchType = "N", PaymentAmt = 500.00 , BillingCode = "Adc" },
new { FileId = 1, SourceFileName = "test.file2.txt " , BatchCode = 300 , BatchType = "S", PaymentAmt = 1200.00 , BillingCode = "Exc" },
new { FileId = 1, SourceFileName = "test.file2.txt " , BatchCode = 300 , BatchType = "S", PaymentAmt = 1500.00 , BillingCode = "Exc" }
};
var result = from file in files
group file by new { file.FileId, file.BatchCode } into fileBachGroups
select new
{
FileId = 1,
SourceFileName = fileBachGroups.First().SourceFileName,
BatchCode = fileBachGroups.Key.BatchCode,
BatchType = fileBachGroups.First().BatchType,
BatchTotal = fileBachGroups.Sum(f => f.PaymentAmt),
RecordCount = fileBachGroups.Count()
};
Console.WriteLine("FileId | SourceFileName | BatchCode | BatchType | BatchTotal |RecordCount");
foreach (var item in result)
{
Console.WriteLine("{0} | {1} | {2} | {3} | {4} | {5}",item.FileId,item.SourceFileName, item.BatchCode, item.BatchType, item.BatchTotal, item.RecordCount);
}

Select all columns but group by only one in linq

I have been looking for a way to get multiple columns but group by only one in SQL and I found some info. However I can not came up with a way to do it in linq.
I have the following toy example table:
| Id | Message | GroupId | Date |
|-------------------------------|
| 1 | Hello | 1 | 1:00 |
| 2 | Hello | 1 | 1:01 |
| 3 | Hey | 2 | 2:00 |
| 4 | Dude | 3 | 3:00 |
| 5 | Dude | 3 | 3:01 |
And I would like to recover all columns for the rows that have a distinct GroupId as follows (with a 'Date' desc order):
| Id | Message | GroupId | Date |
|-------------------------------|
| 1 | Hello | 1 | 1:00 |
| 3 | Hey | 2 | 2:00 |
| 4 | Dude | 3 | 3:00 |
I do not really care about which row is picked from the grouped ones (first, second...) as long as is the only one given that group Id.
I have came out with the following code so far but it does not do what is supposed to:
List<XXX> messages = <MyRep>.Get(<MyWhere>)
.GroupBy(x => x.GroupId)
.Select(grp => grp.OrderBy(x => x.Date))
.OrderBy(y => y.First().Date)
.SelectMany(y => y).ToList();
This will give you one item per group:
List<dynamic> data = new List<dynamic>
{
new {ID = 1, Message = "Hello", GroupId = 1, Date = DateTime.Now},
new {ID = 2, Message = "Hello", GroupId = 1, Date = DateTime.Now},
new {ID = 3, Message = "Hey", GroupId = 2, Date = DateTime.Now},
new {ID = 4, Message = "Dude", GroupId = 3, Date = DateTime.Now},
new {ID = 5, Message = "Dude", GroupId = 3, Date = DateTime.Now},
};
var result = data.GroupBy(item => item.GroupId)
.Select(grouping => grouping.FirstOrDefault())
.OrderByDescending(item => item.Date)
.ToList();
//Or you can also do like this:
var result = data.GroupBy(item => item.GroupId)
.SelectMany(grouping => grouping.Take(1))
.OrderByDescending(item => item.Date)
.ToList();
If you want to control OrderBy then:
var result = data.GroupBy(item => item.GroupId)
.SelectMany(grouping => grouping.OrderBy(item => item.Date).Take(1))
.OrderByDescending(item => item.Date)
.ToList();

Combine tables using row values as column LINQ C# SQL

I have a users table:
Id | Name | Age
--------------------
1 | Steve | 21
2 | Jack | 17
3 | Alice | 25
4 | Harry | 14
I also have a table containing additional user info:
UId | Key | Value
----------------------
1 | Height | 70
2 | Height | 65
2 | Eyes | Blue
4 | Height | 51
3 | Hair | Brown
1 | Eyes | Green
The UId column links to the Id column in the users table. As you can see, not all users have the same additional info present. Alice doesn't have a height value, Jack is the only one with an eye color value etc.
Is there a way to combine this data into one table dynamically using C# and LINQ queries so that the result is something like this:
Id | Name | Age | Height | Eyes | Hair
------------------------------------------
1 | Steve | 21 | 70 | Green |
2 | Jack | 17 | 65 | Blue |
3 | Alice | 25 | | | Brown
4 | Harry | 14 | 51 |
If a user does not have a value for the column, it can remain empty/null. Does this require some sort of data pivoting?
For the case, your user info fields are constant:
var result = users.GroupJoin(details,
user => user.Id,
detail => detail.Id,
(user, detail) => new
{
user.Id,
user.Name,
user.Age,
Height = detail.SingleOrDefault(x => x.Key == "Height").Value,
Eyes = detail.SingleOrDefault(x => x.Key == "Eyes").Value,
Hair = detail.SingleOrDefault(x => x.Key == "Hair").Value,
});
You can do it by utilising GroupJoin, example:
var users = new List<Tuple<int, string, int>> {
Tuple.Create(1, "Steve", 21),
Tuple.Create(2, "Jack", 17),
Tuple.Create(3, "Alice", 25),
Tuple.Create(4, "Harry", 14)
};
var userInfos = new List<Tuple<int, string, string>> {
Tuple.Create(1, "Height", "70"),
Tuple.Create(2, "Height", "65"),
Tuple.Create(2, "Eyes", "Blue"),
Tuple.Create(4, "Height", "51"),
Tuple.Create(3, "Hair", "Brown"),
Tuple.Create(1, "Eyes", "Green"),
};
var query = users.GroupJoin(userInfos,
u => u.Item1,
ui => ui.Item1,
(u, infos) => new { User = u, Infos = infos });
var result = query.Select(qi => new
{
Id = qi.User.Item1,
Name = qi.User.Item2,
Age = qi.User.Item3,
Height = qi.Infos.Where(i => i.Item2 == "Height").Select(i => i.Item3).SingleOrDefault(),
Eyes = qi.Infos.Where(i => i.Item2 == "Eyes").Select(i => i.Item3).SingleOrDefault(),
Hair = qi.Infos.Where(i => i.Item2 == "Hair").Select(i => i.Item3).SingleOrDefault()
});
First of all I have grouped the user details data using Feature (I have renamed the Key property with Feature to avoid confusion) & UId then I have used group join to combine both results using into g. Finally retrieved the result using specified feature.
var result = from user in users
join detail in details.GroupBy(x => new { x.UId, x.Feature })
on user.Id equals detail.Key.UId into g
select new
{
Id = user.Id,
Name = user.Name,
Age = user.Age,
Height = g.FirstOrDefault(z => z.Key.Feature == "Height") != null ?
g.First(z => z.Key.Feature == "Height").First().Value : String.Empty,
Eyes = g.FirstOrDefault(z => z.Key.Feature == "Eyes") != null ?
g.First(z => z.Key.Feature == "Eyes").First().Value : String.Empty,
Hair = g.FirstOrDefault(z => z.Key.Feature == "Hair") != null ?
g.First(z => z.Key.Feature == "Hair").First().Value : String.Empty,
};
I am getting following output:-
Here is the complete Working Fiddle.
Try this
var list = (from u in context.users
join ud in context.UserDetails on u.Id equals ud.UId
select new
{
u.Id,
u.Name,
u.Age,
ud.Key,
ud.Value
});
var finallist = list.GroupBy(x => new { x.Id, x.Name,x.Age}).Select(x => new
{
x.Key.Id,
x.Key.Name,
x.Key.Age,
Height = x.Where(y => y.Key == "Height").Select(y => y.Value).FirstOrDefault(),
Eyes = x.Where(y => y.Key == "Eyes").Select(y => y.Value).FirstOrDefault(),
Hair = x.Where(y => y.Key == "Hair").Select(y => y.Value).FirstOrDefault()
}).ToList();
try this query
var objlist=( form a in contex.user
join b in contex.UserDetails on a.id equals a.Uid into gj
from subpet in gj.DefaultIfEmpty()
select new { Id=a.id, Name=a.name, Age=a.age, Height =subpet.Height,Eyes=subpet.Eyes, Hair=subpet.Hair}).ToList();

Complex SQL JOIN Query, min, max and date range

I have the following tables:
Readings:
+----+---------------------+-------+----------+
| Id | TimestampLocal | Value | Meter_Id |
+----+---------------------+-------+----------+
| 1 | 2014-08-22 18:05:03 | 50.5 | 1 |
| 2 | 2013-08-12 14:02:09 | 30.2 | 1 |
+----+---------------------+-------+----------+
Meters:
+----+--------+
| Id | Number |
+----+--------+
| 1 | 32223 |
+----+--------+
I need to select 2 readings for each meter, the reading with max DateTime and the reading with min DateTime, in addition to the difference between values of the two readings, something like this:
+----------+------------+----------------+------------+----------------+------------+
| Meter_Id | MaxReading | MaxReadingTime | MinReading | MinReadingTime | Difference |
+----------+------------+----------------+------------+----------------+------------+
I need a single query to achieve this for all meters within a date range in Entity Framework
i was able to get this far (get max and min readings):
SELECT
tt.*
FROM Readings tt
INNER JOIN
(
SELECT
Meter_Id,
MAX(TimeStampLocal) AS MaxDateTime,
MIN(TimeStampLocal) AS MinDateTime
FROM Readings
where TimeStampLocal > '2014-12-08'
GROUP BY Meter_Id
) AS groupedtt
ON (tt.Meter_Id = groupedtt.Meter_Id) AND
(tt.TimeStampLocal = groupedtt.MaxDateTime or tt.TimeStampLocal = groupedtt.MinDateTime)
order by Meter_Id;
Using this mockup of your actual schema and data:
class Reading
{
public int Id { get; set; }
public DateTime TimestampLocal { get; set; }
public double Value { get; set; }
public int Meter_Id { get; set; }
}
List<Reading> Readings = new List<Reading>()
{
new Reading { Id = 1, TimestampLocal = new DateTime(2014, 8, 22), Value = 50.5, Meter_Id = 1 },
new Reading { Id = 2, TimestampLocal = new DateTime(2013, 8, 12), Value = 30.2, Meter_Id = 1 },
new Reading { Id = 3, TimestampLocal = new DateTime(2013, 9, 12), Value = 35.2, Meter_Id = 1 }
};
using this linq query:
var q = from r in Readings
group r by r.Meter_Id into rGroup
select new
{
Meter_Id = rGroup.Key,
MaxReading = rGroup.OrderByDescending(x => x.TimestampLocal).First().Id,
MaxReadingTime = rGroup.OrderByDescending(x => x.TimestampLocal).First().TimestampLocal,
MinReading = rGroup.OrderBy(x => x.TimestampLocal).First().Id,
MinReadingTime = rGroup.OrderBy(x => x.TimestampLocal).First().TimestampLocal,
Difference = rGroup.OrderByDescending(x => x.TimestampLocal).First().Value -
rGroup.OrderBy(x => x.TimestampLocal).First().Value
};
produces this output:
[0] = { Meter_Id = 1, MaxReading = 1, MaxReadingTime = {22/8/2014 12:00:00 πμ},
MinReading = 2, MinReadingTime = {12/8/2013 12:00:00 πμ}, Difference = 20.3 }
which should be close to expected result.
EDIT:
You can considerably simplify the above linq query by making use of the let clause:
var q = from r in Readings
group r by r.Meter_Id into rGroup
let MaxReading = rGroup.OrderByDescending(x => x.TimestampLocal).First()
let MinReading = rGroup.OrderBy(x => x.TimestampLocal).First()
select new
{
Meter_Id = rGroup.Key,
MaxReading = MaxReading.Id,
MaxReadingTime = MaxReading.TimestampLocal,
MinReading = MinReading.Id,
MinReadingTime = MinReading.TimestampLocal,
Difference = MaxReading.Value - MinReading.Value
};
Probably not the most efficient, I admit, but that's the quickest I could come up with something without counter-verifying it myself.
SELECT m.Id AS Meter_Id, MaxReading, MaxReadingTime, MinReading, MinReadingTime, (MaxReading - MinReading) AS Difference
FROM Meters m
CROSS APPLY (SELECT MIN(Value) MinReading, TimestampLocal AS MinReadingTime FROM Readings WHERE Meter_Id = m.Id) min
CROSS APPLY (SELECT MAX(Value) MaxReading, TimestampLocal AS MaxReadingTime FROM Readings WHERE Meter_Id = m.Id) max
edit: formatting.

Combining items in a given datatable column using LINQ

I have a datatable which looks like this:
Id | Title | Month | Year |
ebdef240-abb7-4a82-9229-1ed37496da86 | Maths FT | 1 | 2013 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2013 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2014 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2015 |
ebdef239-abb7-4a82-9229-1ed37496da86 | English PT | 1 | 2013 |
ebdef239-abb7-4a82-9229-1ed37496da86 | English PT | 1 | 2014 |
but I would like it to be arranged like this:
Id | Title | Month | Years |
ebdef240-abb7-4a82-9229-1ed37496da86 | Maths FT | 1 | 2013 |
57504a66-4882-4794-a8b9-af0ead38dc70 | Maths FT | 2 | 2013, 2014, 2015 |
ebdef239-abb7-4a82-9229-1ed37496da86 | English PT | 1 | 2013, 2014 |
It maybe that it would make more sense to represent this as a list. I made an attempt at doing this, but am confused as to a) how I can combine the Years (as above, and b) include non-grouped fields, such as the ID (there are others, this is just a few of the columns for simplicity):
From LINQPad:
var objectTable = new DataTable();
objectTable.Columns.Add("Title",typeof(string));
objectTable.Columns.Add("id",typeof(Guid));
objectTable.Columns.Add("Month",typeof(int));
objectTable.Columns.Add("Year",typeof(string));
objectTable.Rows.Add("Maths FT", "ebdef240-abb7-4a82-9229-1ed37496da86", 1, "2013");
objectTable.Rows.Add("Maths FT", "57504a66-4882-4794-a8b9-af0ead38dc70", 2, "2013");
objectTable.Rows.Add("Maths FT", "57504a66-4882-4794-a8b9-af0ead38dc70", 2, "2014");
objectTable.Rows.Add("Maths FT", "57504a66-4882-4794-a8b9-af0ead38dc70", 2, "2015");
objectTable.Rows.Add("English PT", "ebdef239-abb7-4a82-9229-1ed37496da86", 1, "2013");
objectTable.Rows.Add("English PT", "ebdef239-abb7-4a82-9229-1ed37496da86", 1, "2014");
var DataSort = from row in objectTable.AsEnumerable()
group row by new {title = row.Field<string>("Title"), month = row.Field<int>("Month")} into grp
select new
{
Title = grp.Key.title,
Month = grp.Key.month,
};
DataSort.Dump();
Any examples would greatly appreciated.
Thanks.
Perhaps:
var result = objectTable.AsEnumerable()
.Select(r => new { Row = r, Title = r.Field<string>("Title"), Month = r.Field<int>("Month") })
.GroupBy(x => new { x.Title, x.Month })
.Select( g => new {
id = g.First().Row.Field<Guid>("id"),
g.Key.Title,
g.Key.Month,
Year = g.Select(x => x.Row.Field<string>("Year")).ToList()
});
If you want a string with a comma separated list instead of the List<string> for the year-group use Year = string.Join(",", g.Select(x => x.Row.Field<string>("Year"))).
By the way, why is year a string instead of an int?
This will be the LINQ statement for your output
from o in objectTable
group o by new { o.Id, o.Month, o.Title } into g
select new {Id = g.Key.Id, Title = g.Key.Id, Month = g.Key.Month, Years= String.Join(" ", g.Select(x=>x.Year).ToArray()) };

Categories

Resources