Aggregate functions in LINQ - linq

I have the following LINQ conditional where clause query that produces a result of weights:
From this, I'd like to take the result set and join on another table, tblPurchases
var result = weights.Join(getsuppliersproducts.tblPurchases,
w => new { w.MemberId, w.MemberName, w.LocationId, w.UnitId },
p => new { p.MemberId, p.MemberName, p.LocationId, p.UnitId },
(w, p) => p);
In this second table, I have two columns I would like to perform an aggreagte function on, a sum on PurchaseQuantity and a count of UnitID.
So in its raw format, tblPurchases would look like so:
MemberID LocationID UnitId SupplierID SupplierStatus Purchases
1 1 ab Sup1 Live 10
1 1 abc Sup1 Live 10
1 1 abcd Sup2 Dead 50
From my results data set, I would like the output to look like so:
MemberID LocationID SupplierID SupplierStatus UnitIdCount Total Purchases
1 1 Sup1 Live 2 50
Also, with these amendments, can I still return this to a List?
How do I implement this using LINQ? I have tried, and failed miserably.
(To those who have seen my previous posts, I'm trying to cover all angles so I can fully understand the concept of what is going on in both SQL and LINQ)

That query will return an IEnumerable where each of the Purchases matches the MemberId, MemberName, LocationId and UnitId in the original Weights query. You can only easily do one aggregate at a time, so
var result = weights.Join(getsuppliersproducts.tblPurchases,
w => new { w.MemberId, w.MemberName, w.LocationId, w.UnitId },
p => new { p.MemberId, p.MemberName, p.LocationId, p.UnitId },
(w, p) => p).ToList();
Int32 count = result.Count();
Double quantity = result.Sum(p => p.PurchaseQuantity);
Is that what you're trying to do?
EDIT, after your reply of I would like to reutrn a list of tblPurchases with two new columns, the sum of Purchase Quantity and count of unit ID.
This gives a flat output:
var query = Weights.GroupJoin(
Purchases,
w => new {w.MemberId, w.LocationId},
p => new {p.MemberId, p.LocationId},
(w,p) => new {w.MemberId, w.LocationId, Count = p.Count(), Sum = p.Sum(x => x.Purchases)} );
Note that at the point we do the (w, p) => new {} that w is a single Weight and p is a list of Purchases matching that weight, so you can still keep all of teh (hierarchical) data:
var query = Weights.GroupJoin(
Purchases,
w => new {w.MemberId, w.LocationId},
p => new {p.MemberId, p.LocationId},
(w,p) => new {w.MemberId, w.LocationId, Count = p.Count(), Sum = p.Sum(x => x.Purchases), Purchases = p} );

Related

Linq: Count number of times a sub list appear in another list

I guess there must be an easy way, but not finding it. I would like to check whether a list of items, appear (completely or partially) in another list.
For example: Let's say I have people in a department as List 1. Then I have a list of sports with a list of participants in that sport.
Now I want to count, in how many sports does all the people of a department appear.
(I know some tables might not make sense when looking at it from a normalisation angle, but it is easier this way than to try and explain my real tables)
So I have something like this:
var peopleInDepartment = from d in Department_Members
group d by r.DepartmentID into g
select new
{
DepartmentID = g.Key,
TeamMembers = g.Select(r => d.PersonID).ToList()
};
var peopleInTeam = from s in Sports
select new
{
SportID = s.SportID,
PeopleInSport = s.Participants.Select(x => x.PersonID),
NoOfMatches = peopleInDepartment.Contains(s.Participants.Select(x => x.PersonID)).Count()
};
The error here is that peopleInDepartment does not contain a definition for 'Contains'. Think I'm just in need of a new angle to look at this.
As the end result I would like print:
Department 1 : The Department participates in 3 sports
Department 2 : The Department participates in 0 sports
etc.
Judging from the expected result, you should base the query on Department table like the first query. Maybe just include the sports count in the first query like so :
var peopleInDepartment =
from d in Department_Members
group d by r.DepartmentID into g
select new
{
DepartmentID = g.Key,
TeamMembers = g.Select(r => d.PersonID).ToList(),
NumberOfSports = Sports.Count(s => s.Participants
.Any(p => g.Select(r => r.PersonID)
.Contains(p.PersonID)
)
)
};
NumberOfSports should contains count of sports, where any of its participant is listed as member of current department (g.Select(r => r.PersonID).Contains(p.PersonID))).

LINQ Get Grouped ID by condition

Hi I have a List so:
A 1
A 2
A 3
A 4
B 1
B 2
C 1
I want to select the letter that contains AT LEAST these 3 numbers: 1,2,3
So in this case would be selected the letter A.
Can you help me to write this as LINQ expression?
Thanks a lot!
First, make a collection of the numbers you require.
var required = new[] { 1, 2, 3 };
Then, group your pairings by letter.
var groupedPairings = pairings.GroupBy(p => Letter, p => Number);
Then, discard those pairings that don't have your three required items. (The logic here is "take the collection of required items, remove anything in the group, and make sure there is nothing left".)
var groupsWithRequired = groupedPairings
.Where(g => !required.Except(g).Any());
Now, if you just want the letters, you can simply do
var lettersWithRequired = groupsWithRequired.Select(g => g.Key);
or if you want a dictionary mapping from the letter to its collection of numbers, you can do
var dictionary = groupsWithRequired.ToDictionary(g => g.Key, g => g.ToArray());
var numbersForA = dictionary["A"]; // = {1, 2, 3, 4}
You could try this, although I don't feel it's the best answer:
var items = new List<Item>{
new Item{Name="A", Value=1},
new Item{Name="A", Value=2},
new Item{Name="A", Value=3},
new Item{Name="A", Value=3},
new Item{Name="A", Value=4},
new Item{Name="B", Value=1},
new Item{Name="B", Value=2},
new Item{Name="C", Value=1},
};
var values = new List<int>{1,2,3};
var query = items.GroupBy (i => i.Name)
.Where (i => i.Select (x => x.Value)
.Intersect(values).Count() == values.Count)
.Select (i => i.Key);
Where
class Item{
public string Name{get;set;}
public int Value{get;set;}
}

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)
};

