LINQ: Select a column based on another column value - c#

After executing some LINQs, I have this data as IQueryable of an anonymous type:
Cat Type Count
----------------
A 1 10
B 1 15
C 1 25
D 1 5
A 2 15
C 2 30
D 2 20
B 2 10
Now, for every Cat I want to select Count based on Type values with LINQ.
Output should be like this:
Cat Type1Count Type2Count
---------------------------
A 10 15
B 15 10
C 25 30
D 5 20

You can try to use groupby then select to do pivot.
var query = myList
.GroupBy(c => c.Cat)
.Select(g => new {
Cat = g.Key,
Type1Count = g.Where(a => a.Type == 1).Sum(c => c.Count),
Type2Count = g.Where(a => a.Type == 2).Sum(c => c.Count)
});
c# online
Result
Cat:A Type1Count:10 Type2Count:15
Cat:B Type1Count:15 Type2Count:10
Cat:C Type1Count:25 Type2Count:30
Cat:D Type1Count:5 Type2Count:20

So basically you need to group by Cat and Type and get sum of Count:
myList.GroupBy(x => new { x.Cat, x.Type }).Select(x => new { Cat = x.Key.Cat, Type = x.Key.Type, CountForTypeAndCat = x.Sum(y=>y.Count) })
This will allow you go get information for any Type not only 1 and 2
foreach (var item in query.OrderBy(x=>x.Cat).ThenBy(x=>x.Type))
{
Console.WriteLine(string.Format("Cat:{0} Type:{1} CountForTypeAndCat:{2}", item.Cat, item.Type, item.CountForTypeAndCat));
}

Related

Filter data with LINQ from List or DataSet in C#

I am new to Linq, I have a data in DataSet and also in List similar to this; I need to enter count of rows against each Name with respect to month and year and amount should not be 0 or null, also retuned data should be ascending order.
Name Month Year Amount
A 3 1999 10
A 3 1999 10
A 1 1998 10
A 5 2002 10
A 9 2009 // amount is empty
B 1 1999 10
B 1 1999 0 // amount is zero
B 6 2002 10
B 9 2009 10
B 2 1998 10
B 1 1998 10
B 1 1998 10
How can I convert my data like this;
Name 01/1998 02/1998 03/1999 05/2002 06/2002 09/2009
A 1 0 2 1 0 0 // amount was null
B 2 1 1 /*one was 0*/ 0 1 1
I have tried like below query but I guess due to my less knowledge to LINQ, i am making mistake somewhere;
var query = dataList
.GroupBy(a => a.Name)
.Select(g => new
{
Name = g.Key
Jan98 = g.Where(c => c.Day == g.Day).Count()
});
I have also searched, but none of the found was helping for this type of filtration. I am ale to filter this in Excel pivot table. I want to do this in c#. Any other way except LINQ will also be appreciated.
I would prefer making this more verbose and less LINQy just to avoid big queries, but there is still a way to do what you want with LINQ:
var items = data.Where(x => x.Amount != null && x.Amount != 0)
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key,
x => x.GroupBy(t => (t.Month, t.Year))
.ToDictionary(t => t.Key, t => t.Count()));
We can iterate over this thing like this:
foreach (var item in items)
{
Console.WriteLine($"{item.Key}\t");
foreach (var v in items[item.Key].OrderBy(x => x.Key.Year).ThenBy(x => x.Key.Month))
{
Console.WriteLine($"{v.Key}\t{v.Value}");
}
}
The output provided your sample data would be:
A
(1, 1998) 1
(3, 1999) 2
(5, 2002) 1
B
(1, 1998) 2
(2, 1998) 1
(1, 1999) 1
(6, 2002) 1
(9, 2009) 1
If you are trying to get one element you would do something like:
int count = items["A"][(1, 1998)]; // count == 1
Here a IQueryableversion:
var query = dataList
.Where(r => r.Amount.HasValue && r.Amount.Value != 0) // discard 0 and null
.GroupBy(r => r.Name).OrderBy(r => r.Key) // group and sort by name
// now project each name-group (ng) to a 2-tuple:
// 1. The group key
// 2. The elements grouped and order by date.
.Select(ng => (ng.Key, ng.GroupBy(r => (r.Year, r.Mounth)).OrderBy(dg => dg.Key.Year).ThenBy(gd => gd.Key.Mounth)));
foreach (var (name, dateGroup) in query)
{
foreach (var g in dateGroup)
{
Console.WriteLine($"For name {name} in {g.Key.Mounth}/{g.Key.Year}. Count is {g.Count()}. Amount sum is {g.Sum(r => r.Amount)}");
}
}

Linq - how to write group by query to get quantity of each product

Hello every one I have a data as follows
Item Qty Type
1 2 Purchase
1 3 Sales
1 8 Return
2 5 Purchase
2 4 Sales
2 5 Return
Now I have a requirement of getting quantity of each item by Subtracting Sales and Return with Purchase
And my final output would be
item Qty
1 -9
2 -4
Note : To get quantity: Qty => (Purchase Qty - SalesQty - Return Qty) eg: (2-3-8)
So how can I write this query on LINQ or in SQL
SQL Query:
SELECT Item, SUM(CASE Type WHEN 'Purchase' THEN Qty ELSE -Qty END) AS Qty
FROM Table
GROUP BY Item
LINQ:
Items
.GroupBy(p => p.Item)
.Select(p => new
{
Item = p.Key,
Qty = p.Sum(x => x.Type == "Purchase" ? x.Qty : -x.Qty)
});
If you are using LINQ to SQL, you can do:
var ans = from i in src
group i by i.Item into ig
let PurchaseQty = ig.Where(i => i.Type == "Purchase").Sum(i => i.Qty)
let SalesQty = ig.Where(i => i.Type == "Sales").Sum(i => i.Qty)
let ReturnQty = ig.Where(i => i.Type == "Return").Sum(i => i.Qty)
select new {
Item = ig.Key,
Qty = PurchaseQty - SalesQty - ReturnQty
};
If you are using LINQ to EF 2.2, it may work but will do the grouping client side. If you are using EF 3.x, good luck!

