Selecting earliest date using linq/lambda - c#

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();

Related

Using two Linq query in a single method

As shown in the below code, the API will hit the database two times to perform two Linq Query. Can't I perform the action which I shown below by hitting the database only once?
var IsMailIdAlreadyExist = _Context.UserProfile.Any(e => e.Email == myModelUserProfile.Email);
var IsUserNameAlreadyExist = _Context.UserProfile.Any(x => x.Username == myModelUserProfile.Username);
In order to make one request to database you could first filter for only relevant values and then check again for specific values in the query result:
var selection = _Context.UserProfile
.Where(e => e.Email == myModelUserProfile.Email || e.Username == myModelUserProfile.Username)
.ToList();
var IsMailIdAlreadyExist = selection.Any(x => x.Email == myModelUserProfile.Email);
var IsUserNameAlreadyExist = selection.Any(x => x.Username == myModelUserProfile.Username);
The .ToList() call here will execute the query on database once and return relevant values
Start with
var matches = _Context
.UserProfile
.Where(e => e.Email == myModelUserProfile.Email)
.Select(e => false)
.Take(1)
.Concat(
_Context
.UserProfile
.Where(x => x.Username == myModelUserProfile.Username)
.Select(e => true)
.Take(1)
).ToList();
This gets enough information to distinguish between the four possibilities (no match, email match, username match, both match) with a single query that doesn't return more than two rows at most, and doesn't retrieve unused information. Hence about as small as such a query can be.
With this done:
bool isMailIdAlreadyExist = matches.Any(m => !m);
bool isUserNameAlreadyExist = matches.LastOrDefault();
It's possible with a little hack, which is grouping by a constant:
var presenceData = _Context.UserProfile.GroupBy(x => 0)
.Select(g => new
{
IsMailIdAlreadyExist = g.Any(x => x.Email == myModelUserProfile.Email),
IsUserNameAlreadyExist = g.Any(x => x.Username == myModelUserProfile.Username),
}).First();
The grouping gives you access to 1 group containing all UserProfiles that you can access as often as you want in one query.
Not that I would recommend it just like that. The code is not self-explanatory and to me it seems a premature optimization.
You can do it all in one line, using ValueTuple and LINQ's .Aggregate() method:
(IsMailIdAlreadyExist, IsUserNameAlreadyExist) = _context.UserProfile.Aggregate((Email:false, Username:false), (n, o) => (n.Email || (o.Email == myModelUserProfile.Email ? true : false), n.Username || (o.Username == myModelUserProfile.Username ? true : false)));

C# Linq multi-word search

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

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();

Linq: Date less that x, but next record date is greater that x

I got records in database. Each record has field "Date".
For given date=x i need to find records that Date value is less that x, but next record date is greater of x.
Example:
id date
--------------
1 12.03.2013
2 15.03.2013
3 18.03.2013
now, I got X=16.03.2013, and i need a LINQ that return this record:
2 15.03.2013
BUT! for the X=15.03.2014 it should return nothing (because there is record with smaller date, but next record has exactly the same date as X)
How can i do this?
The simplest approach IMO is just to find the record that it would find, and check the date afterwards:
var result = db.Table
.Where(x => x.Date <= target.Date)
.OrderByDescending(x => x.Date)
.FirstOrDefault();
if (result != null && result.Date == target.Date)
{
result = null;
}
Or you could do it all in the query using a secondary Where clause after filtering to a single result:
var result = db.Table
.Where(x => x.Date <= target.Date)
.OrderByDescending(x => x.Date)
.Take(1)
.Where(x => x.Date != target.Date)
.FirstOrDefault();
Note that this doesn't work if all values are less than x (so there's no "next" record at all). I haven't yet worked out a way to handle that.
If all values can be less than x then you can use
var candidate = dates.Where(x => x.Date < target.Date)
.OrderByDescending(x => x.Date)
.FirstOrDefault();
var next = dates.Where(x => x.Date >= target.Date)
.OrderBy(x => x.Date)
.FirstOrDefault();
return (candite != null && next != null && next.Date != target.Date) ? candidate : null

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.

Categories

Resources