LINQ Join With Multiple Where Clause

I am struggling once again so any help would be gratefully received.
I have the following LINQ that pulls back a list of data:
public static List<tblWeight> GetWeights(string memberid, string locationid, string buyer, string subcategory, string product)
{
MyEntity getweights = new MyEntity ();
var r = (from p in getweights.tblWeights
where p.MemberId == memberid &&
p.LocationId == locationid
select p);
if (buyer != "Not Specified")
r = r.Where(p => p.UnitUserField1 == buyer);
if (subcategory != "Not Specified")
r = r.Where(p => p.UnitUserField2 == subcategory);
if (product != "Not Specified")
r = r.Where(p => p.IDDesc == product);
return r.ToList();
}
Lovely!
What I would like to do now is based upon this result set and the unit IDs (IDDesc), I then go to tblPurchase, pull back a few columns from tblPurchases and group the columns.
So for example, we have tblWeight looking like so:
MemberID LocationID Buyer SubCategory IDDesc
1 1 Cat1 Sub1 ab
1 1 Cat1 Sub1 abc
1 1 Cat1 Sub2 abcd
The user makes a search for Sub1 in subcategory and the above LINQ does the trick and pulls back the first two rows from above. Fine.
What I need the LINQ to do now is to go to tblPurchases:
MemberID LocationID IDDesc SupplierID SupplierStatus
1 1 ab Sup1 Live
1 1 abc Sup1 Live
1 1 abcd Sup2 Dead
And then pull back the following result so it is joined on MemberID, LocationID and IDDesc but just selects tblPurchases.
Sup1 Live (or all columns in tblPurchases, just grouped/distinct)
I have tried to add in a join and on but no matter how many different variations, I still come across the red squiggle of doom!!!
If anyone can help, beer/kiss is on offer again.
The following LINQ query should do what you want:
var result = from w in tblWeight
where w.SubCategory == "Sub1"
join p in tblPurchases on
new { w.MemberID, w.LocationID, w.IDDesc } equals
new { p.MemberID, p.LocationID, p.IDDesc }
group p by new { p.SupplierID, p.SupplierStatus } into pg
select pg.Key;
The variable result is a list containing tuples of SupplierID and SupplierStatus.
If you also want to put the conditional parts in there, it gets a little more complicated. Here's how to do it:
var weights = from w in tblWeight
select w;
weights = weights.Where(w => w.SubCategory == "Sub1");
// You can add additional where clauses here.
// Now join with tblPurchases and group by SupplierID and SupplierStatus.
var result =
weights.Join(tblPurchases,
w => new { w.MemberID, w.LocationID, w.IDDesc },
p => new { p.MemberID, p.LocationID, p.IDDesc },
(w, p) => p)
.GroupBy(p => new { p.SupplierID, p.SupplierStatus },
(k, ps) => new
{
k.SupplierID,
k.SupplierStatus,
TotalQty = ps.Sum(p => p.PurchaseQty)
});

Removing values from a returned linq query

HI there I am hoping for some help with a query I have.
I have this query
var group =
from r in CustomerItem
group r by r.StoreItemID into g
select new { StoreItemID = g.Key,
ItemCount = g.Count(),
ItemAmount = Customer.Sum(cr => cr.ItemAmount),
RedeemedAmount = Customer.Sum(x => x.RedeemedAmount)
};
I am returning my results to a list so I can bind it listbox.
I have a property called EntryType which is an int. There are 2 available numbers 1 or 2
Lets say I had 3 items that my query is working with
2 of them had the EntryType = 1 and the 3rd had EntryType2. The first records had a ItemAmount of 55.00 and the 3rd had a ItemAmount of 50.00
How can I group using something simlar to above but minus the ItemAmount of 50.00 from the grouped amount to return 60.00?
Any help would be great!!
It's not really clear what the question is - are you just trying to ignore all items with an entry type of 2? To put it another way, you only want to keep entries with an entry type of 1? If so, just add a where clause:
var group = from r in CustomerItem
where r.EntryType == 1
group r by r.StoreItemID into g
select new {
StoreItemID = g.Key, ItemCount = g.Count(),
ItemAmount = Customer.Sum(cr => cr.ItemAmount),
RedeemedAmount = Customer.Sum(x => x.RedeemedAmount)
};
Change ItemAmount = ... to:
ItemAmount =
g.Where(x => x.EntryType == 1).Sum(cr => cr.ItemAmount) -
g.Where(x => x.EntryType == 2).Sum(cr => cr.ItemAmount),
I changed Customer to g because this seems to be an error, but it's not clear to me from your question what you mean here, so maybe this change is not what you want.
A slightly more concise method is to use test the entry type in the sum and use the ternary operator to choose whether to add the positive or negative value:
ItemAmount = g.Sum(cr => cr.EntryType == 1 ? cr.ItemAmount : -cr.ItemAmount),
This gives the value of 60.00 as you required.

Resources