Querying multiple data using System.Linq - c#

I am new to .Net Framework and am no expert in any programming language. I am just learning new things.
I want to get a list of objects from my database with Ids of 2, 6, and 9 and also all objects with Ids greater than 20. Here is my code.
int [] toRemove= { 2, 6, 9 };
var contributions = dbContext.FoodMenu
.Where(x => toRemove.Contains(x.Id))
.ToList();
As you see, the query with all Ids that are greater than 20 is not yet included. Is there a way I can add to the list with a single query only? How can I do that? Thank you so much.

Add || or condition for Id is greater than 20.
The query will fetch either the Ids within (2, 6, 9) or greater than 20.
int [] toRemove= { 2, 6, 9 };
var contributions = dbContext.FoodMenu
.Where(x => toRemove.Contains(x.Id)
|| x.Id > 20)
.ToList();

Related

Where clause only returning a subset of products matching index array

I have a list of ordered products. I also have a list of index values. I want to pull out all products whose index is in the list of indexes. Right now I'm doing this:
var indexes = new List<int> { 1, 2, 5, 7, 10 };
var myProducts = orderedProducts.Where((pr, i) => indexes.Any(x => x == i)).ToList();
However, myProducts only has 2 elements in it: The products with indexes 1 and 2. It completely misses 5, 7, and 10. What is going on? How do I fix this?
Note: orderedProducts.Count is always greater than the maximum value of the indexes list.
orderedProducts is formed from the following:
orderedProducts = productDictionary[fam.Key]
.ToList()
.OrderBy(g => g.factor)
.ToList();
where g.factor is an int, fam.Key is an int key for the product dictionary. I've checked myProducts and it is indeed a List<Product> ordered by factor ascending.
prodDictionary is a Dictionary<int?, List<Product>>.
You missed something in your testing. You said "myProducts.Count is always greater than the maximum value of the indexes list". That makes no sense considering you said "myProducts only has 2 elements". orderedProducts.Count must be < 6. Thus the problem. You are pulling out elements by simply comparing the indexes in the List. You can "fix" the problem by adding more products to the list.
void Main()
{
var indexes = new List<int> { 1, 2, 5, 7, 10 };
var orderedProducts = new List<Product>();
orderedProducts.Add(new Product());
orderedProducts.Add(new Product());
orderedProducts.Add(new Product());
orderedProducts.Add(new Product());
orderedProducts.Add(new Product());
//orderedProducts.Add(new Product());//Add this in and you will see a result at index 5
//orderedProducts.Add(new Product());
//orderedProducts.Add(new Product());//7
//orderedProducts.Add(new Product());
//orderedProducts.Add(new Product());
//orderedProducts.Add(new Product());//10
var myProducts = orderedProducts.Where((pr, i) => indexes.Any(x => x == i)).ToList();
}
public class Product
{
}
Uncomment the products and you get 5 results as expected. Index of 10 on a 0 based array.
why not just indexes.Where(i => i < orderedProducts.Count).Select(i => orderedProducts[i]).ToList();?

In linq to sql how to apply && condition with a variable size or array

I have the below table AttachmentToTagMappings
And I have an array which contains some tag id as shown below and the size can change in every execution.
int[] tagIds= new int[] {1,2,9}; //again the number of elements can change in every execution for example in next execution it may be {1,2,7,12} because the this time the user is looking for the file(s) which are/is mapped to 1,2,7 and 12
Now I want to write a linq by which I can get fileId(s) which are mapped to the above array tagIds elements. i.e. in above table FileId 201 is mapped to 1,2 and 9 so retrieve it but not 202 cause its only mapped to 1 and 2.
Solution
To solve this problem, I would make use a couple of LINQs like GroupBy, let and Except.
Here is my solution:
var grp = context.AttachmentToTagMappings.GroupBy(x => x.TagId);
int[] tagIds = new int[] {1,2,9};
var fileIds = from g in grp
let grpFileIds = g.Select(y => y.FileId)
let remFileIds = tagIds.Except(grpFileIds)
where remFileIds.ToList().Count == 0
select g.Key;
Or,
int[] tagIds = new int[] {1,2,9};
var fileIds = from attm in context.AttachmentToTagMappings
group attm by new {attm.TagId} into g
let grpFileIds = g.Select(y => y.FileId)
let remFileIds = tagIds.Except(grpFileIds)
where remFileIds.ToList().Count == 0
select g.Key;
Assumption: attm.TagId type is the same as tagId, that is, an int
Input-Output Results
{1, 2} -> 201, 202
{1, 2, 9} -> 201
{1, 2, 7} -> 202
{1, 2, 7, 9} -> nothing
Explanation
Using GroupBy you could first group your AttachmentToTagMappings based on its TagId thus creating two groups, one identified by 201 and having three attm members whose tag ids are 1, 2, and 9 respectively. Another group has key of 202 with four attm members whose tag ids are 1, 2, 7, and 12 respectively.
Using let we want to create an IEnumerable<int> grpFileIds for each group. grpFileIds contains only the group's file ids
Next, Using Except we want to omit all the tagId in tagIds which is contained in grpFileIds, resulting in the remaining remFileIds
If the remFileIds.Count is zero, it means all items in tagIds has a pair in the grpFields. Otherwise, if the remFileIds.Count is greater than zero, meaning at least there is one tagId which the group does not have
Select all keys in the group (that is 201, 202, etc) whose remFileIds.Count is zero.
Contains should be translated to an IN clause in SQL which seems to be what you want:
where tagIds.Contains(attm.TagId)
try .Contain() :
var attachments = from attm in context.AttachmentToTagMappings
where tagIds.Contains(attm.TagId)
select new { attm.FileId};
Or you can use || instead of && but i think .Contains() better less code;)
As I understand your question, you need to get a list of attachments which its tagId exists in an array.
var attachments = from attm in context.AttachmentToTagMappings
where
tagIds.Any(id=>id==attm.TagId)
select new { attm.FileId};
I think this is what you are looking for:
var ids = {1, 2, 3};
var files = context.Files.Where(file => ids.Contains(file.TagId));
if(!ids.AsEnumerable().Except(files.Select(f => f.TagId).AsEnumerable()).Any()){
return files;
}
return null;
Select all files and check that there aren't any ids that don't match to an id in the files and return list of files. If not then return null.
Syntax may not be exact as not tested.

