How to find all rows of items that have a part in common using LINQ? - linq

I need to return all records (items) that has a part (X) so I can use that in a group or .GroupBy afterwards
Using this summary data:
ItemName PartName
1 A
1 B
2 A
3 C
So Item1 has two parts (A,B), etc...
I need a LINQ query that will
- find all items that have part A (i.e items 1 and 2)
- return all rows for all these items
1 A
1 B
2 A
Notice that the end result returned the row (1 B) because Item1 has PartA and so I need to get back all rows for Item1.
I was looking at something like:
let items = from data in summary where data.PartName == A select new { data.ItemName } // to get all the items I need
But then, now that I have that list I need to use it to get all the rows for all items listed, and I can't seem to figure it out ...
Actual Source Code (for reference):
NOTE:
Recipe = ITEM
Ingredient = PART
(I was just trying to make it simpler)
ViewFullRecipeGrouping = (
from data in ViewRecipeSummary
group data by data.RecipeName into recipeGroup
let fullIngredientGroups = recipeGroup.GroupBy(x => x.IngredientName)
select new ViewFullRecipe()
{
RecipeName = recipeGroup.Key,
RecipeIngredients = (
from ingredientGroup in fullIngredientGroups
select new GroupIngredient()
{
IngredientName = ingredientGroup.Key
}
).ToList(),
ViewGroupRecipes = (
from data in ViewRecipeSummary
// this is where I am looking to add the new logic to define something I can then use within the next select statement that has the right data based on the information I got earlier in this query.
let a = ViewRecipeSummary.GroupBy(x => x.RecipeName)
.Where(g => g.Any(x => x.IngredientName == recipeGroup.Key))
.Select(g => new ViewRecipe()
{
RecipeName = g.Key,
IngredientName = g.Select(x => x.IngredientName)
})
select new GroupRecipe()
{
// use the new stuff here
}).ToList(),
}).ToList();
Any help would be much appreciated.
Thanks,

I believe this does what you want:
var data = /* enumerable containing rows in your table */;
var part = "X";
var items = new HashSet<int>(data
.Where(x => x.PartName == part)
.Select(x => x.ItemName));
var query = data.Where(x => items.Contains(x.ItemName));
If I understand your comment at the end, I believe this also does what you want:
var query = data
.GroupBy(x => x.ItemName)
.Where(g => g.Any(x => x.PartName == part))
.Select(g => new
{
ItemName = g.Key,
PartNames = g.Select(x => x.PartName)
});

Related

LINQ : How to set value of Take() from a field in current query

I have a table of Widgets with columns "Name" and "Count"
The "Count" field contains count of Posts each Widget would show:
Name | Count
---------------------
RecentNews | 6
SpecialNews | 5
NewsGallery | 10
The second table associated with Widgets Table and Posts Table :
PostID | WidgetID
------------------
100 | 6
101 | 5
102 | 10
For more performance, I just fetch last needed posts for each Widget by this query:
var postInWidgets = db.PostWidgets
.GroupBy(pw => pw.WidgetID)
.SelectMany(g => g.OrderByDescending(p => p.Post.DateCreated).Take(500))
.ToList();
and then get posts in each widget :
var postsInGalery = postInWidgets
.Where(wid => wid.WidgetID == 1).Take(6)
.ToList();
var postsInSpecialNews=postInWidgets
.Where(wid => wid.WidgetID == 2).Take(5)
.ToList();
var postsInRecentNews=postInWidgets
.Where(wid => wid.WidgetID == 5).Take(10)
.ToList();
and in each Widget Partial View :
foreach(var p in Model.PostsInRecentNews)
{
<li>#Html.ActionLink(p.Post.Title,"Index","Home")</li>
}
My Question : How to Set the int value of Take(count) Dynamically for each widget instead of Take(6) , Take(5) , Take(10) ...
I think I need to use TakeWhile() instead the Take()
Thanks for any assistance...
It sounds like you just need to fetch the counts first (you may want to cache them):
var counts = db.Counts.ToDictionary<string, int>(c => c.Name, c => c.Count);
Then:
var postsInGallery = postInWidgets
.Where(wid => wid.WidgetID == 1)
.Take(counts["NewsGallery"])
.ToList();
var postsInSpecialNews = postInWidgets
.Where(wid => wid.WidgetID == 2)
.Take(counts["SpecialNews"])
.ToList();
var postsInRecentNews = postInWidgets
.Where(wid => wid.WidgetID == 5)
.Take(counts["RecentNews"])
.ToList();
You could potentially use an enum instead of a string, to avoid the use of easily-typoed string constants. If the enum had a value of the related widget ID, you could wrap that up in a single method:
List<Widget> GetWidgets(WidgetType widgetType)
{
return postInWidgets.Where(wid => wid.WidgetID == (int) widgetType)
.Take(counts[widgetType])
.ToList();
}
Then call it as:
var postsInGallery = GetWidgets(WidgetType.NewsGallery);
var postsInSpecialNews = GetWidgets(WidgetType.SpecialNews);
var postInRecentNews = GetWidgets(WidgetType.RecentNews);
(This assumes counts is a field somewhere, of course - adjust as per your requirements.)

