LINQ where clause- compare collection of strings - c#

I have a variable number of "filters" to apply to an entity collection, and these filters are stored in a List. Right now, I'm doing the following:
IQueryable<Items> items = SharedContext.Context.Items.GetAll();
//This list is dynamic, but shown static here for simplicity
IEnumerable<Items> filterList = new List<string>(){"new", "old", "current"};
IEnumerable<Item> items_temp = new List<Item>();
foreach (string type in filterList)
{
var temp = items.Where(i => i.Type.ToLower().Trim() == type.ToLower().Trim());
items_temp = items_temp.Union(temp);
}
items = items_temp.AsQueryable();
Unfortunately, this causes a huge performance issue. I know somebody out there has a better solution... What do you guys think?
EDIT
Running my application with the code above takes around 30 seconds to execute, but if do the following:
items.Where(item => item.Type.ToLower().Trim() == "new" ||
item.Type.ToLower().Trim() == "old" ||
item.Type.ToLower().Trim() == "current");
my application executes in about 4 seconds. Can anyone think of a solution that can match this performance or at least fill me in on why the results are so drastically different? FYI, I'm binding my data to a grid with multiple grids nested inside... a small improvement can go a long way.

Sounds like what you want is this:
var items = SharedContext.Context.Items.GetAll();
IEnumerable<string> filterList = new List<string>(){"new", "old", "current"};
var filteredItems = items.Where(i => filterList.Contains(i.Type.ToLower()));
If you're having issues with this, you might want to try using an array instead:
string[] filterList = new string[] {"new", "old", "current"};
var filteredItems = items.Where(i => filterList.Contains(i.Type.ToLower()));
Update: Here's another strategy which generates a filter expression dynamically:
var filterList = new[] { "new", "old", "current" };
var param = Expression.Parameter(typeof(Item));
var left =
Expression.Call(
Expression.Call(
Expression.PropertyOrField(param, "Type"),
typeof(string).GetMethod("ToLower", Type.EmptyTypes)),
typeof(string).GetMethod("Trim", Type.EmptyTypes));
var filterExpr = (Expression<Func<Item, bool>>)Expression.Lambda(
filterList
.Select(f => Expression.Equal(left, Expression.Constant(f)))
.Aggregate((l, r) => Expression.OrElse(l, r)),
param);
var filteredItems = items.Where(filterExpr);

You could do a join to filter the items:
IEnumerable<Items> filterList = new List<string>(){"new", "old", "current"};
IQueryable<Items> items = SharedContext.Context.Items.GetAll();
var filteredItems = from i items
join f in filterList
on i.Type.ToLower().Trim() equals t.ToLower().Trim()
select i;

Related

How can I use linq to search inside a list of objects

I am new in linq and I want to use it in a list without use a foreach. How can I return a list from a list of objects List<House> where house ha swimming pool.
Class Houses
{
Int Id,
bool HasSwimmingPool
...
}
All you need is Where method, which filters collection based of given predicate:
var results = source.Where(x => x.HasSwimmingPool).ToList();
Additional ToList() call makes the results List<House> instead of IEnumerable<House>.
You can achieve the same using syntax-based query:
var results = (from h in source
where h.HasSwimmingPool
select h).ToList();
That is simple:
var yourCollection = new List<Houses>();
var housesThatHasASwimmingPool = yourCollection.Where(s => s.HasSwimmingPool);
try this:
var swimmngHomes = listOfHouses.
Where( h => h.HasSwimmingPool == true);
List<Houses> housesWithPools = oldHouses.Where(x => x.HasSwimmingPool== true);

Linq query change from List<string> to List<T> where clause