How to use a LINQ in order to remove Min and Max value in List

I have a List such as below.
List<int> temp = new List<int> { 3, 5, 6, 8, 2, 1, 6};
I'm going to use a LINQ to remove Min and Max value in Above List.
For example, below snippet code is just example, not working.
var newValue = from pair in temp
select pair < temp.Max() && pair > temp.Min()
Hopefully, I expect the result like below ;
newValue = {3, 5, 6, 2, 6 }
I've tried Googling, but couldn't find proper example yet.
Is it workigng when I use a LINQ ? Thanks for your time.
You should be using where.
from pair in temp
where pair < temp.Max() && pair > temp.Min()
select pair
Your current approach will select whether the values are in range, not filter them. That's what the where clause is for.
Try this:-
var query = temp.Where(x => x != temp.Min() && x != temp.Max()).ToList();
Working Fiddle.
if you just need to remove the min and max values you why not just use remove()? what's the need for the pair?
List<int> temp =new List<int>() { 3, 5, 6, 8, 2, 1, 6 };
temp.Remove(temp.Max());
temp.Remove(temp.Min());
or something like this if you need to maintain temp and would rather work on a copy
temp.Sort();
temp.Skip(1).Take(temp.Count - 2).ToList();
How could you be able to add an array in Generic Collection. Also you have to convert your query result into list. Use the where clause as suggested by #Matthew Haugen.
List<int> temp = new List<int>();// {3, 5, 6, 8, 2, 1, 6}
temp.Add(3);
temp.Add(5);
temp.Add(6);
temp.Add(8);
temp.Add(2);
temp.Add(1);
temp.Add(6);
List<int> newValue = (from n in temp
where n > temp.Min() & n < temp.Max()
Select n).ToList();

How can I take only entries from even positions in List

How I can select using Linq only entries from even positions in a list ?
You can use the overload to Enumerable.Where in which the predicate includes the item's index.
var myList = new List<int>{ 1, 2, 3, 4, 5, 6 };
var evenIndexes = myList.Where( (num, index) => index % 2 == 0);

LINQ: Determine if two sequences contains exactly the same elements

I need to determine whether or not two sets contains exactly the same elements. The ordering does not matter.
For instance, these two arrays should be considered equal:
IEnumerable<int> data = new []{3, 5, 6, 9};
IEnumerable<int> otherData = new []{6, 5, 9, 3}
One set cannot contain any elements, that are not in the other.
Can this be done using the built-in query operators? And what would be the most efficient way to implement it, considering that the number of elements could range from a few to hundreds?
If you want to treat the arrays as "sets" and ignore order and duplicate items, you can use HashSet<T>.SetEquals method:
var isEqual = new HashSet<int>(first).SetEquals(second);
Otherwise, your best bet is probably sorting both sequences in the same way and using SequenceEqual to compare them.
I suggest sorting both, and doing an element-by-element comparison.
data.OrderBy(x => x).SequenceEqual(otherData.OrderBy(x => x))
I'm not sure how fast the implementation of OrderBy is, but if it's a O(n log n) sort like you'd expect the total algorithm is O(n log n) as well.
For some cases of data, you can improve on this by using a custom implementation of OrderBy that for example uses a counting sort, for O(n+k), with k the size of the range wherein the values lie.
If you might have duplicates (or if you want a solution which performs better for longer lists), I'd try something like this:
static bool IsSame<T>(IEnumerable<T> set1, IEnumerable<T> set2)
{
if (set1 == null && set2 == null)
return true;
if (set1 == null || set2 == null)
return false;
List<T> list1 = set1.ToList();
List<T> list2 = set2.ToList();
if (list1.Count != list2.Count)
return false;
list1.Sort();
list2.Sort();
return list1.SequenceEqual(list2);
}
UPDATE: oops, you guys are right-- the Except() solution below needs to look both ways before crossing the street. And it has lousy perf for longer lists. Ignore the suggestion below! :-)
Here's one easy way to do it. Note that this assumes the lists have no duplicates.
bool same = data.Except (otherData).Count() == 0;
Here is another way to do it:
IEnumerable<int> data = new[] { 3, 5, 6, 9 };
IEnumerable<int> otherData = new[] { 6, 5, 9, 3 };
data = data.OrderBy(d => d);
otherData = otherData.OrderBy(d => d);
data.Zip(otherData, (x, y) => Tuple.Create(x, y)).All(d => d.Item1 == d.Item2);
First, check the length. If they are different, the sets are different.
you can do data.Intersect(otherData);, and check the length is identical.
OR, simplt sort the sets, and iterate through them.
First check if both data collections have the same number of elements and the check if all the elements in one collection are presented in the other
IEnumerable<int> data = new[] { 3, 5, 6, 9 };
IEnumerable<int> otherData = new[] { 6, 5, 9, 3 };
bool equals = data.Count() == otherData.Count() && data.All(x => otherData.Contains(x));
This should help:
IEnumerable<int> data = new []{ 3,5,6,9 };
IEnumerable<int> otherData = new[] {6, 5, 9, 3};
if(data.All(x => otherData.Contains(x)))
{
//Code Goes Here
}

Categories