Linq GROUP & SUM the same columns from different tables

I'm trying to combine these 2 Linq queries into 1:
var query = from s in _context.Set<StockInventoryItem>()
where s.StockCatalogueItemId == id
group s by s.StockType into g
select new
{
inStock = g.Sum(x => x.QtyInStock),
};
var query2 = from p in _context.Set<PurchaseOrderItem>()
where p.StockCatalogueItemId == id
group p by p.StockType into g2
select new
{
onOrder = g2.Sum(x => x.QtyStillDue)
};
Note that the filtering, grouping and output is the same from both tables, and I want the results to look like this:
StockType inStock onOrder
+----------+--------+--------+
Type 1 4 3
+----------+--------+--------+
Type 2 0 1
i.e. Quantities grouped by StockType
This is EF code first and there is no direct relationship between these tables, which is why I'm trying this query in the service layer so I can access both entities.
You should be able to "shoehorn" both groups into the same sequence with anonymous types and Concat, and then count the results separately, like this:
var query = _context.Set<StockInventoryItem>()
.Where(ii => ii.StockCatalogueItemId == id)
.Select(ii => new {
II = ii, PO = (PurchaseOrderItem)null
}).Concat(_context.Set<PurchaseOrderItem>()
.Where(po => po.StockCatalogueItemId == id)
.Select(po => new {
II = (StockInventoryItem)null, PO = po
})).GroupBy(p => II != null ? ii.StockType : PO.StockType)
.Select(g => new {
InStock = g.Sum(p => p.II != null ? p.II.QtyInStock : 0)
, OnOrder = g.Sum(p => p.PO != null ? p.PO.QtyStillDue: 0)
});

LINQ to SQL, select targets with max date

I ended up with this horrible code below, I can't get a better result now.
What is a better way of doing that?
It's about this part of my database:
EDIT
A Patient has a Subscription to multiple MonitoringObjects. Target records refer to these Subscriptions. I want to retrieve the target records with the newest date per Subscription for a given Patient and a Category of MonitoringObjects. These target records may have different max dates, as Targets can be added for Subscriptions to MonitoringsObjects independently.
var subs = db.Subscriptions.Where(p => p.PatientID == patID).Where(p => p.MonitoringObject.Category.Name == "Medication");
var targets1 = from t in db.Targets
where subs.Contains(t.Subscription)
select t;
var maxTa = from t in db.Targets
group t by t.SubscriptionID
into g
select new
{
Ky = g.Key,
Date = g.Max(p => p.Date)
};
var targets2 = from t in targets1
where maxTa.Select(p => p.Ky).Contains( t.SubscriptionID ) &&
maxTa.Select(p => p.Date).Contains( t.Date )
select t;
I am not exactly sure what this is trying to achieve, or what your datamodel looks like, but something like this?
var subs = db.Subscriptions.Where(p => p.PatientID == patID).Where(p => p.MonitoringObject.Category.Name == "Medication");
var targets = subs
.SelectMany(s => s.Targets)
.Where(t => t.Date == t.Subscription.Targets.Max(_t => _t.Date))

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.

How to combine Where clause and group by in LINQ

Can any one help me convert the below code to LINQ?
Select Catg,Count(*) From Mycatg where IsPublic=1 or FirstName='XXX' Group By Catg .
In C#, something like:
var query = from category in mycatg
where category.IsPublic == 1
|| category.FirstName == "XXX"
group 1 by category.Catg into grouped
select new { Catg = grouped.Key,
Count = grouped.Count() };
The projection of "1" makes it clear that all we need is the key of the grouping and the count - the individual entries in each grouping are irrelevant.
Using lambda syntax and dot notation:
var query = mycatg.Where(category => category.IsPublic == 1
|| category.FirstName == "XXX")
.GroupBy(category => category.Catg,
category => 1)
.Select(grouped => new { Catg = grouped.Key,
Count = grouped.Count() });

Resources