Select objects that contain specific child elements - c#

I have the following classes.
public class Bin
{
public int BinId { get; set; }
public IEnumerable<Component> Components { get; set; }
}
public class Component
{
public int ComponentId { get; set; }
public string ComponentName { get; set; }
}
Using LINQ how do I find all Bin objects that contain specific set of components, say components with IDs 1, 2, 3?
Edit
Just to clarify all IDs must be present in a Bin. Also I have a collection that contains IDs to match.

var bins = new List<Bin>();
var ids = new List<int> { 1, 2, 3 };
// go through each bin and make sure it has all the items in ids
bins.Where(x => ids.All(id => x.Components.Select(c =>
c.ComponentId).Contains(id)));

Like this:
bins.Where(b => b.Components.Any( c => new[]{1,2,3}.Contains(c.ComponentId) )
If you need all:
bins.Where(b => b.Components.All( c => new[]{1,2,3}.Any( i => i == c.ComponentId) ))
Or if you need that some items in the list has this items:
bins.Where(b => new[]{1,2,3}.All(i => b.Components.Any(c => i == c.ComponentId) ))
You can combine all/any/contains in sub query as you want

IEnumerable<int> test = ...;
bins.Where(x => !test.Except(x.Components.Select(c => c.ComponentId)).Any());

bins.Where(x => x.Components.Any(y => y.ComponentId ==1 || y.ComponentId == 2 || y.ComponentId == 3))
Try this one.
If you have list of integers then you can modify last conditions like below.
y => list.Any(z => y.ComponentId == z)
Or something like this.
y => list.Contains(y.ComponentId)
These conditions are to contain at least one component id. If you want to contain all component ids you can use All method instead of Any

Related

Get common records from list of list in linq

I have a list contains another list where I supposed to get the common elements.
Class model:
PlanInfo has shiftName, ilist of Plan.
Plan has start time, end time
public class Plan
{
public int Start { get; set; }
public int End { get; set; }
}
public class PlanInfo
{
public string ShiftName { get; set; }
public IList<Plan> lstPlan { get; set; }
}
iList of PlanInfo contains
[“shift1”, [1000,1200]],
[“shift2”,[1000,1100]],
[“shift3”,[1000,1200]]
Expected output in this should be empty since 1000,1200 doesn’t exist in shift2
[“shift1”, [1000,1200]],
[“shift2”,[[1000,1200],[1000,1100]],
[“shift3”,[1000,1200]]
Should return [1000,1200] since it’s common in all lists.
I tried using intersect, but here IList<PlanInfo is not fixed length. it could have more than one records.
Kindly suggest me which LINQ query serve the above result
Hmm, If I understand the requirements: Given a list of PlanInfo, find any Plans common to all PlanInfo...
var totalPlanInfos = planInfos.Count();
var commonPlans = planInfos
.SelectMany(x => x.Plans
.Select(p => new { x.ShiftName, Plan = p }))
.GroupBy(x => x.Plan)
.Where(x => x.Count() == totalPlanInfos)
.Select(x => x.Key)
.ToList();
This assumes that a Plan can only be counted once within a PlanInfo. (No duplicate plans) This also assumes that the plan info references for the same start/end times are pointing to the same object instance. If not, then you cannot group on the Plan, you will need a unique key (Like a plan ID) to group on. If these are EF entities pulled from a DbContext then they will be the same reference.
First get the total # of plan infos. In your example this would return 3.
Next, for all plan infos, use SelectMany to fetch the Plans, but compose that down into the PlanInfo.ShiftName + the Plan. This flattens your one to many. Next group by the Plan so that we can count the # of PlanInfos that each Plan appears in. Any/all counts that match the total number of PlanInfos means a Plan that appears in all PlanInfos, Select the Key to get that grouped Plan(s) and that should have it.
Edit: adding an example...
[Test]
public void TestPlanCheck()
{
var plan1 = new Plan { Start = 1, End = 2 };
var plan2 = new Plan { Start = 2, End = 3 };
var plan3 = new Plan { Start = 3, End = 4 };
var planInfos = new List<PlanInfo>
{
new PlanInfo{ Name = "Test1", Plans = new []{ plan1, plan2}.ToList() },
new PlanInfo{Name = "Test2", Plans = new []{plan2, plan3}.ToList()},
new PlanInfo{Name = "Test3", Plans = new []{ plan3, plan2}.ToList() }
};
var totalPlanInfos = planInfos.Count();
var commonPlans = planInfos
.SelectMany(x => x.Plans
.Select(p => new { x.Name, Plan = p }))
.GroupBy(x => x.Plan)
.Where(x => x.Count() == totalPlanInfos)
.Select(x => x.Key)
.ToList();
}
private class Plan
{
public int Start { get; set; }
public int End { get; set; }
}
private class PlanInfo
{
public string Name { get; set; }
public List<Plan> Plans { get; set; }
}
That was the test I had run using these stub classes. In this case the test will return back 1 match, for the Plan 2 value.
To outline the issue with ensuring plan references for the same start/end times match: If the setup looked like this:
[Test]
public void TestPlanCheck()
{
var plan1 = new Plan { Start = 1, End = 2 };
var plan2A = new Plan { Start = 2, End = 3 };
var plan2B = new Plan { Start = 2, End = 3 };
var plan3 = new Plan { Start = 3, End = 4 };
var planInfos = new List<PlanInfo>
{
new PlanInfo{ Name = "Test1", Plans = new []{ plan1, plan2A}.ToList() },
new PlanInfo{Name = "Test2", Plans = new []{plan2B, plan3}.ToList()},
new PlanInfo{Name = "Test3", Plans = new []{ plan3, plan2B}.ToList() }
};
var totalPlanInfos = planInfos.Count();
var commonPlans = planInfos
.SelectMany(x => x.Plans
.Select(p => new { x.Name, Plan = p }))
.GroupBy(x => x.Plan)
.Where(x => x.Count() == totalPlanInfos)
.Select(x => x.Key)
.ToList();
}
In this case even though plan 2A and 2B have the same start/end time, the group by would not group them together because they represent 2 references to 2 objects. This though would be fine:
var plan2A = new Plan { Start = 2, End = 3 };
var plan2B = plan2A;
Both point to the same reference. If you do have different references for the same plan ranges, you would need a planID then group on a PlanId. Ideally though I would check why the references don't match because they should to avoid potential errors based on assumptions of equality.
One can use Aggregate with Intersect on PlanInfo.Plans like:
var plansCommon = planInfoList.Select(p => p.Plans)
.Aggregate<IEnumerable<Plan>>((p1, p2) =>
p1.Intersect(p2, new PlanComparer()))
.ToList();
// Implement IEqualityComparer
class PlanComparer : IEqualityComparer<Plan>
{
public bool Equals(Plan x, Plan y)
{
if (x.Start == y.Start &&
x.End == y.End)
return true;
return false;
}
public int GetHashCode(Plan obj)
{
return obj.Start.GetHashCode() ^ obj.End.GetHashCode();
}
}
The Intersect will recursively apply on Plans list of each PlanInfo to provide list of Plan common across all.

Filtering from list of C# or LINQ

I am trying to filter from attachList the taxheaderID, it comes from my database which is structured as such.
public int attachmentID { get; set; }
public int headerID { get; set; }
public string uploadedfilename { get; set; }
public string originalfilename { get; set; }
public string foldername { get; set; }
Here is the code that gets data from the database:
public JsonResult GetAllAttach()
{
using (car_monitoringEntities contextObj = new car_monitoringEntities())
{
var attachList = contextObj.car_taxcomputationattachment.ToList();
return Json(attachList, JsonRequestBehavior.AllowGet);
}
}
These are my attempts:
attachList
.Select(x => x.headerID)
.Where(x => x == x)
.Take(1);
and:
attachList = attachList
.Where(al => attachList
.Any(alx => al.taxheaderID == alx.headerID
&& al.headerID == alx.headerID));
The problem is I want to parse multiple attach on a single headerID or filter them base on headerID. For example:
Problem to fix:
This is the table
Desired output:
Combined
data table:
data table
data table 2
Here is the actual solution that was made to get the output, but my coworker told me that it is not a good practice that's why I'm trying to filter it in the function itself. apologies for the trouble, thanks!
<div ng-repeat="att in attach|filter:{headerID:header.headerID}:true">
{{att.uploadedfilename}} <br />
</div>
To get attachments by Id
public JsonResult GetAllAttach(int headerId)
{
using (car_monitoringEntities contextObj = new car_monitoringEntities())
{
var attachList = contextObj.car_taxcomputationattachment
.Where(x => x.headerID == headerId)
.ToList();
return Json(attachList, JsonRequestBehavior.AllowGet);
}
}
If you want to have all data in one JSON result, then you need to create a nested view model.
Assuming you have the header id on which you want to filter in a local variable, you are almost correct
int headerIdToFind = 19;
// think of x as a local variable inside a foreach loop which
// iterates over each item in the attachList (it does not exist
// outside the where method)
// this is what you got wrong when you compared the item to itself
var filteredAttach = attachList.Where(x => x.headerId = headerIdToFind);
// if you want to select only some properties based on header id
// you can use select to project those properties
var filteredAttach = attachList.Where(x => x.headerId = headerIdToFind).
Select(x => new {x.attachmentId, x.folderName});
// based on last image, you only want to select (project) header id and the
// filename. so you do not need where (filter) at all
// you can put all the properties you need in the select clause
var filteredAttach = attachList.Select(x => new {x.headerId, x.attachmentId});
// you can enumerate the filtered attach list of convert it into a list
var filteredAttach = filteredAttach.ToList();

get collection with where clause as collection in Linq

Here is my service method:
public List<RelatedInvoiceData> GetRelatedInvoices(InvoiceSearch invoiceSearchFilters)
{
List<InvoiceInfoView> invoices = _wiseStepDbContext.InvoiceInfoView.Where(i => i.RecruiterCompanyId == _securityManager.CurrentRecruiterCompanyId).ToList();
List<RelatedInvoiceData> relatedInvoiceViewCollection = GetRelatedInvoiceCollection(invoices);
if (invoiceSearchFilters.CustomerId > 0)
{
relatedInvoiceViewCollection = relatedInvoiceViewCollection.Where(i => i.CustomerId == invoiceSearchFilters.CustomerId).ToList();
}
if (invoiceSearchFilters.VendorId > 0)
{
relatedInvoiceViewCollection = relatedInvoiceViewCollection.Where(i => i.VendorId == invoiceSearchFilters.VendorId).ToList();
}
return relatedInvoiceViewCollection;
}
here is my filterObject :
public class InvoiceSearch
{
public int[] CustomerId { get; set; }
public int[] VendorId { get; set; }
}
Previously I used where in linq for single customer Id now i want filter with multiple customerIds and multiple VendorIds.
Now I want to go with array of CustomerIds. How to write LINQ for Array in Where clause. Thanks for any help
If I understand correctly, you mean that i.CustomerId is now an array or List<>. If that's the case, then you can use the.Contains() method. Something like this should do what you want: relatedInvoiceViewCollection = relatedInvoiceViewCollection.Where(i => i.CustomerId.Contains(invoiceSearchFilters.CustomerId)).ToList();
Edit: This question may be helpful if you want to check for intersections in two arrays, which you can do in your case like this:relatedInvoiceViewCollection = relatedInvoiceViewCollection.Where(i => i.CustomerId.Intersect(invoiceSearchFilters.CustomerId).Any()).ToList();
relatedInvoiceViewCollection.Where(x => relatedInvoiceViewCollection.Contains(invoiceSearchFilters.CustomerId)).ToList();
or
relatedInvoiceViewCollection.Where(x => x.Contains(invoiceSearchFilters.CustomerId)).ToList();

How do I count the number of child collection's items using LINQ Method Syntax?

Let's say I have a schema, representing Question entities. Each question can be voted up, voted down or, of course, not voted at all - just like here in StackOverflow. I want to get the number of voteups for a given user.
int number = (from q in userDbContext.Questions
from qv in q.QuestionVotes
where qv.IsVoteUp
select qv).Count();
I want to write the same query, but using Method Syntax. How do I do this with the same example?
You can use SelectMany:
userDbContext.Questions.SelectMany(x => x.QuestionVotes).Count(x => x.IsVoteUp);
This LINQ query demonstrates how to do that using 3 level structure tree > branch > leaf as an example.
So the code below gives you the number of the leaves from all branches of all trees (all or only colored with the given color):
public class Calculator
{
public int CountAllLeafsOn(List<Tree> trees, string сolor = null)
{
// Count the leafs (all from all branches of all trees, or only if they are colored with the provided color)
return сolor == null
? trees.Sum(tree => tree.Branches.Sum(branch => branch.Leaves.Count))
: trees.Sum(tree => tree.Branches.Sum(branch => branch.Leaves.Count(leaf => leaf.Color.Equals(сolor))));
}
}
public class Tree
{
public List<Branch> Branches { get; set; }
}
public class Branch
{
public List<Leaf> Leaves { get; set; }
}
public class Leaf
{
public string Color { get; set; }
}
Hope that helps.
It must work:
int number = userDbContext.Questions
.Select(x => x.QuestionVotes.Count(y => y.IsVoteUp))
.Sum();
It will get the count of filtered child items for each parent. Then Sum() will compute the sum of these values.
You can count children using Where like this:
foreach (YourCollectionType item in datagrid.Items)
{
var children = datagrid.ItemsSource.OfType<YourCollectionType>().Where(x => x.Item1 == item.Item1 && x.Item2 == item.Item2 && x.Item3 == item.Item3 && x.Item4 == item.Item4);
item.Results = children.Count();
Trace.TraceInformation(item.Results.ToString());
}

c# Searching List<t> inside another List<T>

How would I search for a value within a List<t> inside another List<t>
i.e.
//FooInner Class
public class FooInner {
public int FooInnerId { get; set; }
public String FooValue { get; set; }
}
//FooOuter Class
public class FooOuter {
public int FooOuterId { get; set; }
public List<FooInner> FooInnerCollection { get; set; }
}
If I just wanted to find a value in the outer class
// Working code
List<FooOuter> fooOuterCollection = GetSomeData();
var tmp = fooOuterCollection.Find( f => f.FooOuterId == 2 );
But what if I wanted the FooInner Object where FooOuterId == 2 and FooInnerCollection.FooInnerId == 4 (or contains depending how you look at it).
Hopefully that makes sense.
fooOuterCollection
.Where(outer => outer.FooOuterID == 2)
.SelectMany(outer => outer.FooInnerCollection)
.FirstOrDefault(fooInner => fooInner.FooInnerId == 4);
First we filter the outer objects to only include those with Id == 2
Then we use SelectMany to flatten out the multiple InnerCollections that we may find
Finally we filter based on the inner Id == 4
You can get inner object like this-
var temp= fooOuterCollection.Where(f => f.FooOuterId == 2)
.SelectMany(f => f.FooInnerCollection)
.FirstOrDefault(fi => fi.FooInnerId == 4));
If you need outer object, you need to use Any() extension method to see if inner list contains required element -
var temp = fooOuterCollection.FirstOrDefault(f => f.FooOuterId == 2 &&
f.FooInnerCollection.Any(fi => fi.FooInnerId == 4);
You could just use LINQ's query syntax:
var results = from o in outerList
where o.FooOuterId == 2
from i in o.FooInnerCollection
where i.FooInnerId == 4
select i;

Categories

Resources