I have two generic lists where I want to run a couple of Linq queries to find out:
Are any of lists A items found in list B
Are all of lists A items found in list B
Here are the lists:
var ListA = new List<long>()
var ListB = new List<MyObject>()
MyObject is defined as:
public class MyObject
{
public long ItemId { get; set; }
// ... Other stuff...
}
I am trying to determine two things (two queries): 1. Do any of the longs in ListA match any of the MyObject.ItemId in ListB? And 2. Can all of the longs in ListA be found in ListB?
ListA and ListB can be different lengths. For number 2, I would need all of ListA's items found in ListB, but not vice-versa. I hope this makes sense.
Thanks,
-Scott
First, you only care about the ItemIds in ListB, so:
var bIDs = ListB.Select(x => x.ItemId);
To answer the first part of your question, I would approach this by finding the intersection of the two lists (the set of all items they share). If it has at least one element in it, then there is overlap between the two.
var sharedIds = ListA.Intersect(bIDs);
if (sharedIds.Any())
// list A contains at least one ItemID which ListB contains
As for the second part, you want to see if list A is a subset of list B. Searching for this, Stack Overflow presents a clean solution:
if (!ListA.Except(bIDs).Any())
// yes, list A is a subset of list B
This snippet works because ListA.Except(bIDs) finds the elements that ListA has that bIDs doesn't. If this is empty, then ListA doesn't contain anything that bIDs doesn't. Thus, everything that is in ListA is also in bIDs.
Here's an example: A = {1, 2}; B = {1, 2, 3}. A is a subset of B. A.Except(B) gives you an empty set - B has both 1 and 2, so can't be in the resulting list, and there isn't anything left in B. So when A is a subset of B, A.Except(B).Any() gives false, as there are no elements in the result; so we obviously negate it if we want to handle that case.
For completeness, if we swap A and B round such that A is not a subset of B: A = {1, 2, 3}; B = {1, 2}, then A.Except(B) gives {3}. It can't contain 1 or 2, because B contains 1 and 2. But B doesn't contain 3, so A.Except(B) can contain it. As {3} contains one element, it isn't empty, so A.Except(B).Any() is true. Negated, it is false if A is not a subset of B.
My explanation is a little terse; if you want to look things up further (and I recommend you do - a little set theory can go a long way), A.Except(B) is LINQ's name for the set difference, or relative set complement. Wikibooks has a decent introduction to set theory if you are so inclined.
var value1 =
(
from itemA in ListA
where ListB.Any(itemB => itemB.ItemID == itemA)
select item
).Count();
var value2 = value1 == ListA.Count();
To just test the conditions, assuming you extract a list of ItemIds into listB:
bool inListA = listA.Any(x => listB.Contains(x));
bool allInListB = listA.All(x => listB.Contains(x));
To test in place without extracting a separate list if ItemIds
bool inListA = listA.Any(x => listB.Select(b => b.ItemId).Contains(x));
bool allInListB = listA.All(x => listB.Select(b => b.ItemId).Contains(x));
If you need to answer all three questions at the same time then it's likely that a pure LINQ solution won't be optimal, since the individual queries will each need to perform the same intersection operation. Do the intersection once, and then use that result to answer your three questions:
var tempSet = new HashSet<long>(ListA);
int uniqueAItemCount = tempSet.Count;
// 2b. "I would need all of ListA's items found in ListB, but not vice-versa."
tempSet.IntersectWith(ListB.Select(x => x.ItemId));
// tempSet now contains all items from ListA also found in ListB
// we can use this result to answer the other two questions...
// 1. "Do any of the longs in ListA match any of the MyObject.ItemId in ListB?"
bool anyAFoundInB = tempSet.Count > 0;
// 2a. "Can all of the longs in ListA be found in ListB?"
bool allAFoundInB = tempSet.Count == uniqueAItemCount;
Related
I have 2 lists of Objects. I am using Intersect LINQ operator to any match between 2 lists and setting it as a variable. In this newly formed list, I am only able to get the matched value.
I've looked into SelectMany, but don't think it's the right solution for this.
var CommonList = iMIScustomersList.Select(s1 => s1.Company).ToList().Intersect(zendeskCompaniesList.Select(s2 => s2.name).ToList()).ToList();
I want to get the matched value and from the matched value in list 2, I want to get the id property as well. For example s2.id in zendeskCompaniesList. Currently using Intersect I am only getting the matched value and no other properties from that match.
What you need is Join, instead of Intersect.
var CommonList = iMIScustomersList.Join(zendeskCompaniesList,
cust=>cust.Company,
comp=>comp.Name,
(cust,comp)=> new {Customer=cust,Company=comp}).ToList();
For example,
var iMIScustomersList = Enumerable.Range(1,10)
.Select(x=> new Customer{Name = $"Name{x}", Company=$"Company{x}"});
var zendeskCompaniesList = Enumerable.Range(5,10)
.Select(x=> new Company{Name=$"Company{x}", Location = $"Location{x}"});
var CommonList = iMIScustomersList.Join(zendeskCompaniesList,
cust=>cust.Company,
comp=>comp.Name,
(cust,comp)=> new {Customer=cust,Company=comp}).ToList();
Sample Output
For example I have 3 lists (or more):
List1:
[{store:"store1",item:"item1",price:10},{store:"store1",item:"item2",price:5},{store:"store1",item:"item4",price:100},{store:"store1",item:"item10",price:10}]
List2:
[{store:"store2",item:"item1",price:15},{store:"store2",item:"item2",price:10},{store:"store2",item:"item10",price:110}]
List3:
[{store:"store3",item:"item1",price:5},{store:"store3",item:"item2",price:10},{store:"store3",item:"item10",price:100},{store:"store3",item:"item100",price:1}]
As you can see It's like 3 stores with different items and prices. Not all stores have all items so I would like to make a list by comparing the lists and finding the objects that contain "item1" for example and then choose the cheaper price. And also to compare the lists 1 by one (list 1 with list 2 , list 1 with list 3, list 2 with 1 and list 2 with 3). Do I make any sense?
Any answer is appreciated.
I've tried some things but I just cant understand it (and its for 2 stores):
var result = (from l1 in store1list join l2 in store2list on l1.Symbol equals l2.Symbol orderby l1.Symbol select new
{
store = l1.store,
price = l1.price,
item = l1.item
}).ToList();
You may Union your lists and then GroupBy item, and select ordering each group with price and taking the first one (cheapest) from each group.
var result = List1.Concat(List2).Concat(List3).GroupBy(x => x.item)
.Select(g => g.OrderBy(x=> x.price).First()).ToList();
I try to count the number of child elements for each parent where the parent id is in a list if ints. But the count is not correct. Is it not possible to make a query like this? What is the correct way of doing this?
List<int> ids = [1, 2, 3];
var counts = (from d in db.Parent where ids.Contains(d.Id) select d.Child.Count()).ToList();
Assuming Child is a collection, because you are attempting to count it.
Try the following:
var counts = db.Parent.Where(x => ids.Contains(x.Id)).SelectMany(x => x.Child).Count();
"Count" will work on an IEnumerable. However it seems you are trying to work with a collection of IEnumerables. In these situations use "SelectMany" so that all the resulting collections are joined into one single collection.
The query does exact what I want, sorry for that. My problem was that the ids list was not sorted and that somehow made the query to not sort the output as the ids list was sorted. By sort the ids list (ids.Orderby(id)) this question was solved.
A list of counts and nothing else may offer sufficient information, but often some information on the containing entity will be useful (if only to prevent confusion):
List<int> ids = [1, 2, 3];
var counts = (from d in db.Parent where ids.Contains(d.Id)
select new
{
Parent = d.Name,
ChildCount = d.Child.Count()
}).ToList();
I understand how you can get the count of a List where each value might have some number. For example if I wanted the number of times a value was equal to 1 in "ListA" I would do the following:
int countOf1=ListA.Count(x => x ==1);
If I have several Lists, and I want to get the count of ListB where ListA is equal to some value, how can I modify the above command to do so?
To illustrate the question, I will show a picture in a spreadsheet of what this would look like. In the pic, you can see ListA and ListB will always have the same number of values, as I want information on each row to be an 'observation' of sorts.
I could do this with a loop, but I would like my script to be a bit simpler. I also realize for this example, I could just get the count of ListA where the value is equal to 1. But I will have some other things I want to do in the future like compute averages, percentiles or other aggregations on ListB, where ListA is equal to some value.
Is this possible to do?
Picture of Lists
So it looks like what you want to do is join list A onto list B. You don't specify in your question how the two lists should be joined but based on your image I am guessing you want to do it by the index - i.e. A[0] to B[0], A[1] to B[1], etc. If that is the case you can use Zip. Then if you wanted to take the average of B where A equals 1, for example, you could do the following:
ListA
.Zip(
ListB,
(a, b) => new { A = a, B = b })
.Where(x => x.A == 1)
.Average(x => x.B);
This question already has an answer here:
How to express IN (list of argument) in a query LINQ?
(1 answer)
Closed 8 years ago.
How can I write this where condition in Linq?
var result = from i in context.ItemsFullDetails where i.iditem in (1,2,3,4)
where condition includes a list of id's where which be compared with the iditem column.
I am getting an error if I write in the above way.
Try this:
/*Hold here the items you want to filter by*/
var filterCriteria = new List(){1,2,3,4};
/*Get results as Ienumerable<int>*/
var result = context.ItemsFullDetails.Where(i => filterCriteria.Contains(i.iditem));
If you want to get results as list / array, you can use result.ToList() / result.ToArray()
Try this approach:
//create filter list
int[] productList = new int[] { 1, 2, 3, 4 };
// check your search item exists in the filter list
var myProducts = from p in db.Products
where productList.Contains(p.ProductID)
select p;
Reference:
What is LINQ equivalent of SQL’s "IN" keyword
You have a couple options. The first, as I see many people have suggested on this and similar posts, is to create a collection external from the LINQ query and use Contains against that:
int[] items = new[] { 1, 2, 3, 4 };
var result = from i in context.ItemsFullDetails where items.Contains(i.iditem)
But that said, if your example here is more or less what you're actually looking to do and you do have a constant set of possibilities, I'd be more tempted to write out the logic:
var result = from i in context.ItemsFullDetails where i.iditem == 1
|| i.iditem == 2
|| i.iditem == 3
|| i.iditem == 4
Now, this is nearly as pretty. I agree. But this will be the most efficient way to do what you're after, and I don't think it's unreadable enough--particularly to experienced developers who are inherently used to logic like this--to warrant a lack of efficiency. Now, obviously, this approach only works if you have a compile-time collection set. But since you did in your example, I'm assuming you will in real life. So that's what I'd do. Then you don't have to risk looping through the entire collection for every element.
Or if you need a dynamic set of conditions:
List<int> items = new List<int>() { 1, 2, 3, 4 };
items.Add(...);;
var result = from i in context.ItemsFullDetails where items.Contains(i.iditem)