Collecting elements from an enumerable using LINQ - c#

I am attempting to collect an IEnumerable<Item> from a list of projects.
Here's the code I'm working with:
IEnumerable<Project> projects;
List<Item> itemList = db.Items.ToList();
IEnumerable<Items> item = itemList.Where(i => i.ProjectID == /*the ID of each project from projects*/)
I also tried going this route: IEnumerable<Items> item = from i in itemList where i.ProjectID == /*the ID of each project from projects*/ select i;
How do I iterate through each project to collect all associated items?

IEnumerable<Items> item = from i in itemList
where projects.Any(p => p.ID == i.ProjectId)
select i;
or using your method syntax
IEnumerable<Items> item = itemList.Where(i => projects.Any(p => p.ID == i.ProjectID));

There are so many ways to do that. Like those:
IEnumerable<Items> items = projects.SelectMany(p => itemsList.Where(i => i.ProjectID == p.ID))
or
IEnumerable<Items> items = itemsList.Where(i => projects.Any(p => p.ID == i.ProjectID))

Instead of Any(), you can also use the join:
var item = from i in itemList
join p in projects on i.ProjectId equals p.ProjectId
select i;

Related

C# LINQ single query to complete an incomplete model

I have a list of incomplete product models. Everyone is missing an owner and a price.
Can these deficiencies be filled with a single query to context? Without this foreach loop?
foreach(var item in products)
{
item.Owner = context.Products.Where(x => x.Id == item.Id).Select(x => x.ProductOwner).FirstOrDefault();
item.Price = context.Products.Where(x => x.Id == item.Id).Select(x => x.ProductPrice).FirstOrDefault();
}
I would like one query to fill in the missing fields in IEnumerable products
// build list of Id for which we need data
var idsToUpdate = products.Select(o => o.Id).ToList();
var dataById = Context.Products
// get matching entries (server side)
.Where(x => idsToUpdate.Contains(x.Id))
// get only relevant data
.Select(x => new { x.Id, x.ProductOwner, x.Price })
// ensure uniqueness (server side, free if Id is a PK)
.DistinctBy(x => x.Id)
// we will not update db
.AsNoTracking()
// now client side
.AsEnumerable()
// arrange the data
.ToDictionary(x => x.Id, x => new { x.ProductOwner, x.Price });
foreach (var item in products)
{
if (!dataById.TryGetValue(item.Id, out var data))
continue;
item.ProductOwner = data.ProductOwner;
item.Price = data.Price;
}
If data is not many then try query once, maybe?
Select all the target id
Get all products from DB
Do as you please with data(two lists) you have
ref : Using LINQ, is it possible to output a dynamic object from a Select statement? If so, how?
Since "products" is coming from external service and "context.Products" is from your DB. Why don't you join "context.Products" with "products" and return properties of "products" by applying value for "owner" and "price".
Example
var result = (from p in products
join dbP in context.Products on dbP.Id == p.Id into gj
from subDbP in gj.DefaultIfEmpty()
select new {
Owner = subDbP?.ProductOwner ?? string.Empty,
Price = subDbP?.ProductPrice ?? string.Empty,
Id = p.Id
}).ToList();
This is highly depends on the DataType of products. If this is a List, there is an method available, called ForEach.
If you are using something different, you have to write an extension method somewhere within your code. This can be something like this:
public static class EnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> values, Action<T> predicate)
{
foreach(var value in values)
{
predicate(value);
}
}
}
Afterwards, you can use this extension method like LINQ:
products.ForEach(item =>
{
var product = context.Products.Where(x => x.Id == item.Id);
item.Owner = product.Select(x => x.ProductOwner).FirstOrDefault();
item.Price = product.Select(x => x.ProductPrice).FirstOrDefault();
});
Hope this helps :-)

List except and union two list

I have two list.
I need remove items from the first list are not in the second list and add the other elements of the first.
foreach (var product in item.Products)
{
item.Products.Remove(product);
}
var newProducts = _catalogService.GetProductBaseItems(x => model.Products.Contains(x.Id))
.ToList();
foreach (var product in newProducts)
{
item.Products.Add(product);
}
You can use Enumerable.Except to find all which are in the first but not in the second. But since your Product class might not override Equals and GetHashCode by comparing the ID's you either have to do it, create a custom IEqualityComparer<Product> or use following approach:
IEnumerable<int> idsInFirstNotSecond = item.Products
.Select(x => x.Id)
.Except(newProducts.Select(x => x.Id));
var productsInFirstNotSecond = from p in item.Products
join id in idsInFirstNotSecond
on p.Id equals id
select p;
List<Product> completeListOfOldAndNew = productsInFirstNotSecond
.Concat(newProducts)
.ToList()

C#- Select specific items from a list based on partial intersection with another list (Linq + Lambda)

