C# Linq multi-word search - c#

I have the following, where searchby is a string
var products = db.Products
.Where(p => p.Category.Name == category
&& p.Active == true
&& (searchby == null || (searchby != null && p.Keywords.Contains(searchby))))
.Include(p => p.Category)
.OrderBy(p => p.Description)
.ThenBy(p => p.Name);
and would like to change it to allow searchby to contain multiple words which would filter the results to records where Keywords contains all of the words in searchby.
Thanks in advance

You can use another collection and either Enumerable.All(not sure if supported by your LINQ provider) or !Enumerable.Any:
List<string> searchby = ... (empty if there is no filter)
var products = db.Products
.Where(p => p.Category.Name == category
&& p.Active == true
&& !searchby.Any(s => !p.Keywords.Contains(s)))
.Include(p => p.Category)
.OrderBy(p => p.Description)
.ThenBy(p => p.Name);
If supported this is more readable:
&& searchby.All(s => p.Keywords.Contains(s)))

This answer assumes, that searchby is either null or an Array.
Since contains only checks for 1 item, you need to find a way to check for all items within searchby. The Enumerable.All-Method comes to mind. Here is the relevant part:
searchby.All(searchItem => p.Keywords.Contains(searchItem))
Source

Related

LinQ to get the latest group of records satisfying a condition

I have below stated 2 tables:
now I want to get the set of Child Table objects for whichever their parent table entries are latest(wr.r.t lastmodified). It should be something like....
List<Child_Table> List = ChildsList.Where(x=>x.name =="pqr" && status == "done")
.Select(x=>x.Parent.lastmodified == recent record).....ToList();
You can use GroupBy on the date, then OrderByDescending on the Key then take the First followed by SelectMany to flatten the results.
var result = ChildsList.Where(x => x.name == "pqr" && x.status == "done")
.GroupBy(x => x.Parent.lastmodified)
.OrderByDescending(g => g.Key)
.First()
.SelectMany(g => g)
.ToList();
You could use a join to accomplish it:
var results = children
.Join(parents.OrderByDescending(p => p.lastmodified).Take(1),
c => c.parent_id,
p => p.id,
(c, p) => c)
.Where(x => x.name == "pqr" && x.status == "done")
.ToList();

Selecting earliest date using linq/lambda

I have following expression
var list = techlinks.GetItems().Where(p => p.Status == 1).ToList();
I want to change this so that I want to select the earliest date value for example
var list = techlinks.GetItems().Where(p =>p.Date is earliest && p.Status == 1).ToList();
Please let me know what to insert for p.Date is earliest
Thanks
you can use OrderBy or OrderByDescending() to sort them on Date this way:
var list = techlinks.GetItems()
.Where(p => p.Status == 1)
.OrderBy(x=>x.Date).First(); // this will give oldest date
and:
var list = techlinks.GetItems()
.Where(p => p.Status == 1)
.OrderByDescending(x=>x.Date).First(); // this will give latest date
Here's another way.
var list=techlinks.GetItems()
.Where(p=> p.Status==1)
.Min(d => d.Date)
.Single();
If there might be multiple items all with the earliest date:
var list = techlinks.GetItems()
.Where(p => p.Status == 1)
.OrderBy(x=>x.Date)
.GroupBy(x => x.Date)
.First()
.ToList()
Student student = _context.Set<Student>()
.Where(p => p.StudentID == ID.Value)
.OrderBy(p => p.AddedDate)
.FirstOrDefault();
It slightly depends on what GetItems() of techLinks does, but something like that should work:
var list = techlinks.GetItems().Where(p => p.Date == techlinks.GetItems().Min(x => x.Date) && p.Status == 1).ToList();
If GetItems() method actually hits the database, you can store its result first and use it twice:
var allItems = techLinks.GetItems();
var list = allItems.Where(p => p.Date == allItems.Min(x => x.Date) && p.Status == 1).ToList();
If you only want 1 you could go with
techlinks.GetItems().Where(p => p.Status == 1).OrderBy(c => c.Date).FirstOrDefault();
otherwise I'd break it up into two statements
var date = techlinks.Min(c=>c.Date);
techlinks.GetItems().Where(p => p.Status == 1 && c.Date == date).ToList();
also be aware of how your dates are inserted, DateTime.Now will add time components so might have to do something gnarly like this
techlinks.GetItems().Where(p => p.Status == 1 && c.Date.Year == date.Year && c.Date.Month == date.Month && c.Date.Day == date.Day).ToList();

Multiple Any() in one Where() clause LINQ

