C# Linq .Find() return many results - c#

I am trying to create a simple search function for my application. I am using Linq's .Find() method to search through a list of objects. It all works very well, the only problem I'm currently having is that I only get the first result. I know for a fact that there are more than one results to be had, but I only get one. Here is my code:
case 5: {
//Search for Price
Product searchResult = tempList.Find(x => x.getPrice() == searchPrice);
if (searchResult != null) {
//Present Result
searchTable.Rows.Add(convertIDValue(searchResult.getProductID()), searchResult.getTitle(), searchResult.getYear(), searchResult.getAmount(), searchResult.getPrice());
}
else {
MessageBox.Show("No product with that price", "0 results");
}
break;
}
I thought I could change Product searchResult into List<Product> searchResults in order to get a list of Products, then loop through that list. But that gave me an error saying:
Cannot implicitly convert type '.Product' to 'System.Collections.Generic.List<.Product>
Is there any way to get Linq's .Find() to return multiple results?

Use Where() and ToList() to get all objects, matching the condition into a List
replace
Product searchResult = tempList.Find(x => x.getPrice() == searchPrice);
with
List<Product> searchResult = tempList.Where(x => x.getPrice() == searchPrice).ToList();

There is a FindAll method for that purpose:
List<Product> products = tempList.FindAll(x => x.getPrice() == searchPrice);

Find() searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire List.
You need to use FindAll() instead.

Microsoft explains the "Find()" method :"Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire List."
I would suggest you to use this Where() method from Linq extension.
Don't forget to import "using System.Linq" in your current class.

Product searchResult =
means you are declaring one element. The thing you need is a collection of products, like:
IEnumerable<product> searchResult =
The easiest way to do it is to change Find() to where():
IEnumerable<product> searchResult = tempList.Where(x => x.getPrice() == searchPrice);
this will create some collection of product's. It will be easier to maintain as a list, so:
list<product> searchResult = tempList.Where(x => x.getPrice() == searchPrice).toList();
Read about IEnumerable interface :)

Related

How to filter a List<T> if it contains specific class data?