I have two lists:
List<objA> list1
List<objB> list2
class objA
{
string Name;
}
class objB
{
string Name;
bool SomeProp;
}
Using C# linq/lambda I want to select all the objA objects which has their Name property equals to the name property of the second obj (list1.Name == list2.Name) and check another property of objB (list2.SomeProp == true).
I recommend to use join:
from a in list1
join b in list2 on a.Name equals b.Name
where b.SomeProp
select a
Lambda syntax:
list1.Join(listb.Where(b => b.SomeProp),
a => a.Name, b => b.Name, (a,b) => a)
NOTE: Thus you will avoid enumerating list2 collection for each item in list1 (solution with Any)
To show difference between two solutions - lets see what they look like after translating linq to plain loops. First is
list1.Where(a => list2.Any(b => b.Name == a.Name && b.SomeProp))
It is equivalent to
foreach(var a in list1)
{
foreach(var b in list2)
{
if (a.Name == b.Name && b.SomeProp)
{
yield return a;
break;
}
}
}
As you can see - it has nested loop, and complexity is O(N*M). With join we have Lookup created for inner sequence:
Lookup<string, objB> lookup = list2.Where(b => b.SomeProp).ToLookup(b => b.Name);
foreach(var a in list1)
{
Grouping<string, objB> g = lookup.GetGrouping(a.Name);
if (g == null)
continue;
foreach(var b in g)
yield return a;
}
Difference here is searching in lookup - which is O(1) operation. Total complexity will be O(M +N)
This should do it:
list1.Where(a => list2.Any(b => b.Name == a.Name && b.SomeProp));

How to compare properties of a subitem in 2 lists in linq

I have the following objects:
public interface ITray
{
int OrderNo {get; set;}
IEnumerable<ITrayItem> TrayItems {get;}
}
public interface ITrayItem
{
int Aisle {get; set;}
}
Now, I have two List objects,
List<ITray> selectedTrays
List<ITray> poolTrays
What I am trying to do is for each element in poolTrays, I want to compare the Aisles that are in the list of selected trays. If all of the Aisles match, I want to add it to a list of trays to return.
I'm just tying myself in knots a bit trying to get the linq working with the querying of a property of a collection inside a list and returning the items in the list that match.
This is what I have at the moment:
List<int> selectedAisles = (from tray in selectedTrays
from item in tray.TrayItems
select item.Aisle).Distinct().ToList()
List<ITray> trayswithMatchingAisles =
(from t in poolTrays
from item in t.TrayItems
where selectedAisles.Contains(item.Aisle)
select t).ToList();
So, if I have selected Trays A, B, C with aisles in brackets
A[1,2,3] B[4,5,6] c[7,8,9]
then a poolTray with TrayItems in aisles [7,9] should return successfully, but a pool tray with TrayItems [7,8,9,10] should not be returned in the list.
At the moment, I am passing in (just) [7,9] in my poolTray list, and 2 instances of it are returned in my Linq query
Something like this should work:
List<int> selectedAisles =
(from tray in selectedTrays
from item in tray.TrayItems
select item.Aisle)
.Distinct().ToList();
List<ITray> trayswithMatchingAisles =
(from t in poolTrays
where t.TrayItems.Select(i => i.Aisle)
.All(a => selectedAisles.Contains(a))
select t)
.ToList();
But this can be simplified to:
List<ITray> trayswithMatchingAisles =
(from t in poolTrays
where t.TrayItems.Select(i => i.Aisle)
.All(a => selectedTrays
.SelectMany(s => s.TrayItems)
.Select(i => i.Aisle)
.Contains(a))
select t)
.ToList();
Or this:
List<ITray> trayswithMatchingAisles = poolTrays
.Where(t => t.TrayItems
.Select(i => i.Aisle)
.All(a => selectedTrays
.SelectMany(s => s.TrayItems)
.Select(i => i.Aisle)
.Contains(a)))
.ToList();
I think you need to use the "SelectMany" extension, this is to flat queries that return lists of lists.
For example:
var distinctSelectedItems = selectedTrays.SelectMany(t => t.TrayItems).Select(ti => ti.Aisle).Distinct();
bool success = poolTrays.SelectMany(t => t.TrayItems).All(ti => distinctSelectedItems.Contains(ti.Aisle));
You can also create a HashSet, in order to have O(1) performance, instead of O(n) for the List.Contains.
var distinctSelectedItems = new HashSet<int>(selectedTrays.SelectMany(t => t.TrayItems).Select(ti => ti.Aisle));
bool success = poolTrays.SelectMany(t => t.TrayItems).All(ti => distinctSelectedItems.Contains(ti.Aisle));
Good luck.
var result = poolTrays.Where(x => selectedTrays.Any(z=>z.TrayItems.Select(y => y.Aisle)
.Intersect(x.TrayItems.Select(k => k.Aisle))
.Count() == x.TrayItems.Count()));

Using Linq, get all items from list that are in another List<int>

I have the following scenario:
a list of int: List<int> idsOnly = new List<int>();
and another list of object that should bring all items that their ids matching the list idsOnly
var myList = db.Items.Where(item => idsOnly.Contains(item.ID.Value))
.Select(a => new { a.Title })
.ToList();
I only need to get the titles from the myList
Any help will be appreciated
Your code works but it will create the list of anonymous object, not string type
Instead of using (a => new { a.Title }, you just use a => a.Title if you just only want to get the title:
var myList = db.Items.Where(item => idsOnly.Contains(item.ID.Value))
.Select(a => a.Title).ToList();
You can use a Join
var titlesInIdList = from item in db.Items
join id in idsOnly
on item.ID.Value equals id
select item.Title;
var list = titlesInIdList.ToList();
var myList =
(from item in db.Items
where idsOnly.Contains(item.ID.Value)
select item.Title).ToList()
You can try with (If problem conversion : Convert to int your Id, if is not of int type)
var myList = db.Items.Where(item => (idsOnly.Contains(Convert.ToInt32(item.ID.Value)))).Select(a => a.Title ).ToList();
Without conversion
var myList = db.Items.Where(item => (idsOnly.Contains(item.ID.Value))).Select(a => a.Title ).ToList();
var Result = (from item in db.Items
join id in idsOnly
on item.ID.Value equals id
select new {item.Title}).ToList(); //Here U can return ToArray or ToList()

Categories

Resources