I have an ObservableCollection<myClass> list. It contains a 10 objects of type MyClass.
class MyClass
{
string name;
int age;
}
If I want to find all items in list where age = 10, can I use the Contains method?
If yes how can I do this without using iteration?
var age10 = list.Where(i => i.age == 10);
Lots more queries here: http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
No, Contains only looks for a specific value, not something matching a predicate. It also only finds one value rather than every matching value.
You can, however, use Where from LINQ to Objects, assuming you're on .NET 3.5 or higher:
foreach (var item in list.Where(x => x.Age == 10))
{
// Do something with item
}
Since ObservableCollection<T> implements Collection<T> which implements IEnumerable<T>...you can use the LINQ to Object extension methods to make this simple (even though it will use iteration in the background):
var results = list.Where(m => m.age == 10);
As others have stated, using .Where(i => i.Age == 10) would be the correct way to get the result stated in the question. You would use .Contains() to check your collection for a specific instance of your class.
You can use linq to do this but not Contains
var foo = from bar in myCollection where bar.age == 10 select bar;
Related
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;
Asume we have a list of objects (to make it more clear no properties etc.pp are used)
public class SomeObject{
public bool IsValid;
public int Height;
}
List<SomeObject> objects = new List<SomeObject>();
Now I want only the value from a list, which is both valid and has the lowest height.
Classically i would have used sth like:
SomeObject temp;
foreach(SomeObject so in objects)
{
if(so.IsValid)
{
if (null == temp)
temp = so;
else if (temp.Height > so.Height)
temp = so;
}
}
return temp;
I was thinking that it can be done more clearly with LinQ.
The first approach which came to my mind was:
List<SomeObject> sos = objects.Where(obj => obj.IsValid);
if(sos.Count>0)
{
return sos.OrderBy(obj => obj.Height).FirstOrDefault();
}
But then i waas thinking: In the foreach approach i am going one time through the list. With Linq i would go one time through the list for filtering, and one time for ordering even i do not need to complete order the list.
Would something like
return objects.OrderBy(obj => obj.Height).FirstOrDefault(o => o.IsValid);
also go twice throught the list?
Can this be somehow optimized, so that the linw also only needs to run once through the list?
You can use GroupBy:
IEnumerable<SomeObject> validHighestHeights = objects
.Where(o => o.IsValid)
.GroupBy(o => o.Height)
.OrderByDescending(g => g.Key)
.First();
This group contains all valid objects with the highest height.
The most efficient way to do this with Linq is as follows:
var result = objects.Aggregate(
default(SomeObject),
(acc, current) =>
!current.IsValid ? acc :
acc == null ? current :
current.Height < acc.Height ? current :
acc);
This will loop over the collection only once.
However, you said "I was thinking that it can be done more clearly with LinQ." Whether this is more clear or not, I leave that up to you to decide.
You can try this one:
return (from _Object in Objects Where _Object.isValid OrderBy _Object.Height).FirstOrDefault();
or
return _Objects.Where(_Object => _Object.isValid).OrderBy(_Object => _Object.Height).FirstOrDefault();
Would something like
return objects.OrderBy(obj => obj.Height).FirstOrDefault(o => o.IsValid);
also go twice throught the list?
Only in the worst case scenario, where the first valid object is the last in order of obj.Height (or there is none to be found). Iterating the collection using FirstOrDefault will stop as soon as a valid element is found.
Can this be somehow optimized, so that the linw also only needs to run
once through the list?
I'm afraid you'd have to make your own extension method. Considering what I've written above though, I'd consider it pretty optimized as it is.
**UPDATE**
Actually, the following would be a bit faster, as we'd avoid sorting invalid items:
return object.Where(o => o.IsValid).OrderBy(o => o.Height).FirstOrDefault();
I'm sure there's an wasy way of doing this (I'm guessing one of the extension methods?), but am struggling to find it with Google.
Basically I have a List of custom classes; I want to select some items from this into a new List where one of the properties is equal to any value in another List.
Here's a (simplified) quick example of what I'm trying to do:
public class Job
{
public int Number;
public string ClientCompanyName;
}
List<Job> lstJobs = new List<Job>();
List<Job> lstCompare = new List<Job>();
normally I would do something like:
List<Job> lstFiltered = new List<Job>();
foreach(Job jobThis in lstCompare)
{
foreach(jobComp in lstCompare)
{
if(jobThis.Number = jobComp.Number)
{
lstFiltered.Add(jobThis);
}
}
}
Is there an extension method that neatens this last bit up into (ideally) a single line?
Cheers
You can use Intersect() for this:
http://msdn.microsoft.com/en-us/library/bb460136.aspx
Use Intersect.
For it to work with your custom comparison you either need to implement IEquatable<T> in your class or create a new class the implements IEqualityComparer<T> for your class and pass that to the overload of Intersect.
Jez,
You might be able to use the LINQ intersect function, or try:
var matches = from jobs in lstJobs
join comp in lstCompare on jobs.Number equals comp.Number
select jobs;
or LINQ syntax:
var matches = lstJobs.Join(lstCompare, jobs => jobs.Number,
comp => comp.Number, (jobs, comp) => jobs);
and here was reSharper's version based on your original loop:
List<Job> lstFiltered = (lstJobs.SelectMany(jobThis => lstCompare,
(jobThis, jobComp) => new {jobThis, jobComp})
.Where(#t => #t.jobThis.Number == #t.jobComp.Number)
.Select(#t => #t.jobThis)).ToList();
slightly verbose, but another way to skin the cat.
[edited] as had set to new list, rather than selected elements - doh
var lstFiltered = lstJobs
.Where(job => lstCompare.Any(item => item.Number == job.Number))
.ToList();
The above solution works well if the number of items in the lstCompare is small. For bigger comparison lists you may want to use some hash based collection.
var compareSet = new HashSet<int>(lstCompare.Select(item => item.Number));
var lstFiltered = lstJobs
.Where(job => compareSet.Contains(job.Number))
.ToList();
If the comparison condition is more complex or it is needed in several places, you should create a comparer class that implements IEqualityComparer<T>. Then you could use the Intersect() method as others have already suggested. However, it is not functionally identical with the above solutions. It returns only distinct elements while my solutions return all matching elements. It may be a significant difference in some applications.
My second example can be easily changed to use IEqualityComparer<T> if necessary. The HashSet<T> takes the comparer as second parameter.
I am trying to use the "List.Find" method to find a match with an element in my class. Here is an example...
class MyClass
{
String item1;
String item2;
}
List<MyClass> myClassList = new List<MyClass>();
// I am trying to find all instances of "MyClass" in the list "myClassList"
// where the element "item1" is equal to "abc"
// myClassList.Find(item => .item1 == "abc"); ?????
Anyway, I hope that explains a bit better. I am confused about the last part, so my question is: How can I use List.Find to find matches of an element in a list of classes.
Thanks and please let me know if I'm not being clear.
Your example is almost there. You should probably be using the FindAll method:
List<MyClass> results = myClassList.FindAll(x => x.item1 == "abc");
Or, if you prefer your results to be typed as IEnumerable<T> rather than List<T>, you can use LINQ's Where method:
IEnumerable<MyClass> results = myClassList.Where(x => x.item1 == "abc");
Use the where extension method:
var items = myClassList.Where(x => x.item1 == "abc");
The above snippet will return all objects with property item1 equal to "abc".
Here's the c# code that I have:
private double get806Fees (Loan loan)
{
Loan.Fee.Items class806;
foreach (Loan.Fee.Item currentFee in loan.Item.Fees)
{
if (currentFee.Classification == 806) class806.Add(currentFee);
}
// then down here I will return the sum of all items in class806
}
Can I do this using linq? If so, how? I have never used linq and i've read in several places that using linq instead of a foreach loop is faster... is this true?
Similar to some existing answers, but doing the projection in the query, to make the Sum call a lot simpler:
var sum = (from fee in loan.Items.Fees
where fee.Classification == 806
select fee.SomeValueToSum).Sum();
loan.Item.Fees.
Where(x => x.Classification == 806).
Sum(x => x.SomeValueProperty)
Whether it is faster or not is debatable. IMO, both complexities are the same, the non-LINQ version may be faster.
var q =
from currentFee in loan.Item.Fees
where currentFee.Classification == 806
select currentFee;
var sum = q.Sum(currentFee => currentFee.Fee);
private double get806Fees(Loan loan)
{
return load.Item.Fees.
Where(f => f.Classification == 806).
Sum(f => f.ValueToCalculateSum);
}
I'm assuming here that ValueToCalculateSum is also a double. If it's not then you have to convert it before it is returned.
All of the answers so far are assuming that you're summing up loan.Fees. But the code you actually posted calls Items.Add() to add each Item in loan.Fees.Items to an Items object, and it's that Items object (and not loan.Fees, which is also an Items object) that you say you want to sum up.
Now, if Items is just a simple collection class, then there's no need to do anything other than what people are suggesting here. But if there's some side-effect of the Add method that we don't know about (or, worse, that you don't know about), simply summing up a filtered list of Item objects might not give you the results you're looking for.
You could still use Linq:
foreach (Loan.Fee.Item currentFee in loan.Item.Fees.Where(x => x.Classification == 806)
{
class806.Add(currentFee);
}
return class806.Sum(x => x.Fee)
I'll confess that I'm a little perplexed by the class hierarchy implied here, though, in which the Loan.Item.Fees property is a collection of Loan.Fee.Item objects. I don't know if what I'm seeing is a namespace hierarchy that conflicts with a class hierarchy, or if you're using nested classes, or what. I know I don't like it.