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

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;

Related

Select objects that contain specific child elements

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

Best way to find values not in two lists c#

I have two lists which I need to compare (carOptions and custOptions).
Both of these lists are in my Customer class like below:
public class CustomerDTO
{
public int CustomerId { get; set; }
//other props removed for brevity
public List<OptionDTO> SelectedCarOptions { get; set; }
public List<OptionDTO> SelectedCustomerOptions { get; set; }
}
var existingData = _myRepository.GetDataByCustomer(customerId, year);
var existingCarOptions = existingData.Select(f => f.SelectedCarOptions);
var existingCustomerOptions = existingData.Select(f => f.SelectedCustomerOptions);
existingData is an IEnumerable of CustomerDTO and then existingCarOptions and existingCustomerOptions is an IEnumerable<List<OptionDTO>>
In the method, I have a list of IEnumerable<OptionDTO> options that gets passed in. I then break this down into car or customer based on the Enum as below:
var newCarOptions = options.Where(o => o.OptionTypeID == OptionType.CarOptions);
var newCustomerOptions = options.Where(o => o.OptionTypeID == OptionType.CustomerOptions).ToList();
What I need to do is find which options are in one collection but no in the other.
I tried as below but getting an Error on the Except (I maybe need to create my own static method in that class) but I am not sure this is the best approach really?
if (existingCarOptions.Count() != newCarOptions.Count())
{
//var test = newCarOptions.Except(existingCarOptions);
}
if (existingCustomerOptions.Count() != newCustomerOptions.Count())
{
//var test2 = newCustomerOptions.Except(existingCustomerOptions);
}
Is it also quite a bit of code in the method - I could split it out into sperate methods if required but perhaps there is a simpler way I could achieve this?
I'm assuming OptionDTO has a property called Id, which uniquely identifies an option (you have to change the code accordingly if this is not the case), you may use HashSets to quickly find unmatched OptionsDTOs, while keeping the overall time cost O(n) (where n is the max number of combined options).
Create the existing options sets:
var existingCarOptions = existingData.SelectMany(d => d.SelectedCarOptions).Select(o => o.Id);
var existingCustomerOptions = existingData.SelectMany(d => d.SelectedCustomerOptions).Select(o => o.Id);
var existingCarOptionsIds = new HashSet<int>(existingCarOptions);
var existingCustomerOptionsIds = new HashSet<int>(existingCustomerOptions );
Then you extract options missing in existing sets with:
var unmatchedCarOptions = newCarOptions.Where(o => !existingCarOptionsIds.Contains(o.Id));
var unmatchedCustomerOptions = newCustomerOptions.Where(o => !existingCustomerOptionsIds.Contains(o.Id));
If you want to compare two classes you can use IEqualityComparer
public class OptionComparer : IEqualityComparer<OptionDTO>
{
public bool Equals(OptionDTO x, OptionDTO y)
{
if (object.ReferenceEquals(x, y))
{
return true;
}
if (object.ReferenceEquals(x, null) ||
object.ReferenceEquals(y, null))
{
return false;
}
return x.OptionTypeID == y.OptionTypeID ;
}
public int GetHashCode(OptionDTO obj)
{
if (obj == null)
{
return 0;
}
return obj.OptionTypeID.GetHashCode();
}
With using this you can ıdentify that What is the concept of equality for these classes.
Now we can find different values..
public List<OptionDTO>CalculateDiffBetweenLists(List<OptionDTO> left, List<OptionDTO> right){
List<OptionDTO> optionDiff;
optionDiff = left.Except(right, new OptionComparer ()).ToList();
return optionDiff ;
}

C# MongoDB LINQ: Cannot query nested list

I'm trying to query a MongoDB collection using official C# driver. Here's the object structure I've created:
IMongoDatabase db = mongoClient.GetDatabase("appdb");
IMongoCollection<MusicFile> musicfiles = db.GetCollection<MusicFile>("files");
public class MusicFile
{
public ObjectId Id { get; set; }
public string Name { get; set; }
public IList<Comment> Comments { get; set; }
}
public class Comment
{
public string Text { get; set; }
}
This is the query I'm trying to get any MusicFile objects that contains a Comment object with property Text = "Comment1":
musicfiles.AsQueryable().Where(f => f.Comments != null && f.Comments.Any(c => c.Text == "Comment1")).ToList();
I can't get this query to work, it always returns an empty list. I also tried this, which too didn't work:
musicfiles.Find(f => f.Comments.Any(c => c.Text == "Comment1")).ToList()
But, if I get the complete collection is memory, the query works:
musicfiles.Find(FilterDefinition<MusicFile>.Empty).ToList().Where(f => f.Comments != null && f.Comments.Any(c => c.Text == "Comment1")).ToList();
This seems like a very inefficient way to query. Any suggestions?
OK. I'm back at home. Try this:
var musicFilter = Builders<MusicFile>.Filter;
var commentFilter = Builders<Comment>.Filter;
var files = musicfiles
.Find(
musicFilter.NE(m => m.Comments, null)
& musicFilter.ElemMatch(m => m.Comments, commentFilter.Eq(c => c.Text, "Comment1"))
)
.ToEnumerable()
.ToList();
Note I call .ToList() because, otherwise, if you iterate through files multiple times, you'll get multiple calls to the database for the same objects.

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

Categories

Resources