I have a linq query that works when it I had a list of a single value now that I change to having a List that has several properties I need to change the where clause
So this works:
List<string> etchList = new List<string>();
etchList.Add("24");
var etchVect = (from vio in AddPlas
where etchList.Any(v => vio.Key.Formatted.Equals(v))
let firstOrDefault = vio.Shapes.FirstOrDefault()
where firstOrDefault != null
select new
{
EtchVectors = firstOrDefault.Formatted
}).ToList();
However I have a new hard coded list (which will represent incoming data:
List<ExcelViolations> excelViolations = new List<ExcelViolations>();
excelViolations.Add(new ExcelViolations
{
VioID = 24,
RuleType = "SPACING",
VioType = "Line-Line",
XCoordinate = 6132,
YCoordinate = 10031.46
});
So the NEW Linq query looks like this, but is obviously will not work as
AddPlas is a List and so using this other list of excelviolations, I wish to have it do where on each one of the properties in the excelviolations list
var etchVect = (from vio in AddPlas
where excelViolations.Any(vioId => vio.Key.Formatted.Equals(vioId))
let firstOrDefault = vio.Shapes.FirstOrDefault()
select new
{
EtchVectors = firstOrDefault.Formatted
}).ToList();
Now, since this is a list within a list, I would like to do something like add in each of the properties
so for example:
where excelViolations.VioID.Any(vioId => vio.Key.Formatted.Equals(vioId))
However that is not possible, but you see that I'm trying to access the property of VioID that is in the excelViolations and match it to the Key which is in vio list
Just change this line
where excelViolations.Any(vioId => vio.Key.Formatted.Equals(vioId))
to
where excelViolations.Any(excelVio => vio.Key.Formatted.Equals(excelVio.VioID))
then i thought it will works

How to get first object out from List<Object> using Linq

I have below code in c# 4.0.
//Dictionary object with Key as string and Value as List of Component type object
Dictionary<String, List<Component>> dic = new Dictionary<String, List<Component>>();
//Here I am trying to do the loping for List<Component>
foreach (List<Component> lstComp in dic.Values.ToList())
{
// Below I am trying to get first component from the lstComp object.
// Can we achieve same thing using LINQ?
// Which one will give more performance as well as good object handling?
Component depCountry = lstComp[0].ComponentValue("Dep");
}
Try:
var firstElement = lstComp.First();
You can also use FirstOrDefault() just in case lstComp does not contain any items.
http://msdn.microsoft.com/en-gb/library/bb340482(v=vs.100).aspx
Edit:
To get the Component Value:
var firstElement = lstComp.First().ComponentValue("Dep");
This would assume there is an element in lstComp. An alternative and safer way would be...
var firstOrDefault = lstComp.FirstOrDefault();
if (firstOrDefault != null)
{
var firstComponentValue = firstOrDefault.ComponentValue("Dep");
}
[0] or .First() will give you the same performance whatever happens.
But your Dictionary could contains IEnumerable<Component> instead of List<Component>, and then you cant use the [] operator. That is where the difference is huge.
So for your example, it doesn't really matters, but for this code, you have no choice to use First():
var dic = new Dictionary<String, IEnumerable<Component>>();
foreach (var components in dic.Values)
{
// you can't use [0] because components is an IEnumerable<Component>
var firstComponent = components.First(); // be aware that it will throw an exception if components is empty.
var depCountry = firstComponent.ComponentValue("Dep");
}
You also can use this:
var firstOrDefault = lstComp.FirstOrDefault();
if(firstOrDefault != null)
{
//doSmth
}
for the linq expression you can use like this :
List<int> list = new List<int>() {1,2,3 };
var result = (from l in list
select l).FirstOrDefault();
for the lambda expression you can use like this
List list = new List() { 1, 2, 3 };
int x = list.FirstOrDefault();
You can do
Component depCountry = lstComp
.Select(x => x.ComponentValue("Dep"))
.FirstOrDefault();
Alternatively if you are wanting this for the entire dictionary of values, you can even tie it back to the key
var newDictionary = dic.Select(x => new
{
Key = x.Key,
Value = x.Value.Select( y =>
{
depCountry = y.ComponentValue("Dep")
}).FirstOrDefault()
}
.Where(x => x.Value != null)
.ToDictionary(x => x.Key, x => x.Value());
This will give you a new dictionary. You can access the values
var myTest = newDictionary[key1].depCountry
Try this to get all the list at first, then your desired element (say the First in your case):
var desiredElementCompoundValueList = new List<YourType>();
dic.Values.ToList().ForEach( elem =>
{
desiredElementCompoundValue.Add(elem.ComponentValue("Dep"));
});
var x = desiredElementCompoundValueList.FirstOrDefault();
To get directly the first element value without a lot of foreach iteration and variable assignment:
var desiredCompoundValue = dic.Values.ToList().Select( elem => elem.CompoundValue("Dep")).FirstOrDefault();
See the difference between the two approaches: in the first one you get the list through a ForEach, then your element. In the second you can get your value in a straight way.
Same result, different computation ;)
There are a bunch of such methods:
.First .FirstOrDefault .Single .SingleOrDefault
Choose which suits you best.
var firstObjectsOfValues = (from d in dic select d.Value[0].ComponentValue("Dep"));
I would to it like this:
//Dictionary object with Key as string and Value as List of Component type object
Dictionary<String, List<Component>> dic = new Dictionary<String, List<Component>>();
//from each element of the dictionary select first component if any
IEnumerable<Component> components = dic.Where(kvp => kvp.Value.Any()).Select(kvp => (kvp.Value.First() as Component).ComponentValue("Dep"));
but only if it is sure that list contains only objects of Component class or children

Using LINQ, how to select conditionally some items but when no conditions select all?

I want to select elements from myCollection using myFilters for filtering:
var myFilters = new List<string> {"111", "222"};
var myCollection = new List<SomeClass> {
new SomeClass ("111"),
new SomeClass ("999")
};
from filter in myFilters
from item in myCollection
where item.Name == filter
select item
would return the "111" item.
However, if myFilters is empty I want to return all the items from myCollection.
var myFilters = new List<string> ();
var myCollection = new List<SomeClass> {
new SomeClass ("111"),
new SomeClass ("999")
};
// Here's where I'm lost...
from filter in myFilters
from item in myCollection
where item.Name == filter
select item
would return all items ("111" and "999").
If these collections are going to be sizable, then I recommend using a join. It would look something like this:
var result =
myFilters.Any() ?
from item in myCollection
join filter in myFilters
on item.Name equals filter into gj
where gj.Any()
select item
: myCollection;
Opportunities for using joins are easily overlooked. This join approach will outperform the contains approach when the lists are remotely large. If they're small and performance is acceptable, then use whichever seems the clearest.
var result = myCollection
.Where(i => (!myFilters.Any() || myFilters.Contains(i.Name)));
The best you're going to be able to do is project the filters into SomeClass. Something like:
var results = myCollection.Any() ?
myCollection.Where(item => myFilters.Contains(item.Name)) :
myFilters.Select(f => new SomeClass (f));
How about this?
var myFilters = new List<string> ();
var myCollection = new List<SomeClass> {new SomeClass ("111"), new SomeClass ("999")};
// Here's where I'm lost...
from filter in myFilters
from item in myCollection
where item.Name == filter || !myFilters.Any()
select item
Selecting from two collections performs a join based on your where clause. The join condition above says join on item.Name equal to filter OR select it if there are no filters available.
Try this:
var result = myCollection.Where(s => !myFilters.Any() ||
myFilters.Contains(s.Name));
//EDIT: commented these lines..based on comment by #Servy
//var result = myCollection.Where(s => myFilters.Count == 0 ||
// myFilters.Contains(s.Name));
Maybe it would be better to count filter collection only once:
bool isFilterEmpty = !myFilters.Any();
//bool isFilterEmpty = myFilters.Count == 0; //...or like this
var result = myCollection.Where(s => isFilterEmpty ||
myFilters.Contains(s.Name));
EDIT
I'd even say that the answer by #itsme86 is correct, but, I guess, he has confused your collections. So his answer should look somehow like this:
var results = myFilters.Any()
? myCollection.Where(item => myFilters.Contains(item.Name))
: myCollection;

Generate LINQ or Lambda Expression for Multiple columns based on the user selection of checkboxes

I have a few checkboxes on top of my webpage which correspond to the columns of my Table. Like there is a column studentId then there will be a checkbox studentId and so on. I want to write such a linq/lamda expression for List which will filter the on the basis of the checkboxes selected. For example if selects studentId and studentType checkbox then linq/lambda expression should bring all the rows matching the selection.
example:
If studentId and studentType checked then:
foreach (Child c in SomeList)
{
if (chkStudentId.checked)
{
List.FindAll (h=> h.StudentId == c.studentId);
}
if (chkStudentType.checked)
{
List.FindAll (h => h.StudentType == c.studentType)
}
}
}
I can't figure out how am I going to write such a code that if user selects multiple checkboxes, the query should compare to all the columns and bring the values based only on the checkboxes checked. The above is only static and does not help. please help. Thanks.
Expression trees are a great help if you want your query to be totally dynamic. But if the number of checkboxes is static you can also choose for the following solution:
var students = <your list of students>.AsQueryable();
if ( chkStudentId.checked)
{
students = students.Where(s => s.StudentId == c.StudentId);
}
if (chkStudentType.checked))
{
students = students.Where(s => s.StudentType== h.StudentType);
}
In such a way you can combine the where clauses in a dynamic way.
Disclaimer: I'm fairly new to this and there is probably much better ways to do this. Any feedback is higlhy appreciated.
Note that this method has no error/null checking. Since it uses reflection, you should profile it if you plan to use it in production.
public static IEnumerable<T> Filter<T>(IEnumerable<T> collection, Dictionary<string, object> filters) {
var type = typeof (T);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var queryable = collection.AsQueryable();
var instance = Expression.Parameter(type, "instance");
var expressions = new Stack<Expression>();
foreach (var filter in filters) {
var propertyName = filter.Key;
var property = properties.FirstOrDefault(x => x.Name == propertyName);
if (property == null)
continue;
var left = Expression.Property(instance, property);
var right = Expression.Constant(filter.Value, property.PropertyType);
var expr = Expression.Equal(left, right);
expressions.Push(expr);
}
Expression call = null;
Expression previousExpression = null;
while(expressions.Count > 0) {
var expr = expressions.Pop();
if(previousExpression == null) {
previousExpression = expr;
call = expr;
} else {
var and = Expression.AndAlso(previousExpression, expr);
call = and;
previousExpression = and;
}
}
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { queryable.ElementType },
queryable.Expression,
Expression.Lambda<Func<T, bool>>(call, new[] { instance }));
return queryable.Provider.CreateQuery<T>(whereCallExpression);
}
It overates all filters and tries to find a matching property. If it does find a property, it creates a EqualExpression which compares the actual value and the value you want to filter by. It then creates a MethodCallExpression which is passed to the query provider.
Theese expressions are then combined. I think the part with the stack is wrong, and that there is a better way to do it.
Usage:
var persons = new List<Person> {new Person {Name = "Alex", Age = 22}, new Person {Name = "Jesper", Age = 30}};
var filters = new Dictionary<string, object>();
filters.Add("Name", "Alexander Nyquist");
var results = Filter(persons, filters);
Since it's building expressions, it does works with Linq 2 Sql (tested) and probably Entity Framework. Linq 2 sql produces the following query:
SELECT [t0].[Id], [t0].[Name], [t0].[Email]
FROM [dbo].[Persons] AS [t0]
WHERE [t0].[Name] = #p0
-- #p0: Input VarChar (Size = 8000; Prec = 0; Scale = 0) [Alexander Nyquist]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
Hope this helps.
From your description of your problem it seemed fairly straight forward that you have a list of predicates, that are enabled or disable based on the check boxes, and then you want to aggregate them to create a single composite filter. Easy!
Your predicates look like this:
h => h.StudentId == c.studentId
h => h.StudentType == c.studentType
But because of the check boxes, you really want them to look like this:
h => chkStudentId.Checked ? h.StudentId == c.studentId : true
h => chkStudentId.Checked ? h.StudentType == c.studentType : true
You're effectively extending the predicates to include the check box. Here's a function that does that:
Func<CheckBox, Func<Student, bool>, Func<Student, bool>> extend =
(cb, p) =>
s => cb.Checked ? p(s) : true;
Now you can write your list of predicates like this:
var predicates = new Func<Student, bool>[]
{
extend(chkStudentId, h => h.StudentId == c.studentId),
extend(chkStudentType, h => h.StudentType == c.studentType),
// etc
};
Next LINQ has an easy way to turn a list of somethings into a single something:
Func<Student, bool>
predicate =
predicates.Aggregate((a, p) => s => a(s) && p(s));
Now you just have to execute this code to get your filtered list:
var filtered = SomeList.Where(predicate);
Now anytime any of the check boxes change you just need to enumerate the filtered collection and you'll get your filtered results.
Expression Trees can do what you want, but they are rather complicated. They allow you to dynamically build the linq query.
http://msdn.microsoft.com/en-us/library/bb882637.aspx
http://msdn.microsoft.com/en-us/library/bb397951.aspx
The Dynamic Linq Library may also be of use. I haven't personally used it though.
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
The solution that worked for me was a bit modification of what Wouter de Kort
suggest and was like this:
var students = StudentList.AsQueryable();
foreach (Student sc in GroupingList)
{
students = students.Where(s => (chkStudentId.Checked? h.StudentId.Trim() == sc.StudentId.Trim() : true) && (chkStudentType.Checked? h.StudentType.Trim() == sc.StudentType.Trim() : true) );
}

Categories