Shorten where clauses - c#

I have multiple tables which are combined with a members table on a one to one basis, the two columns I want to extract are the name from the members table and the test from the test table.
The test column is nvarchar() and has the possible entries of "P", "F", null, "", a Partial score such as (26.5) or a decimal number below 100. The following code almost does the job but it looks clunky with so many where clauses.
Also the output is ordered descending but lists double digit number after single numbers such as :
9.0, 9.0, 9.0, 91.4, 8.9, 8.8, 8.8, 86.3, 7.9...etc it seems to descend the numbers on the first digit.
var tests = from t in dc1.testLevels
join m in dc1.Members
on t.testCode equals m.memCode
where !t.Final.Equals("P") && // previous year pass
!t.Final.Equals("F")&& // previous year fail
!t.Final.Equals(null)&& // null values
!t.Final.Equals("") && // empty value
!t.Final.Contains("(") // partial value ie..(26)
select new {Member = m.MemName, Current = Convert.ToDecimal(t.Final)};
var outTest = from tr in tests
orderby tr.Current descending
select new { Member = tr.Member, Current = trCurrent};
dgv1.DataSource = outTest;

It looks like you're trying to only get the results that are decimal numbers only. Instead of blacklisting it to remove what you aren't looking for, I would instead whitelist it.
I think it would be easier to convert it to fluid notation so that you can create a Where clause that would look for only decimal numbers:
var tests = dc1.testLevels
.Where (t => {decimal dec; return decimal.TryParse(t.Final, out dec);})
.Join(dc1.Members, t => t.testCode, m => m.memCode,
(t,m) => new { Member = m.memCode, Current = Convert.ToDecimal(t.Final) })
.OrderByDescending (x => x.Current);
Also notice that this allows you to put your OrderBy clause at the end which will order your results numerically and not alphabetically like you were doing before.
EDIT
After some further thought, I worked this out in query notation like you have it. Also note how the orderby clause has changed:
decimal deci;
var tests = from t in dc1.testLevels
let IsDecimal = decimal.TryParse(t.Final, out deci)
where IsDecimal == true
join m in dc1.Members
on t.testCode equals m.memCode
orderby Convert.ToDecimal(t.Final) descending
select new {Member = m.MemName, Current = Convert.ToDecimal(t.Final)};
EDIT
Since those solution will not work in Linq-To-SQL you are limited to what you can do. The solution that the commenters to your question have given is this:
var ignoreList = new List<string>
{
"P",
"F",
null,
""
};
var tests = from t in dc1.testLevels
join m in dc1.Members
on t.testCode equals m.memCode
where !ignoreList.Contains(t.Final)
where !t.Final.Contains("(") // partial value ie..(26)
orderby Convert.ToDecimal(t.Final) descending
select new {Member = m.MemName, Current = Convert.ToDecimal(t.Final)};

Related

perform inequality comparisons to alphanumeric data in linq

Need help with linq query. I have a column which contains alphanumeric data in it and I want to extract numbers and perform mathematical comparisons. For example >,< etc.
So the data would be like: JS20, MRR12, DEEN2 etc.
I tried converting it to Int but it fails
var TheData = (from p in db.data.Where(l => Convert.ToInt16(l.TextAndNumber) <= 10)
group p by p.Names into g
select g.Key).ToList();
return Json(new { data = TheData });
Try something like this:
(from row in db.data
let digits = new String(row.TextAndNumber.Where(Char.IsDigit).ToArray())
let number = Int64.Parse(digits)
where number < 10
select row.Names
).Distinct();
You need to extract the numbers from the string before applying your conversion.
List<string> data = new List<string> { "JS20", "MRR112", "DEEN2" };
var TheData = data.Where(d => Convert.ToInt32(new string(d.Where(char.IsNumber).ToArray())) <= 10);
This returns DEEN2.

LINQ query optimization