I need help with filtering list data in c#.
I got 3 class named Product.cs, Storage.cs and Inventory.cs.
public class Storage{
string StorageId;
string Name;
}
public class Inventory{
string InventoryId;
string StorageId;
string ProductId;
}
I got the filled List<Storage> mStorages, List<Product> mProduct and List<Inventory> mInventories.
I have trouble to print mStorages that contain with specific productId that only can be obtained from mInventories.
So, I tried this:
List<Storage> mFilteredStorage;
for(int i=0;i<mStorages.Count;i++){
if(mStorages[i] contain (productId from inventories)){
mFilteredStorage.add(mstorages[i]);
}
So I can get mFilteredStorage that contains specific product from inventories. (in inventories there are lot of product id).
What should I do to get that filteredStorage? I tried to use list.contains() but it only return true and at last there are duplicated storage at mFilteredStorage.
Really need your help guys. Thanks in advance.
I suggest you to read about lambda-expressions, that is what you are looking for.
mFilteredStorage.AddRange(mStorages.Where(storage => inventories.Any(inventory => inventory.productId == storage.productId)).ToList());
This returns you a list with your filtered conditions. So right after Where you iterate over each item in your list, I called this item storage. (you can name those what ever you want to) Then we iterate over your object inventories with another lambda expression. This, the second lambda expression, returns either true if any of inventories's productIds match the productId of the current iterating object of mStorages or false if they don't match.
So you once the productIds match you can imagine the code like the following:
mStorages.Where(storage => true);
And once the result of the second lambda expression is true, storage will be added to the IEnumerable you will get as a result of the Where method.
Since we get an IEnumerable as return, but we want to add those Storage objects to mFilteredStorage, I convert the IEnumerable to a list, by:
/*(the return object we get from the `Where` method)*/.ToList();
You can use LINQ to accomplish your goal. Since Storage has no ProductId, the query will match by StorageId.
var filteredStoragesQry =
from storage in mStorages
where inventories.Any(inventory => inventory.StorageId == storage.StorageId)
select storage;
mFilteredStorages = filteredStoragesQry.ToList();
This query is for LINQ to objects, but it will also work in Entity Framework, when you replace mStorages and inventories by the respective DbSet objects from the context.
mStorages.Join(mInventories, x => x.StorageId, y => y.StorageId, (x, y) => new { Storage = x, ProductId = y.ProductId})
.Where(z => z.ProductId == "specificProductId").Select(z => z.Storage).ToList()
I ended with this code.
mFilteredStorage = tempStorage.GroupBy(s => s.Id).Select(group => group.First()).ToList()
This code is what I want to show.

LINQ - query does not return an object from a list of objects

I have a list of objects and I need to execute a LINQ query on it to find some specific object.
class MyClass
{
int id;
int someOtherIdbutNotUnique;
}
var ls = myObjectList.Where(x => x.id==specificId
&& x.someOtherIdbutNotUnique == someOtherSpecificId)
.FirstOrDefault();
But this query does not return a MyClass object. And also, how should I change the query to get list of MyClass objects that fulfill the given condition. At the same time, would like to know if there is any good LINQ tutorial where I can start from the scratch.
LINQ extension methods take predicates to filter the list by. Where, First, FirstOrDefault, Single, SingleOrDefault (to name but a few) all take the same predicate.
Some Lambda expression to filter the list by.
public class MyClass
{
public int Id {get;set;}
public int Other {get;set;}
}
// myClasses is a populated list <-- this needs to be checked.
var result = myClasses.FirstOrDefault(x => x.Id == specificId && x.Other == specificOther);
Result should now contain either a single MyClass instance or a null.
If you omit the OrDefault() then your code will throw an error if it can't find an Instance that matches the predicate.
If the predicate returns several items, then First will pick the first item. If you swap First to Single and the predicate returns several items, it will throw an exception.
Things to check
The list you are performing the query against has a list of instances.
The variables specificId and specificOther have values that exist in the list. The last thing you want to do is be wondering why it isn't returning anything, when actually it is doing exactly what you asked for and that the reason for failing is the values you were using to query with where wrong.

Lambda expression to return one result for each distinct value in list

I currently have a large list of a class object and I am currently using the following lambda function to return elements that meet the condition.
var call = callList.Where(i => i.ApplicationID == 001).ToList();
This will return a list of objects that all have an id of 001.
I am now curious as to what different ApplicationIDs there are. So I would like a lambda function that will look into this list and return a list where all the element have a different ApplicationID but only fetches one of those.
If i understand your question you can try:
var list = callList.GroupBy(x => x.ApplicationID).Select(x => x.First()).ToList();
So if you have a list like:
AppID:1, AppID:1, AppID:2, AppID:2, AppID:3, AppID:3
Will return:
AppID:1 AppID:2 AppID:3
You can use either First or FirstOrDefault to get back one result
var call = callList.First(i => i.ApplicationID == 001);
If no call exisrs with an ApplicationID of 001 this will throw an exception. If this may be expected consider using:
var call = callList.FirstOrDefault(i => i.ApplicationID == 001);
Here null will be returned if no such call exists and you can handle accordingly in you code.
To find out what other ApplicationId's exist you can query:
var Ids = callList.Where(i => i.ApplicationID != 001).Select(i => i.ApplicationID).Distinct();
You are saying
I am now curious as to what different ApplicationIDs there are. So I
would like a lambda function that will look into this list and return
a list where all the element have a different ApplicationID but only
fetches one of those.
I would suggest that is never something you'd actually want. You either don't care about the elements, you care about all of them, or you care about a specific one. There are few (none?) situations where you care about a random one from the list.
Without knowing about which specific one you care, I can't give you a solution for that version. Allesandro has given you a solution for the random one.
When you only care about the distinct ID's you would end up with
callList.Select(c => c.ApplicationID).Distinct()
which just gives you all ApplicationIDs.
if you care about all of them, you'd end up with
callList.GroupBy(c => c.ApplicationID)
this will give you an IEnumerable<IGrouping<String, Thingy>> (where Thingy is the type of whatever the type of elements of callList is.)
This means you now have a collection of ApplicationID -> collection of Thingy's. For each distinct ApplicationID you'll have a "List" (actually IEnumerable) of every element that has that ApplicationID
If you care for the Thingy of that - for example - has the lowest value of property Foo you would want
callList.GroupBy(c => c.ApplicationID)
.Select(group => group.OrderBy(thingy => thingy.Foo).First()))
here you first Group them by ApplicationID, and then for each list of thingies with the sample ApplicationID you Select the first one of them if you Order them by Foo
There is a way to use the Distinct in the query, but it makes you take care about the values equality. Let's assume your type is called CallClass and try:
class CallClass : IEqualityComparer<CallClass>
{
public int ApplicationId { get; set; }
//other properties etc.
public bool Equals(CallClass x, CallClass y)
{
return x.ApplicationId == y.ApplicationId;
}
public int GetHashCode(CallClass obj)
{
return obj.GetHashCode();
}
}
Now you're able to query values distinctly:
var call = callList.Distinct().ToList();

How to "query" List<T>