Entity Framework group by and get max

Lets say I've table with the structure as below:
MyRow:
Id Name Date
1 A 2015/01/01
2 B 2015/01/01
3 C 2015/01/02
4 A 2015/01/03
5 B 2015/01/03
6 A 2015/01/02
7 C 2015/01/01
Using EF I would like to get list of MyRow which would contain elements with distinct names and newest date so in this case it would be:
4 A 2015/01/03
5 B 2015/01/03
3 C 2015/01/02
I started with something like this:
var myRows = context.MyRows.GroupBy(mr => mr.Name).Select(..now wth with max..)
Order each group and take the last of each.
Or since EF doesn't (last time I checked) support Last(), order each group in reverse and take the first:
var myRows = context.MyRows
.GroupBy(mr => mr.Name)
.Select(grp => grp.OrderByDescending(mr => mr.Date).First());
var data = context.MyRows.Group(p => p.Name)
.Select(g => new {
Type = g.Key,
Date = g.OrderByDescending(p => p.Date)
.FirstOrDefault()
}

Linq Group by Values in a list

So Currently I have this TrackingInfo class which contains an ID and a list of EmailActionIDs, which is an int.
I have a List Of this class which the data looks like:
ID, | EmailActions
_______________
A | 1, 3, 5
B | 3, 5, 6
C | 2, 4, 6
I'm trying to write a Linq Statement To convert this into A list of IDs grouped by each individual value in the list.
So the Results Set would look like:
ID | Values
_______________
1 | A
2 | C
3 | A, B
4 | C,
5 | A, B
6 | B, C
I can't figure out how I would write the group by can anyone give me some insight.
DistinctValues = new List<int> {1,2,3,4,5,6};
TrackingInfo.Groupby(t => DistinctValues.foreach(d =>
t.EmailActions.Contains(d))).Tolist()
This ofcourse isn't working any suggestions on how to do this using Linq
Its easy enough to get a distinct list of EmailActions
var distinctEmailActions = items.SelectMany(i => i.EmailActions).Distinct();
Then pivoting this is a little complex, but here it is:
var result = distinctEmailActions.Select(e => new {
ID=e,
Values = items.Where(i => i.EmailActions.Contains(e)).Select(i => i.ID)
});
Live example: http://rextester.com/CQFDY66608
What you're looking for is SelectMany, but it's easier to use query syntax here:
var result = from item in source
from action in item.EmailActions
group item.ID by action into g
select new { ID = g.Key, Values = g.ToList() }
You can do it by first generating a range using Enumerable.Range, and then matching EmailActions, like this:
var res = Enumerable.Range(1, 6)
.SelectMany(v => TrackingInfo.Where(info => info.EmailActions.Contains(v)).Select(info => new { Id, Value = v }))
.GroupBy(p => p.Value)
.Select(g => new {
Id = g.Key
, Values = g.Select(p => p.Id).ToList()
});
You can achieve this using SelectMany & GroupBy like this:-
var result = tracking.SelectMany(x => x.EmailActionIDs,
(trackObj, EmailIds) => new { trackObj, EmailIds })
.GroupBy(x => x.EmailIds)
.Select(x => new
{
ID = x.Key,
Values = String.Join(",", x.Select(z => z.trackObj.ID))
}).OrderBy(x => x.ID);
Working Fiddle.

How to SUM the value from DISTINCT row using LINQ

Datatable as below:
Item CartonID Quantity
A 0001 1000
A 0002 500
A 0003 250
A 0002 500
B 0002 500
B 0003 250
My output suppose to be this:
ItemNo CartonID TotalCarton TotalQuantity
A 0001,0002,0003 3 2250
B 0002,0003 2 750
But my result is list as below:
ItemNo CartonID TotalCarton TotalQuantity
A 0001, 0002, 0003 3 2,250
B 0002, 0003 2 750
My code is list as below:
var items = ( from item in dtTest.AsEnumerable()
group item by new
{
Item_No=item.Field<string>("Item")
}
into g
select new
{
g.Key.Item_No,
TotalCarton = (from p in dtTest.AsEnumerable()
where p.Field<string>("Item") == g.Key.Item_No
select p.Field<string>("CartonID")).Distinct().ToArray().Count(),
Total_Quantity =g.Sum((p=>p.Field<decimal>("Quantity"))).ToString("###,###,###,###"),
CartonID = (from p in dtTest.AsEnumerable()
where p.Field<string>("Item") == g.Key.Item_No
select p.Field<string>("CartonID")).Distinct().ToArray().Aggregate
((p1, p2) => p1 + ", " + p2)
}).ToList();
Anyone can tell me how to sum the total with distinct Carton ID. Thanks in advance.
You should GroupBy CartonID and then sum the groups elements:
something like this
dtTest.GroupBy(x => x.CartonId)
.ToDictionary(y => y.Key, y => y.Sum(z => z.Quantity);
This will return a dictionary containing the total quantity per each CartonID. I hope i understood correctly that this is what you want.
Based on the Output that you said is supposed to be retrieved, i adapted the query to this:
dtTest.GroupBy(x => x.ItemNo)
.Select(itemGroup => new {
itemGroup.Key,
itemGroup.Distinct(item => item.CartonID),
itemGroup.Distinct(item => item.CartonID).Count(),
itemGroup.Sum(item => item.Quantity)
});

Categories

Resources