Is it possible to use several Any() in one where() clause ?
For example, If I need to get favourite beers, this query will do the job:
var favouriteDrinks = drinks
.Where(f => favouriteBeers
.Any(d => d.drinkID == f.drinkID));
But what if I need to get favourite Beers and favourite Wines ? I am looking for something like this:
var favouriteDrinks = drinks
.Where(f => favouriteBeers.Any(d => d.drinkID == f.drinkID) ||
f => favouriteWines.Any(d => drinkID == f.drinkID));
var favouriteDrinks = drinks
.Where(f => favouriteBeers.Any(d => d.drinkID == f.drinkID) ||
favouriteWines.Any(d => d.drinkID == f.drinkID));
why not do it like this:
var favouriteDrinks = drinks.Where(f =>
favouriteBeers.Any(d => d.drinkID == f.drinkID)) ||
favouriteWines.Any(d => d.drinkID == f.drinkID)));
also you can use Contains:
var favouriteDrinks = drinks.Where(f =>
favouriteBeers.Contains(f.drinkID) ||
favouriteWines.Contains(f.drinkID));
You can use .Union() and .Join()
var favouriteDrinks = favouriteBeers
.Union(favouriteWines)
.Join(drinks,
x => x.drinkID,
y => y.drinkID,
(x,y) => y
);
This will work as long as favouriteBeers, and favouriteWines are of the same type.
This fixes Tim.Tang's second example I believe. There are several approaches in here, but if it comes down to "contains" vs "any", I prefer contains as the intention is much more clear to me.
var favouriteDrinks = drinks.Where(d =>
favouriteBeers.Select(b => b.drinkId).Contains(d.drinkID) ||
favouriteWines.Select(w => w.drinkId).Contains(d.drinkID));
Similarly, with the correct IEquatable interfaces implemented on your "drink" classes, you can slightly simplify too
var favouriteDrinks = drinks.Where(d =>
favouriteBeers.Contains(d) || favouriteWines.Contains(d));

How to find minimum value in a collection using linq?

I have a collection where i need to find an item with lowest price if more than 1 found the by default any should be selected and it's isPriceSelected property need to set false.
I am trying something like this.
lstBtn.Where(p => p.CategoryID == btnObj.CategoryID &&
p.IsSelected == true && p.IsPriceApplied == true)
.ToList()
.Min(m=>m.Price)
Just the select the property that you want the minimum from:
var minimumPrice = lstBtn
.Where(p => p.CategoryID == btnObj.CategoryID && p.IsSelected && p.IsPriceApplied)
.Min(p => p.Price);
If you actually want to find the item with the lowest price you need to order the collection:
var itemWithMinimumPrice = lstBtn
.OrderBy(p => p.Price)
.FirstOrDefault(p => p.CategoryID == btnObj.CategoryID && p.IsSelected && p.IsPriceApplied);
or this, could be more efficient:
var itemWithMinimumPrice = lstBtn
.Where(p => p.CategoryID == btnObj.CategoryID && p.IsSelected && p.IsPriceApplied)
.OrderBy(p => p.Price)
.FirstOrDefault();
Enumerable.FirstOrDefault returns one item or null if no item matches the predicate.
You can try something like this:
var result = lstBtn
.Where(p => p.CategoryID == btnObj.CategoryID && p.IsSelected && p.IsPriceApplied)
.OrderBy(p => p.Price)
.First();
This will first find all items which have the specified CategoryID, IsSelected, and IsPriceApplied all set to true, then sort items by Price, and return the first item with the lowest price.
Out of the box, linq can only return the actual value with Min and Max methods.
You can use a good project morelinq https://code.google.com/p/morelinq/wiki/OperatorsOverview
It has the method you need. For myself, I find this project having too many methods, so I simply cut and paste only needed from its sources.
With morelinq your code should look like:
lstBtn.Where(p => p.CategoryID == btnObj.CategoryID && p.IsSelected == true && p.IsPriceApplied==true).MinBy(m=>m.Price)
Another approach, if you also need to get all duplicates:
var lowestPriceProducts = lstBtn.Where(p => p.CategoryID == btnObj.CategoryID)
.GroupBy(p => p.Price, new { p.Price, Product = p})
.OrderByDescending(x => x.Price)
.First()
.Select(x => x.Product)
.ToList()
This query will return you a list (with only one item if there are no duplicate prices) of products with minimal price. Then you can do anything with it.

Linq to SQL / C#: How to Order By using property in cross reference table with lambda expressions

I am trying to order a list of products based on the zindex property of the cross reference table with the category table (in this case called 'Chassis'), but I get the following error:
Cannot order by type 'System.Collections.Generic.IEnumerable`1[System.Int32]'.
The following is the method I am using:
public IQueryable<E_Product> Product_GetList_ByChassisId(int chassisId)
{
return dc.E_Products
.Where(x => x.Deleted == false)
.Where(x => x.Published == true)
.Where(x => x.E_Product_Chassis
.Any(c => c.ChassisId == chassisId && c.Deleted == false))
.OrderBy(x => x.E_Product_Chassis.Select(c => c.Zindex));
}
I understand the .Select method returns an IEnumerable, but being a many-to-many relationship, x.E_Product_Chassis does not allow simple selection of its properties (e.g. x.E_Product_Chassis.Zindex).
Any help would be very appreciated...
FirstOrDefault(), Min(), Max() -- use one of these functions to select the appropriate z-index out of the set.
public IQueryable<E_Product> Product_GetList_ByChassisId(int chassisId)
{
return dc.E_Products
.Where(x => x.Deleted == false)
.Where(x => x.Published == true)
.Where(x => x.E_Product_Chassis
.Any(c => c.ChassisId == chassisId && c.Deleted == false))
.OrderBy(x => x.E_Product_Chassis.Min(c => c.Zindex));
}

Categories

Resources