Say I have,
List<ExampleType> example;
Somewhere it has been populated with let's say 10 objects of ExampleType.
ExampleType looks like this:
class ExampleType
{
public string ID;
public string name;
//etc.
//Some other members..
}
Now, how would I go through the List example, and query only ExampleType objects that has the name e.g. Peter and store it in another List, like:
List<ExampleType> peterExamples = example. //some query functionality that I can't find
I have tried example.AsQueryable. But couldn't get it to work. I suspect I need some LINQ to query the list maybe?
You can get all ExampleType elements with name == "Peter" like this:
var peterExamples = example.Where(e => e.name == "Peter");
That will return an IEnumerable, if you need a List you can convert it to one by calling ToList().
var peterList = peterExamples.ToList();
This will do the trick
var peterList = example.Where(x => x.name == "Peter").ToList();
In this case the list is only of ExampleType hence no query for only ExampleType is needed. If you were faced with a List<object> though and needed to only run this on ExampleType then you could do the following
var peterList = example
.OfType<ExampleType>()
.Where(x => x.name == "Peter")
.ToList();
You can do it like this:
List<ExampleType> peterExamples = example.Where(t=>t.name == "Peter").ToList();
Or you could use query syntax:
List<ExampleType> peterExamples = (
from item in example
where item.name == "peter"
select item ).ToList();
peterExamples.Where(p=>p.name== "Peter").ToList()
peterExamples.Where(p=>p.name.Contains("Peter")).ToList();//to search for LIKE '%Peter%'
LINQ (Enumerable) extension methods can be used on any IEnumerable<T> (IQueryable<T> is a subtype of IEnumerable<T>), which includes List<T>.
If intellisense is "not working", or there are errors such as "Where/AsQueryable not recognized as .. method", then the code needs to import the extension methods (e.g. using System.Linq) so that the are available.
See LINQ (Language-Integrated Query) for further usage and information.
Here is a solution without LINQ using List<T>'s FindAll method:
var peterExamples = example.FindAll(item => item.name == "Peter");
You must also have this using statement for the Linq queries to work.
using System.Linq;

Search within a list in C# [duplicate]

This question already has answers here:
How can I find a specific element in a List<T>?
(8 answers)
Closed 6 years ago.
I have a list containing the following structure.
class CompareDesignGroup
{
string FieldId;
string Caption;
}
The list is containing items of the above structure.
Is it possible to retrieve an element of the list if FieldId is known?
You can use the Find method on the generic list class. The find method takes a predicate that lets you filter/search the list for a single item.
List<CompareDesignGroup> list = // ..;
CompareDesignGroup item = list.Find(c => c.FieldId == "SomeFieldId");
item will be null if there is no matching item in the list.
If you need to find more than one item you can use the FindAll method:
List<CompareDesignGroup> list = // ..;
List<CompareDesignGroup> result= list.FindAll(c => c.FieldId == "SomeFieldId");
You can use LINQ like this:
CompareDesignGroup result = yourList.FirstOrDefault(x => x.FieldId == yourKnownId);
If you use the FirstOrDefault method the result will be null when list doesn't contain a record with a known id. So before using result check if it is not null.
There are a plethora of methods to find an item inside a list.
LINQ provides extensions method useful to work with collections that does not provide their own search features (or when you do not have the collection itself but a generic interface like IEnumerable<T>). If you have a List<CompareDesignGroup> object and you'll work on that object you can use the methods provided by that class (specialized methods are almost always faster than LINQ methods, they know collection's internal structure and does not have to rely on many abstraction layers).
In all examples I'll perform a culture invariant and case sensitive comparison for FieldId to a hypothetical id parameter. This may not be what you need and you may have to change according to your requirements.
Using List<T>
Given a list declared as:
List<CompareDesignGroup>() list = new List<CompareDesignGroup>();
To find first element that matches the search criteria (it'll return null if no items have been found):
CompareDesignGroup item = list.Find(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
To find all the elements that matches the search criteria:
List<CompareDesignGroup> items = list.FindAll(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
Using IEnumerable<T> (or IList<T>, for example)
Given a list declared as:
IEnumerable<CompareDesignGroup> list = ...
To find first element that matches the search criteria (null if no items have been found):
CompareDesignGroup item = list.FirstOrDefault(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
To find the first element that matches the search criteria (or throw an exception if no items have been found):
CompareDesignGroup item = list.First(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
To find all elements that matches the search criteria:
IEnumerable<CompareDesignGroup> item = list.Where(
x => String.Equals(x.FieldId, id, StringComparison.InvariantCulture));
There are many LINQ extensions methods, I suggest to take a look to them all to find the one that better suits your needs.
You can use Where and then you can use FirstOrDefault. That is an LINQ expression.
var ls = new List<CompareDesignGroup>();
var result = ls.Where(a => a.FieldId=="123").FirstOrDefault();
Or SingleOrDefault to get the item you want. Like this:
var ls = new List<CompareDesignGroup>();
var result = ls.Where(a => a.FieldId=="123").SingleOrDefault()
Or even simpler:
var result = ls.SingleOrDefault(a => a.FieldId=="123");
var result2 = ls.FirstOrDefault(a => a.FieldId=="123");
Yes. Use LINQ or the built-in functionalities of List.
List<CompareDesignGroup> listData = new List<CompareDesignGroup>(); // init the data
var result = listData.Where(x=> String.Equals(x.FieldID,"FIELDID KNOWN VALUE"); // gets all data
var first = listData.FirstOrDefault(x=> String.Equals(x.FieldID,"FIELDID KNOWN VALUE"); // gets first search result

Categories

Resources