I retrieve data from two different repositories:
List<F> allFs = fRepository.GetFs().ToList();
List<E> allEs = eRepository.GetEs().ToList();
Now I need to join them so I do the following:
var EFs = from c in allFs.AsQueryable()
join e in allEs on c.SerialNumber equals e.FSerialNumber
where e.Year == Convert.ToInt32(billingYear) &&
e.Month == Convert.ToInt32(billingMonth)
select new EReport
{
FSerialNumber = c.SerialNumber,
FName = c.Name,
IntCustID = Convert.ToInt32(e.IntCustID),
TotalECases = 0,
TotalPrice = "$0"
};
How can I make this LINQ query better so it will run faster? I would appreciate any suggestions.
Thanks
Unless you're able to create one repository that contains both pieces of data, which would be a far preferred solution, I can see the following things which might speed up the process.
Since you'r always filtering all E's by Month and Year, you should do that before calling ToList on the IQueryable, that way you reduce the number of E's in the join (probably considerably)
Since you're only using a subset of fields from E and F, you can use an anonymous type to limit the amount of data to transfer
Depending on how many serialnumbers you're retrieving from F's, you could filter your E's by serials in the database (or vice versa). But if most of the serialnumbers are to be expected in both sets, that doesn't really help you much further
Reasons why you might not be able to combine the repositories into one are probably because the data is coming from two separate databases.
The code, updated with the above mentioned points 1 and 2 would be similar to this:
var allFs = fRepository.GetFs().Select(f => new {f.Name, f.SerialNumber}).ToList();
int year = Convert.ToInt32(billingYear);
int month = Convert.ToInt32(billingMonth);
var allEs = eRepository.GetEs().Where(e.Year == year && e.Month == month).Select(e => new {e.FSerialNumber, e.IntCustID}).ToList();
var EFs = from c in allFs
join e in allEs on c.SerialNumber equals e.FSerialNumber
select new EReport
{
FSerialNumber = c.SerialNumber,
FName = c.Name,
IntCustID = Convert.ToInt32(e.IntCustID),
TotalECases = 0,
TotalPrice = "$0"
};

Linq intersect or join to return items from one collection that have matching properties to another?

I have the following scenario: Two lists of different Types which happen to contain 3 matching properties (in reality, the names are not the same as they are from different systems/database tables, but their contents match).
In my example I have named the properties the same just to make it easier!
I'd like to get a list of Prefix+Number+Suffix for accounts where there is a matching item in lookup (NOTE: Lookup can contain the same values multiple times - the rest of the properties in the objects are different)
This is the code I am currently using, but it feels clunky. Is there a cleaner, better way to acheive the same result? I tried "Contains()" but wasn't sure how to restrict to all three properties.
var accounts = new List<Account>{
new Account{Prefix="001", Number="10101", Suffix="666"},
new Account{Prefix="001", Number="10202", Suffix="777"},
new Account{Prefix="001", Number="10303", Suffix="777"},
new Account{Prefix="002", Number="20101", Suffix="666"},
new Account{Prefix="002", Number="20101", Suffix="777"}
};
var lookup = new List<Lookup>{
new Lookup{Prefix="001", Number="10101", Suffix="666"},
new Lookup{Prefix="001", Number="10101", Suffix="666"},
new Lookup{Prefix="002", Number="20101", Suffix="666"},
new Lookup{Prefix="001", Number="10101", Suffix="666"},
};
var match = ((from a in accounts
select a)
.Intersect(from l in lookup
from a in accounts
where l.Prefix == a.Prefix &&
l.Number == a.Number &&
l.Suffix == a.Suffix
select a)
).Select(a => string.Format("{0}{1}{2}", a.Prefix, a.Number, a.Suffix));
You can use the following code to get the match:
var match = (from a in accounts
select new { P = a.Prefix, N = a.Number, S = a.Suffix })
.Intersect(from l in lookup
select new { P = l.Prefix, N = l.Number, S = l.Suffix })
.Select(t => string.Format("{0}{1}{2}", t.P, t.N, t.S));;
You make use here of the automatically generated equality operators on anonymous types.
Why not just simply:
match = (from l in lookup
from a in accounts
where l.Prefix == a.Prefix &&
l.Number == a.Number &&
l.Suffix == a.Suffix
select string.Format("{0}|{1}|{2}", l.Prefix, l.Number, l.Suffix))
.Distinct();
Try this:
from a in accounts
join l in lookup on
new
{
a.Prefix, a.Number, a. Suffix
}
equals
new
{
l.Prefix, l.Number, l. Suffix
}
into gls
select a
I would not work directly with the tables but use a database view in cases like this. Create a view which performs the union for you and returns a normalised data structure, like so:
CREATE VIEW ExampleView
AS
SELECT
Prefix = a.Prefix,
Number = a.Number,
Suffix = a.Suffix
FROM
FirstTable AS a
UNION ALL
SELECT
Prefix = l.Prefix,
Number = l.NumberWithDifferentName,
Suffix = l.WeirdlyNamedSuffix
FROM
SecondTable AS l
Then you can run a simple select on that view instead of performing complex database logic in your application, where it does not really belong anyway:
SELECT Prefix, Number, Suffix FROM ExampleView; /* or obviously the LINQ equivalent */
Here a link to an article on that matter (why to use views): http://www.tdan.com/view-articles/5109. To cite the part that explains why its better practice to let the database do what it does best, not the application:
Developers find having to work with normalized data structures awkward and time-consuming, since it involves coding complex SQL queries that join data from multiple tables. [...] "refactoring" non-normalized data structures into normalized ones after the fact is always extremely difficult and labor-intensive, and sometimes isn't even possible (because data in non-key fields must be "refactored" into key fields, and data in these fields may have missing or incorrect values).
why dont you try join between them
from a in accounts
join l in lookup
on
new { a.Prefix, a.Number, a. Suffix}
equals
new { l.Prefix, l.Number,l. Suffix}
select a;

