Sum amount using Linq in <List<Dictionary<string, int>> - linq

I have a generic list declared as so:
List<Dictionary<string, int>> sales;
The string will be a product name and the int the number of units sold. I want to group by productname and sum the total sales. So i can see the number of units sold for each product.
How would i do this using linq?
Thanks!

This will give you the result as a dictionary where the key is the product name and the value is the total number of units sold.
var result =
sales.SelectMany(d => d) // Flatten the list of dictionaries
.GroupBy(kvp => kvp.Key, kvp => kvp.Value) // Group the products
.ToDictionary(g => g.Key, g => g.Sum()); // Sum each group

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 query to return first item in an ordered grouping

Grouping is an area of LINQ that I haven't quite managed to get my head around yet. I have the following code:
var subTrips = tbTripData
.Where(t => selectedVehicleIds.Contains(t.VehicleID))
.Join(tbSubTripData,
t => t.TripID,
s => s.TripID,
(t, s) => new { t = t, s = s })
.Select(r =>
new SubTrip
{
VehicleID = r.t.VehicleID,
TripID = r.t.TripID,
Sequence = r.s.Sequence,
TripDistance = r.s.TripDistance,
Odometer = r.s.Odometer
})
.ToList();
I'm trying to figure out a LINQ query that will look at subTrips and for each VehicleID, find the first Odometer, i.e. the Odometer corresponding to the lowest TripID and Sequence values.
I've been poking at it for an hour but just can't figure it out. Can anyone offer some advice before I give up and write procedural code to do it?
UPDATE: To clarify, Sequence is the sequential number of each subtrip within a trip. So what I'm looking for is the Odometer from the first subtrip for each vehicle when the subtrips within each grouped VehicleID are ordered by TripID then by Sequence.
I'm not 100% what you're looking for. But this might get you started in the right direction.
var groupedList = (from s in subTrips
group s by s.VehicleID
into grp
select new
{
VehicleID = grp.Key,
Odometer = grp.OrderBy(ex => ex.TripID).Select(ex => ex.Odometer).First(),
TripID = grp.Min(ex => ex.TripID)
}
).ToList();
This will return the VehicleID, the Odometer corresponding to the lowest TripID, and the lowest TripID.

Using Linq to check if a list includes null values in the middle

I have a requirement where I need to filter out the null values from a list, only if the null value appears after the first non-null value and before the last non-null value.
Product one = new Product { Name="A" Priority="1" Value=null };
Product two = new Product { Name="A" Priority="2" Value=null };
Product three = new Product { Name="A" Priority="3" Value="10" };
Product four = new Product { Name="A" Priority="4" Value=null };
Product five = new Product { Name="A" Priority="5" Value="20" };
Product six = new Product { Name="A" Priority="6" Value=null };
In the example, I need to first sort the list of products based on their priority and then check the first non-null value (ie., priority 3) and last non-null value (ie, priority 5), then get the list of all products with null values within priority 3 & 5. So, in our example only Product 4 with Priority 4 is the record I am looking for.
I got to the part of actually grouping them by products and sorting them by priority but stuck on how to proceed after that
from p in Products
group p by p.Product into grp
select new
{
Product = grp.Key
Values = grp.OrderBy(x => x.Priority)
}
Can someone point me as how to proceed? I am thinking I may to use the indexes to identify all the non-null and iterate through to get min and max Priority values and later query for all records with blank null values within the min/max priority.
It would be easier and more efficient if you determined which values are your start and stop points in the group and filter from there.
var query =
from product in Products
group product by product.Name into g
let ordered = g.OrderBy(p => p.Priority).ToList()
let firstIndex = ordered.FindIndex(p => p.Value != null)
let lastIndex = ordered.FindLastIndex(p => p.Value != null)
select new
{
Product = g.Key,
Values = ordered
.Skip(firstIndex + 1)
.Take(lastIndex - firstIndex - 1)
.Where(p => p.Value == null),
};
How about this (you can use .Select instead of .SelectMany to get separate groups for each product. .SelectMany combines all the valid result records into a single list):
Products
.GroupBy(p => p.Name)
.SelectMany (grp =>
grp.OrderBy(p => p.Priority) // sort by priority
.SkipWhile(p => p.Value == null) // skip null entries at beginning
.Reverse() // reverse
.SkipWhile(p => p.Value == null) // skip null entries at end
.Reverse() // reverse back to normal
.Where(p => p.Value == null) // then find null entries
);
Demo: http://ideone.com/2dU9L

group by and joining tables in linq to sql

I have the following 3 classes(mapped to sql tables).
Places table:
Name(key)
Address
Capacity
Events table:
Name(key)
Date
Place
Orders table:
Id(key)
EventName
Qty
The Places and Events tables are connected through Places.Name = Events.Place, while the Events and Orders tables: Events.Name = Orders.EventName .
The task is that given an event, return the tickets left for that event. Capacity is the number a place can hold and Qty is the number of tickets ordered by someone. So some sort of grouping in the Orders table is needed and then subtract the sum from capacity.
Something like this (C# code sample below)?
Sorry for the weird variable names, but event is a keyword :)
I didn't use visual studio, so I hope that the syntax is correct.
string eventName = "Event";
var theEvent = Events.FirstOrDefault(ev => ev.Name == eventName);
int eventOrderNo = Orders.Count(or => or.EventName == eventName);
var thePlace = Places.FirstOrDefault(pl => pl.Name == theEvent.Place);
int ticketsLeft = thePlace.Capacity - eventOrderNo;
If the Event has multiple places, the last two lines would look like this:
int placesCapacity = Places.Where(pl => pl.Name == theEvent.Place)
.Sum(pl => pl.Capacity);
int ticketsLeft = placesCapacity - eventOrderNo;
On a sidenote
LINQ 101 is a great way to get familiar with LINQ: http://msdn.microsoft.com/en-us/vcsharp/aa336746

Aggregate functions in 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} );

Resources