C# Find best matching element / Simplify query to List - c#

I ask myself how I can simplify something like this
var myList = new List<MyObject>
pulic MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value)
{
MyObject item = null;
item = myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value && x.Prop3 == Prop3Value);
if(item != null)
{
return item;
}
item = myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value);
if(item != null)
{
return item;
}
item = myList.Find(x => x.Prop1 == Prop1Value);
// Doesn't matter if its null
return item;
}
I'm sure LINQ offers a solution, but I'm not able to find it :)
Thank you.

Technically, you can simplify the current code into
pulic MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value) {
return
myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value && x.Prop3 == Prop3Value)
?? myList.Find(x => x.Prop1 == Prop1Value && x.Prop2 == Prop2Value)
?? myList.Find(x => x.Prop1 == Prop1Value);
}
But doing Find (scaning the entire list) can be a costly operation, if it's your case you can find the best match in one loop only:
public MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value) {
MyObject result1 = null;
MyObject result2 = null;
foreach (MyObject item in myList) {
if (item.Prop1 == Prop1Value) {
result1 = item;
if (item.Prop2 == Prop2Value) {
result2 = item;
if (item.Prop3 == Prop3Value)
return item;
}
}
}
return result2 ?? result1;
}

Try this:
public MyObject FindBestMatching(int Prop1Value, int Prop2Value, int Prop3Value)
{
return myList.FirstOrDefault(x => (x.Prop1 == Prop1Value && x.Prop2 == Prop2Value && x.Prop3 == Prop3Value)
|| (x.Prop1 == Prop1Value && x.Prop2 == Prop2Value)
|| (x => x.Prop1 == Prop1Value));
}

Related