C# LINQ Query

I have some data in a List of User defined types that contains the following data:
name, study, group, result, date. Now I want to obtain the name, study and group and then a calculation based onthe result and date. The calculation is effectively:
log(result).where max(date) minus log(result).where min(date)
There are only two dates for each name/study/group, so the result from the maximum data (log) minus the result from the minumum date (log). here is what I have tried so far with no luck:
var result =
from results in sortedData.AsEnumerable()
group results by results.animal
into grp
select new
{
animal = results.animal,
study = results.study,
groupNumber = results.groupNumber,
TGI = System.Math.Log(grp.Select(c => c.volume)
.Where(grp.Max(c=>c.operationDate)))
- System.Math.Log(grp.Select(c => c.volume)
.Where(grp.Min(c => c.operationDate)))
};
Anybody any pointers? Thanks.
It isn't entirely clear how the grouping relates to your problem (what sense does it make to extract a property from a range variable after it has been grouped?), but the part you're having difficult with can be solved easily with MaxBy and MinBy operators, such as the ones that come with morelinq.
var result = from results in sortedData.AsEnumerable()
group results by results.animal into grp
select new
{
animal = grp.Key,
study = ??,
groupNumber = ??,
TGI = Math.Log(grp.MaxBy(c => c.operationDate).volume)
- Math.Log(grp.MinBy(c => c.operationDate).volume)
};
Otherwise, you can simulate these operators with Aggregate, or if you don't mind the inefficiency of sorting:
var result = from results in sortedData.AsEnumerable()
group results by results.animal into grp
let sortedGrp = grp.OrderBy(c => c.operationDate)
.ToList()
select new
{
animal = grp.Key,
study = ??,
groupNumber = ??,
TGI = sortedGrp.Last().volume - sortedGrp.First().volume
};
You have a few syntax problems, you cannot use the results parameter after your into grp line. So my initial attempt would be to change your statement like so
var result =
from results in sortedData.AsEnumerable()
group results by new
{
Animal = results.animal,
Study = results.study,
GroupNumber = results.groupNumber
}
into grp
select new
{
animal = grp.Key.Animal,
study = grp.Key.Study,
groupNumber = grp.Key.GroupNumber,
TGI = System.Math.Log(grp.OrderByDescending(c=>c.operationDate).First().volume)
- System.Math.Log(grp.OrderBy(c=>c.operationDate).First().volume)
};

Is there an efficient way in LINQ to use a contains match if and only if there is no exact match?

I have an application where I am taking a large number of 'product names' input by a user and retrieving some information about each product. The problem is, the user may input a partial name or even a wrong name, so I want to return the closest matches for further selection.
Essentially if product name A exactly matches a record, return that, otherwise return any contains matches. Otherwise return null.
I have done this with three separate statements, and I was wondering if there was a more efficient way to do this. I am using LINQ to EF, but I materialize the products to a list first for performance reasons.
productNames is a List of product names (input by the user).
products is a List of product 'records'
var directMatches = (from s in productNames
join p in products on s.ToLower() equals p.name.ToLower() into result
from r in result.DefaultIfEmpty()
select new {Key = s, Product = r});
var containsMatches = (from d in directMatches
from p in products
where d.Product == null
&& p.name.ToLower().Contains(d.Key)
select new { d.Key, Product = p });
var matches = from d in directMatches
join c in containsMatches on d.Key equals c.Key into result
from r in result.DefaultIfEmpty()
select new {d.Key, Product = d.Product ?? (r != null ? r.Product: null) };
If you have a small to medium-sized list in-memory, take a look at LiquidMetal and for phonetic matches, the Soundex algorithm to rank the closest matches.
If you are using SQL Server, look into Full-Text Search, which is what Stack Overflow uses. Otherwise, here is how I implemented a keyword-based search.

Categories

Resources