This question already has answers here:
Is it possible to Pivot data using LINQ?
(7 answers)
Closed 5 years ago.
I have a list of items with a property code and I want to group by eventId and create a table representation where code values are transformed to columns.
Every triple (event, code,amount) is unique.
I want to transform this
eventId code amount
1 A 100
1 B 101
1 C 102
2 A 103
2 C 104
3 B 105
....
to this
eventId A B C
1 100 101 102
2 103 0 104
3 0 105 0
...
var table=from x in list
group x by x.eventId into gr
select new
{
eventId=gr.Key,
....
}
You need to filter on grouped result and project in to anonymous object :
var table=from x in list
group x by x.eventId into gr
select new
{
eventId=gr.Key,
A = gr.Where(x=>x.code == "A").Sum(x=>x.amount),
B = gr.Where(x=>x.code == "B").Sum(x=>x.amount),
C = gr.Where(x=>x.code == "C").Sum(x=>x.amount)
}
It's actually quite neat to use a lookup:
var table =
from x in list
group x by x.eventId into gr
let lookup = gr.ToLookup(y => y.code, y => y.amount)
select new
{
eventId = gr.Key,
A = lookup["A"].Sum(),
B = lookup["B"].Sum(),
C = lookup["C"].Sum(),
};
I get this result:
Related
I need to group this data list to new collection list by Date and UserId
Id
Date
TypeId
Value
UserId
1
2021-10-19
1
12
5
2
2021-10-19
2
15
5
3
2021-10-20
1
20
20
4
2021-10-20
2
11
20
5
2021-10-20
3
14
20
6
2021-10-20
1
19
18
7
2021-10-20
4
15
20
8
2021-10-20
1
18
19
new collection list in C# Lambda
Date
UserId
TypeId1
TypeId2
TypeId3
TypeId4
2021-10-19
5
12
15
2021-10-20
18
19
2021-10-20
19
18
2021-10-20
20
20
11
14
15
The first me code : var groupTranDate = db.System_Transactions.GroupBy(g => new { g.Transaction_Date,g.User_Id }).ToList();
true result, but i can't get the data to new collection
Please click here to view result image for my code
You must understand, GroupBy return IEnumerable<IGrouping<TKey, TSource>>, therefore you want cast IGrouping to IEnumerable
var groupTranDate = db.System_Transactions
.GroupBy(g => new MyGroupedClass
{
UserId = g.UserId,
Transaction_Date = g.Transaction_Date
)})
.Select(x => x.Where(x => x))
.Select(x => x);
How to get a new collection:
List<MyGroupedClass> ts = new List<MyGroupedClass>();
foreach(var item in groupTranDate)
{
ts.AddRange(item);
}
Seems reasonable, I might just go a step further and turn the resulting list of the group into a class designed for the job:
record X(
DateTime Date,
int UserId,
int? TypeId1,
int? TypeId2,
int? TypeId3,
int? TypeId4
){
public X(IGrouping<(DateTime T,int U),System_Transaction> y):this(y.Key.T, y.Key.U, null, null, null, null){
foreach(var x in y){
_ = x.Type_Id switch {
1 => TypeId1 = x.Value,
2 => TypeId2 = x.Value,
3 => TypeId3 = x.Value,
4 => TypeId4 = x.Value,
_ => throw new NotImplementedException($"Type Id {x.Type_Id} is not handled")
};
}
}
Then make it in your grouping
var groupTranDate = db.System_Transactions
.GroupBy(g => (g.Transaction_Date,g.User_Id))
.Select(g => new X(g))
.ToList();
The record is like a class, it's just simpler to write out; it has a constructor that can convert the grouping generated by GroupBy; basically the constructor first calls the other constructor (that you can't see because C# generates it behind the scenes) passing the transaction date and the user is- that gets those properties set. Next the constructor loops over the grouping (which behaves like a list of YourClass, whatever your class is called - please change the type name to your true class name) and if the Type_Id is 1 then property TypeId1 ends up assigned the Value etc
At the end of the loop, however many items there were (between 1 and 4) you'll have the TypeIdX properties set to the values (or remain null if there was no value)
The only adjustment needed to the LINQ query is to change the grouping from an anonymous type to a tuple, because ATs are a pain to work with outside of the context they're created in, and to use a Select to convert the grouping to our record X
If you want that record in something more familiar/a class that works in an older .NET, it would look like:
class X(
public DateTime Date {get;set;},
public int UserId {get;set;},
public int? TypeId1 {get;set;},
public int? TypeId2 {get;set;},
public int? TypeId3 {get;set;},
public int? TypeId4 {get;set;}
){
public X(IGrouping<(DateTime T,int U),System_Transaction> y){
Date = T;
UserId = U;
foreach(var x in y){
if(x.Type_Id == 1) TypeId1 = x.Value;
else if(x.Type_Id == 2) TypeId2 = x.Value;
else if(x.Type_Id == 3) TypeId3 = x.Value;
else if(x.Type_Id == 4) TypeId4 = x.Value;
else throw new NotImplementedException($"Type Id {x.Type_Id} is not handled");
}
}
There are ways to get cute with dictionaries etc to avoid having to write out a huge if/switch block in a loop like there is there, but you still have to write out the properties, unless you're planning on making the table a display-only thing that can be driven by a dictionary with N keys, rather than an object you refer to in code as some X with a property TypeId4..
I am trying to build a LINQ Statement for Joining the below two list in C#.
List1:
FormID FormRound
2 1
2 2
2 3
3 1
4 2
List2:
FormID FormRound Category Date
2 1 Test1 23-Aug
2 1 Test2 24-Aug
2 1 Test3 25-Aug
2 2 Test1 26-Aug
2 2 Test3 27-Aug
3 1 Test1 28-Aug
3 1 Test2 29-Aug
3 1 Test3 30-Aug
I should get the Output as below.
FormID FormRound Test1Date Test2Date Test3Date
2 1 23-Aug 24-Aug Test3
2 2 26-Aug NA 27-Aug
3 3 28-Aug 29-Aug NA
Can anyone please help me in Framing the LINQ Statement?
This LINQ query might look like:
var results = (from a in list1
join b in list2 on new { a.FormId, a.FormRound } equals new { b.FormId, b.FormRound }
group b by new { a.FormId, a.FormRound } into c
select new
{
c.Key.FormId,
c.Key.FormRound,
Test1Date = c.Where(d => d.Category == "Test1").Select(e => e.Date).FirstOrDefault(),
Test2Date = c.Where(d => d.Category == "Test2").Select(e => e.Date).FirstOrDefault(),
Test3Date = c.Where(d => d.Category == "Test3").Select(e => e.Date).FirstOrDefault(),
});
Which, using your sample data, produces:
2 1 23-Aug 24-Aug 25-Aug
2 2 26-Aug null 27-Aug
3 1 28-Aug 29-Aug 30-Aug
This is not exactly your expected results, but I think you have some issues with what you're expecting based on my comments on your question.
This question already has answers here:
LINQ: Distinct values
(8 answers)
Closed 8 years ago.
I have following query:
var query = from product in SH_Products
from product_group in SH_ProductGroups_Products.Where(c=>c.ProductID == product.ID)
from manufacturer in SH_Manufacturers.Where(c=>c.ID == product.ManufactureID)
from attributeOption_product in SH_AttributeOptions_Products.Where(c=>c.ProductID == product.ID).DefaultIfEmpty()
where !product.IsDeleted
&& !product_group.IsDeleted
&& !manufacturer.IsDeleted
&& product.Status != 0
select new
{
product.ID,
ProductGroupID = product_group.ProductGroupID,
AttributeOptionID = (int?)attributeOption_product.AttributeOptionsID,
product.ManufacturerID
};
query.Distinct().Dump();
Below is the sample output:
ID ProductGroupID AttributeOptionID ManufacturerID
1 1 75 1
1 1 76 1
2 3 17 2
3 2 3 1
4 1 NULL 1
As you see, we have 2 record with ID = 1, and I don't wanna this, how I can remove this one?
I wrote this query for filtering section of my project.
I have filtering by ProductGroupID, AttributeOptionID, ManufacturerID, all of them are multi-select-able !
thanks !
As I said in the comments, you can use the other overload of Distinct extension method which asks for an EqualityComparer.
Or another way you can use
query.GroupBy(item => item.ID).Select(grp => grp.First())...
instead of
query.Distinct()...
Please do not forget that the output will not contain the second record in your sample output.
If the key of your records is not only ID but ID and ProductGroupID instead, usage can be
query.GroupBy(item => new {item.ID, item.ProductGroupId} ).Select(grp => grp.First())...
I have the following query:
var results = from theData in GeometricAverage
group theData by new { study = theData.study, groupNumber = theData.groupNumber, GeoAverage= theData.GeoAverage } into grp
select new
{
study = grp.Key.study,
groupNumber = grp.Key.groupNumber,
TGI = testFunction(grp.Key.GeoAverage, Also here I want to pass in the GeoAverage for only group 1 (but for each individual study))
};
What I want to do is that for each study, there are multiple groups with a GeoAverage figure for each group. The TGI is calculated by passing the GeoAverage figure for each group and the GeoAverage figure for group 1 (on each study) into the testFunction. I can't figure out how to pass in the value just for group 1.
Hope this makes sense.
EDIT: Sample of data:
Study Group GeoAverage
1 1 3
1 2 5
1 3 6
2 1 2
2 2 3
2 3 9
So, for the above data, I would want each GeoAverage figure for each group, to be evaluated against the GeoAverage figure of group 1 within that same study. So if I have say a function:
int foo(int a, int b)
{
return a * b;
}
Using the data above, I would first evaluate study 1, group 1 against itself, so pass in GeoAverage 3 twice and return 9. For Study 1, group 2, pass in group 2 GA at 5, and that studys group1 GA at 3, returning 15.
Have now worked it out. I iterate through a collection of data that I want the value to be stored against and use the following two LINQ queries:
foreach (var data in compoundData)
{
var controlValue = from d in GeometricAverage
where d.study == data.study
where d.groupNumber == "1"
select d.GeoAverage;
var treatmentValue = from l in GeometricAverage
where l.study == data.study
where l.groupNumber == data.groupNumber
select l.GeoAverage;
data.TGI = CalculateTGI(controlValue, treatmentValue);
}
I've started using Linq-to-entities with .NET 3.5, and I've come across a scenario that I can do real easy in SQL, but I'm having great difficulty with linq.
I have a table in a SQL database with three fields, for purposes of this example I'll call them foo, bar, and foo_index and populate them like so:
Blockquote
foo | bar | foo_index
555 101 1
555 101 2
555 101 3
555 101 4
555 101 5
To get what bar is at the max foo_index where foo=555 the SQL is
SELECT bar, max(foo_index) as max_foo_index
FROM T
WHERE foo=555
GROUP BY bar
I've written some linq-to-entities code in C# that works, but I have a feeling there is a much more elegent solution out there, and that I'm missing some concept that would make the following much easier. Also, is there any way other then a foreach to get the data out of a var?
db_entity db = new db_entity();
var q =
from table in db.T
where table.FOO == 555
select new { table.BAR, table.FOO_INDEX };
var q2 =
from t2 in q
where t2.FOO_INDEX == table.Max(r => r.FOO_INDEX)
select new { t2.BAR };
int result = 0;
foreach (var record in q2}
{
result = record.BAR.Value;
}
Any help or guidance would be appreciated, Thanks!
I think this should work:
var query = from item in db.T
where item.FOO == 555
group item by item.BAR into g
select new { Bar = g.Key, Max = g.Max(x => x.FOO_INDEX) };
So basically you're taking the same approach as in the SQL: group the data, then take the key for the group and the maximum FOO_INDEX within the group.
You can group elements in LINQ to Entities the same way you do in SQL. The syntax would look something like:
var q = from t in db.T
where t.FOO == 555
group t by t.BAR into g
select new {
Bar = group.Key,
Max = g.Max(b => b.FOO_INDEX)
};