How to replace IF statements for dictionary? (C#, Linq)

I have this equality comparer of a SampleObject:
public bool Equals(SampleObject x, SampleObject y)
{
if (x == null)
{
return y == null;
}
if (y == null)
{
return false;
}
if (!string.Equals(x.SomeId, y.SomeId))
{
return false;
}
if (x.EventsList == null)
{
return y.EventsList == null;
}
if (y.EventsList == null)
{
return false;
}
return x.EventsList.OrderBy(e => e)
.SequenceEqual(y.EventsList.OrderBy(e => e));
}
What I would like to know is if there is a way to replace all those IF clauses for a dictionary?
It is not possible with a dictionary, but with a list. A dictionary has no order, hence you can't guarantee that your checks are performed in the right order. I used a list of tuples, where the first item is the condition, and the second item is the return value. Your code will be the following:
public bool Equals(SampleObject x, SampleObject y)
{
var checks = new List<(Func<bool>,Func<bool>)>
{
(() => x == null, () => y == null),
(() => y == null, () => false),
(() => !string.Equals(x.SomeId, y.SomeId), () => false),
(() => x.EventsList == null, () => y.EventsList == null),
(() => y.EventsList == null, () => false)
};
foreach(var entry in checks)
{
if(entry.Item1.Invoke())
{
return entry.Item2.Invoke();
}
}
return x.EventsList.OrderBy(e => e)
.SequenceEqual(y.EventsList.OrderBy(e => e));
}
But I strongly recommend to stay with your original version, because from my point of view the readability strongly decreases with this approach. Sometimes a classic sequence of if statements is much more appropriate then any fancy LINQ or whatever stuff.
Well, I doubt if Dictionary is of any help here, but you can slightly simplify the routine into
public bool Equals(SampleObject x, SampleObject y) {
if (ReferenceEquals(x, y))
return true;
else if (x == null || y == null)
return false;
// From now on, both x and y are not null
//TODO: to avoid such constructions, do not let collections be null, but empty
if (x.EventList == null || y.EventList == null)
return x.EventList == y.EventList;
// From now on, both x.EventList and y.EventList are not null
return string.Equals(x.SomeId, y.SomeId) &&
x.EventList.OrderBy(e => e).SequenceEquals(y.EventList.OrderBy(e => e));
}
I don't think a dictionary is of any help here. You can replace all the if-statements by a simple expression:
return
x == null && y == null ||
x != null && y != null &&
String.Equals(x.SomeId, y.SomeId) &&
(x.EventsList == null && y.EventsList == null ||
x.EventsList != null && y.EventsList != null &&
x.EventsList.OrderBy(e => e)
.SequenceEqual(y.EventsList.OrderBy(e => e));
Note that because of C#'s short-circuit evaluation, the expression has to be evaluated only partially in most cases.

LINQ using Contains with entity

Am using ASP.NET MVC 5 to create an Application for billing, now i have thing function which receive a filter object with different variables, am having a problem with contains when i search, what am i doing wrong
public static List<Quote> getCustomerQuotes(QuoteFilter filter)
{
using (var db = new AppDBContext())
{
var q = db.Quotes.Where(u => u.entryDate > 0); ;
if (filter.type != null)
{
q = q.Where(u => u.quoteType == filter.type);
}
if (filter.only_permitable != null)
{
q = q.Where(u => !Values.NON_PERMITABLE_QUOTES.Contains(u.quoteType));
}
if (filter.quote_status != null)
q = q.Where(u => u.quote_status == (int)filter.quote_status);
if (filter.quotenumber != null)
{
q = q.Where(u => u.quote_number.Contains(filter.quotenumber));
}
if (filter.permitnumber != null)
q = q.Where(u => u.permit_number.Contains(filter.permitnumber));
if (filter.permit_status != null)
q = q.Where(u => u.permit_status == (int)filter.permit_status);
if (filter.quoteId != null)
q = q.Where(u => u.Id == (int)filter.quoteId);
if (filter.customer_id != null)
q = q.Where(u => u.customer_id == (int)filter.customer_id);
q = q.OrderByDescending(u => u.Id);
FileLogger.Log("getCustomerQuotes", q.ToString());
return q.ToList();
}
}
When i call the function and pass quotenumber, the contains doesnt search, it returns nothing
You have to evaluate your expression, before you apply the OrderByDescending.
q = q.Where(u => u.quote_number.Contains(filter.quotenumber)).ToList();
This should be happen also to the rest places.
Is quote number alpha-numeric? If yes, as Contains is case sensitive can you try comparison by first turning source and target to same case ? like
q = q.Where(u => u.quote_number.ToLower().Contains(filter.quotenumber.ToLower()));
Cheers
Ok, am answering my own question after finding a solution or i may call it a hack
public static List<Quote> getCustomerQuotes(QuoteFilter filter)
{
using (var db = new AppDBContext())
{
var q = db.Quotes.Where(u =>
(filter.type != null ? u.quoteType == filter.type : u.quoteType > 0) &&
(filter.only_permitable != null ? !Values.NON_PERMITABLE_QUOTES.Contains(u.quoteType) : u.permitType > 0) &&
(filter.quote_status != null ? u.quote_status == filter.quote_status : u.quote_status > -100) &&
(!string.IsNullOrEmpty(filter.quotenumber) ? u.quote_number.Contains(filter.quotenumber) || u.groupName.Contains(filter.quotenumber) : u.quoteType > 0) &&
(!string.IsNullOrEmpty(filter.permitnumber) ? u.permit_number.Contains(filter.permitnumber) || u.groupName.Contains(filter.permitnumber) : u.quoteType > 0) &&
(filter.permit_status != null ? u.permit_status == filter.permit_status : u.quoteType > 0) &&
(filter.quoteId != null ? u.Id == filter.quoteId : u.Id > 0) &&
(filter.customer_id != null ? u.customer_id == filter.customer_id : u.customer_id > -1)
).OrderByDescending(u => u.Id);
//FileLogger.Log("getCustomerQuotes", q.ToString());
return q.ToList();
}
}
i dont know why it didn't work the first time but now it works.

C# search by many parameters Entity Framework 6.0

In my table I have 4 columns: BAZAR_TEXT, CATEGORY, COUNTY, PRICE and I would like to search records by parameters from textboxes: search, category, county, priceFrom, priceUntil
var searchResult = db.bazar.Include(c => c.images).Where(da => da.BAZAR_TEXT.Contains(search) || search == null);
var categoryResult = searchResult.Where(x => x.CATEGORY == category || category == null);
var countyResult = categoryResult.Where(x => x.DISTRICT == county || county == null);
var priceFromResult = countyResult.Where(x => x.PRICE >= priceFrom);
var priceUntilResult = priceFromResult.Where(x => x.PRICE <= priceUntil);
return View(priceUntilResult.ToList().ToPagedList(page ?? 1, 10));
and need to return list to view.
If I search only by searchResults all is OK
var searchResult = db.bazar.Include(c => c.images).Where(da => da.BAZAR_TEXT.Contains(search) || search == null);
return View(searchResult.ToList().ToPagedList(page ?? 1, 10));
but If I add other results list is null.
Note: all parameters from textboxes can be null, but columns in table are not null.
var searchResult = db.bazar.Include(c => c.images).AsQueryable();
if(search != null){
searchResult = searchResult.Where(da => da.BAZAR_TEXT.Contains(search));
}
if(category != null){
searchResult = searchResult.Where(x => x.CATEGORY == category);
}
if(county != null){
searchResult = searchResult.Where(x => x.DISTRICT == county);
}
if(priceFrom != null){
searchResult = searchResult.Where(x => x.PRICE == priceFrom);
}
if(priceUntil != null){
searchResult = searchResult.Where(x => x.PRICE <= priceUntil);
}
return View(searchResult.ToList().ToPagedList(page ?? 1, 10));
There is difference between null and string empty. So if you search with empty textbox:
WebEntities db = new WebEntities();
var searchResult = db.bazar.Include(c => c.images);
if (!string.IsNullOrEmpty(search))
{
searchResult = searchResult.Where(da => da.BAZAR_TEXT.Contains(search));
}
if (!string.IsNullOrEmpty(category))
{
searchResult = searchResult.Where(x => x.CATEGORY == category);
}
if (!string.IsNullOrEmpty(county))
{
searchResult = searchResult.Where(x => x.DISTRICT == county);
}
if (priceFrom != null)
{
searchResult = searchResult.Where(x => x.PRICE >= priceFrom);
}
if (priceUntil != null)
{
searchResult = searchResult.Where(x => x.PRICE <= priceUntil);
}
return View(searchResult.ToList().ToPagedList(page ?? 1, 10));
You should reverse the order of condition evaluation
.Where(x => x.CATEGORY == category || category == null)
to
.Where(x => category == null || x.CATEGORY == category )

C# use left outer join in LINQ

This is my method:
public IEnumerable<Web_Vendor> GetSaleBrands()
{
using (var _db = new LuxedecorContext())
{
IQueryable<Web_Vendor> web_vendors = _db.Web_Vendor.
Where(x =>
(_db.Web_Promotion.Where(z => z.VendorID != string.Empty
&& z.Static == true && z.Percent != 0 &&
(x.WebPromotion.Expires >= DateTime.Now || x.WebPromotion.Expires == null))
.Select(r => r.VendorID).Contains(x.VendorID))
||
(_db.NavItems.Where(y => x.WebPromotion.VendorID == y.SC1
&& !(y.Promotion == "" || y.Promotion == null)
&& (y.PromotionStart <= DateTime.Now) && (y.PromotionEnd >= DateTime.Now || y.PromotionEnd == null))
.Select(g => g.SC1).Contains(x.WebPromotion.VendorID)))
.Where(x => x.Active == true).OrderBy(x => x.NameExtra)
.ThenBy(x => x.Sequence);
var a = web_vendors.ToList(); // 16 values
var b = web_vendors.Include(x => x.WebPromotion).ToList(); // 13 values
}
throw new NotImplementedException();
}
I need to Include nullable values to my collection of Web_Vendors. I know, it's famous question, but I need to return collection of Web_Vendors instead of collection of dynamic types. Is it possible?

Handling null result

I have a db request that could return null:
Pony MyPony = db.Pony.Where(p => p.PonyOwnerId == user.UserId).First();
If there is no row in my db, there is an error message.
How to accept an empty query?
You can use FirstOrDefault
Pony myPony = db.Pony.Where(p => p.PonyOwnerId == user.UserId).FirstOrDefault();
if (myPony == null)
{
..
}
You can write:
Pony myPony = db.Pony.Where(p => p.PonyOwnerId == user.UserId).FirstOrDefault();
if( myPony != null ) {
// Do something
}
var MyPony = db.Pony.FirstOrDefault(p => p.PonyOwnerId != null && p.PonyOwnerId == user.UserId);
or
var MyPony = db.Pony.Where(p => p.PonyOwnerId != null && p.PonyOwnerId == user.UserId).FirstOrDefault();
or
if (db.Pony.FirstOrDefault(p => p.PonyOwnerId != null && p.PonyOwnerId == user.UserId) != null)
{
//Do stuff
}

Categories

Resources