c# filter search, multiple searchboxes - c#

List<LICENSE> licenseList = context.LICENSE.Where(l => ( string.IsNullOrEmpty(licenseID) || l.LICENSE_ID.Contains(licenseID) ) && ( string.IsNullOrEmpty(hardwareID) || l.HARDWARE_ID.Contains(hardwareID) ) ).Take(10).ToList();
This is my current solution for handling more than one searchbox. Its a search function that combines 2 or more textfields in to a search. So my questions are: Is this an ok way to filter out the passed searchstrings. And how do i use it when the queries are decimals instead of strings? Thanks

Your example is perfectly fine.
With regards to it being decimals:
If it is a nullable type, then you first have to check if it has a value, and if it has that it is not the default value for a decimal which is 0.
If it is not a nullable type, then all you have to do is check that it is/is not == to 0 which is the default type. I always just check to make sure it's greater than zero, based on the assumption that a license won't be negative.
I am going to assume that it's not a nullable type as it seems to be an inline declared var, so here is a formatted example for decimal:
List<LICENSE> licenseList =
context.LICENSE.Where(l => licenseID == 0 || l.LICENSE_ID.Contains(licenseID))
.Where(l => hardwareID == 0 || l.HARDWARE_ID.Contains(hardwareID))
.Take(10)
.ToList();
Interesting thing to note, if you don't know the default type of a field, you can always do
licenseID == default(decimal)

You may try using a foreach loop on the search boxes, modifying linq, for any of those.
object[] a = {"seach", 5}; // "Data"
string[] Search = { "asdf", "asdf" }; //Search boxes
var s = a.Where(l => ((string)l).Contains(Search[0])); //first search
for (int i = 1; i < Search.Length; i++) //consecutive searches
s = s.Where(l => ((string)l).Contains(Search[i]));

Yes it looks ok. You could also use a loop somehow like this:
var query = context.LICENSE;
foreach(var item in stringVariables) {
query = query.Where(x => string.IsNullOrEmpty(item) || l.LICENSE_ID.Contains(item));
}
and stringVariables can be predefined or some algorithm to decide whether it's a search field or not.
Concerning the numbers (and assuming your column has type int, if it is a string you don't have to change anything), you probably have a nullable number, depending on your search form. So, you also have to check whether it's null or not and whether it's the right number. You may want to cast it to a string to also have the Contains function. But that all depends on your application.

Related

How to convert list of string into list of number then apply OrderByDescending on that list in C#

Consider this as the values in column Emp_code.
E1000
E1001
E9000
E4000
E1339
E10000
I'm using this code to first remove the E from all of the occurrences than convert them into number than apply OrderByDescending to the list.
var idd = db?.HRMS_EmpMst_TR?.Where(a => a.Emp_code != null)?
.Select(x=>x.Emp_code.Remove(0,1)).Select(int.Parse).OrderByDescending(y => y).First();
Can somebody help me with this code. I want to get 10000 as the answer.
Thanks for the help!
You need to
Use TrimStart('E') to remove E char from each string and parse it to
integer.
Get Max value from the processed sequence.
var input = new List<string>(){"E1000", "E1001", "E9000", "E4000", "E1339"};
var result = input
.Select(x => int.Parse(x.TrimStart('E'))) //Remove E and then parse string to integer
.Max(); //Get max value from an IEnumerable
Try Online: .NET Fiddle
You didn't say so, but I think you are working with a database, so you are working IQueryable, and not IEnumerable. This means that you can't use methods like String.TrimStart nor String.Parse.
So you have something called db, of which you didn't bother to tell us what it is. I assume it is a DbContext or something similar to access a database management system.
This DbContext has a table HRMS_EmpMst_TR, filled with rows of which I don't know what they are (please, next time give us some more information!). What I do know, that there are no null rows in this table. So your Where is meaningless.
By the way, are you not certain that db is not null?
if (db == null) return null;
After this, we know that db.HRMS_EmpMst_TR is a non null possible empty sequence of rows, where every row has a string column EmpCode. Every EmpCode starts with the character E followed by a four digits number. You want the EmpCode with the largest number.
string largestEmpCode = db.HRMS_EmpMst_TR
.OrderByDescending(row => row.EmpCode)
.Select(row => row.EmpCode)
.FirstOrDefault();
You get the string E9000, or null, if the table is empty. If you want 9000 just remove the first character and parse. What do you want if the table is empty?
if (largestEmpCode != null)
{
int largestEmpCodeValue = Int32.Parse(largestEmpCode.SubString(1));
}
else
{
// TODO: handle empty table.
}
There is room for improvement
If you are certain that every EmpCode is the character E followed by a four digit number, and you want to do calculations with this number, consider to change the EmpCode column to an integer column, without the E. This is a one time action, and it will make future calculations much easier.
Database column:
int EmpCodeValue;
LINQ to get the largest EmpCodeValue:
int largestEmpCodeValue = db.HRMS_EmpMst_TR
.Select(row => row.EmpCodeValue)
.Max();
If other parts of your application really need an "E followed by four digits", you can always make an extension method. I don't know what HRMS_EmpMst_TR are, let's assume it is a table of EmpMst
public string GetEmpCode(this EmpMst empMst)
{
return String.Format("E{0,04}", empMst.EmpCode);
}
I'm not sure about the ,04 part. You'll have to look it up, how to convert integer 4 to string "0004"
Usage:
List<EmpMst> fetchedEmpMsts = ...
string firstEmpCode = fetchedEmpMsts[0].GetEmpCode();
Or:
var result = db.HRMS_EmpMst_TR
.Where(empMst => empMst.Name == ...) // or use some other filter, just an example
.AsEnumerable()
.Select(empMst => new
{
Id = empMst.Id,
Name = empMst.Name,
EmpCode = empMst.GetEmpCode(),
...
});

IQueryable WHERE inside a for loop does not produce expected result

Objective:
Implementing AND filter. Where users input list of keywords in an input, then the list gets filtered based on the first word, then the next and so on. For instance, when they input "May", their list should be filtered to show only records for the month of may. In case, they input "May June", they should get empty list. Because, you shouldn't find records for the month of June within a list for the month of May
My failed attempt:
Below example does not work as I expected to
List<string> keywords; // e.g. "May", "June"
for (int i = 0; i < keywords.Length; i++) {
string searchTerm = keywords[i];
entityObj = entityObj.Where(x =>
x.Month.ToLower().Contains(searchTerm) ||
x.Year.ToString().ToLower().Contains(searchTerm) ||
x.Product.ToLower().Contains(searchTerm));
}
entityObj is of type IQueryable<SomeEntity>. Assume there is a table in the database with Month, Year and Product columns. On the first iteration, searchTerm will have May as its value. So, entityObj should return IQueryable object for records that only has May in the Month column. I expect the second iteration to return nothing, since we are looking for value of June in a result of the previous iteration which has only May result. However, I am getting records that have June value instead. No matter how many keywords I have, it is always returning the result based on the last element in the list which is the last iteration.
When I do below
string searchTerm = "May";
entityObj = entityObj.Where(x =>
x.Month.ToLower().Contains(searchTerm) ||
x.Year.ToString().ToLower().Contains(searchTerm) ||
x.Product.ToLower().Contains(searchTerm));
searchTerm = "June";
entityObj = entityObj.Where(x =>
x.Month.ToLower().Contains(searchTerm) ||
x.Year.ToString().ToLower().Contains(searchTerm) ||
x.Product.ToLower().Contains(searchTerm));
I get the result that I expect, empty list. However, When I do that inside a for loop, it does not work. Am I missing something here?
The issue must be because of using same variable entityObj.
First of all, entityObj is an IQueryable - right? That means it just a query and not a collection or list.
Secondly do not use same variable to store the result as well.
Instead try this it should work fine
string searchTerm = "May";
var mainList= entityObj.Where(x => x.Month.ToLower().Contains(searchTerm) ||
x.Year.ToString().ToLower().Contains(searchTerm) ||
x.Product.ToLower().Contains(searchTerm)).ToList();
searchTerm = "June";
var subList = mainList.Where(x => x.Month.ToLower().Contains(searchTerm) ||
x.Year.ToString().ToLower().Contains(searchTerm) ||
x.Product.ToLower().Contains(searchTerm));
I am sure this should work but even this one doesn't work then you might want to look into how C# is referencing variable assigning memory allocation.
Don't go too deep but there is shallow copy and deep copy techniques you might want to look into.
https://www.geeksforgeeks.org/shallow-copy-and-deep-copy-in-c-sharp/

How do I check the values retrieved from this Linq query as .ToLower()?

This is my code:
DataRow r = VirtualTable
.AsEnumerable()
.FirstOrDefault(tt => (tt.Field<string>("Column1") == value1) ||
(tt.Field<string>("Column1") == value2));
This code retrieves a data row whose 'Column1' matches a given string. I then check this against a bool if statement. However, though I can change my string's capitalization, I don't know how to handle it with the value Linq gives me. Still learning linq, so I don't know my way around it yet.
In short, I have the string "Red box" in the table, but want it to be read as "red box" so it will match my internal string of the same value.
Additionally, I was trying to retrieve the IndexOf the row this query gives me, but I'm always retrieving a -1 even if it finds a match.
Here's the code to retrieve it:
int SelectedIndex = VirtualTable.Rows.IndexOf(r);
Try string.Equals to ignore case and overload Select to get row's index:
var row = VirtualTable
.AsEnumerable()
.Select((tt, index) => new {
value = tt.Field<string>("Column1"),
index = index})
.FirstOrDefault(item =>
string.Equals(item.value, value1, StringComparison.OrdinalIgnoreCase) ||
string.Equals(item.value, value2, StringComparison.OrdinalIgnoreCase));
// If we have the row found, we can get
if (row != null) {
var r = row.value; // value, e.g. "bla-bla-bla"
int selectedIndex = row.index; // as well as its index, e.g. 123
...
}
You can use String.Equals(string,StringComparisonOption) to compare two strings using case-insensitive comparison. This avoids generating yet-another-temporary-string as ToLower() would do, eg:
tt.Field<string>("Column1").Equals(value1,StringComparison.OrdinalIgnoreCase)
or
tt.Field<string>("Column1").Equals(value1,StringComparison.CurrentCultureIgnoreCase)
Make sure you use the appropriate comparison option. Different cultures have different casing rules. Ordinal is the fastest option as it compares strings using binary rules.

Linq sorting with nullable object

could you please help me!
I have object list like:
item[0].title = apple
item[0].data.weight = 1
item[1].title = lemon
item[1].data = null
item[2].title = melon
item[2].data.weight = 3
I would like to sort it (ASC and DESC) by weight with null data.
I tried like this:
item.OrderBy(x => x.data == null).ThenBy(x => x.data.weight); // failed
item.Where(x => x.data != null).OrderBy(x => x.data.weight); // ok, but as result only two records
So how i can sort items and receive all results.
ASC at first should be data with null.
DESC at first data with max weight and null at the end of the list.
item.OrderBy(x => x.data == null).ThenByDescending(x => x.data == null ? 0 : x.data.weight);
I am assuming weight is an int, otherwise provide the default value based on type.
Given you're only shipping fruit, and not, say, light, you can treat items having null data as having weight 0. Alternatively, just pick any value that's lower than the possible, valid values in order to put the null items at the top when sorting ascendingly.
You can express that like this:
var ordered = item.OrderBy(x => x.data == null ? 0 : x.data.weight);
You could use something like this: (assuming C# 6 or above)
item.OrderBy(x => x.data?.weight ?? int.MinValue);
This makes use of the new C#6 null-conditional and null-coalescing operators - if you need something applicable in lower versions of C#, you can use a ternary operator, like this:
item.OrderBy(x => x.data != null ? x.data.weight : int.MinValue);
If it's possible that you could have x.data.weight being int.MinValue, then you would need to do something like what you were doing before, but the second linq method should make use of the above lambda/s.
You can do this a few ways, one way would be to have a value replacing the null values using ternary conditional operator on the order by or filtering out the items without a value and concatenating them to the enumerable after you've sorted the objects with values.
By conditionally providing a value for items with null
This is, in my opinion, the best way, and it performs better. You only enumerate over the collection once, versus the other method where you enumerate to determine if each element has a value then order, and then check for the items without a value
item.OrderBy(x => x.data != null ? x.data.weight : int.MinValue)
Filtering and then concatenating the items without a value
There are times where this could possibly be the better solution. One example would be if you want to use a different method for ordering the values when they are missing the property you are looking for.
item.Where(x => x.data != null)
.OrderBy(x => x.data.weight)
.Concat(item.Where(a=>a.data == null))

C# how to linq.select

i want to do the following i have variables stored in an int array called Straight i want to use Linq and get all the values when divided by 4 return 0 i tried this but it will only give me some bool variables and I'm not sure why
var a = Straight.Select(o => o % 4==0).ToArray();
any help is appreciated also i want to note that I'm still learning c# and Linq is something completely new to me
also i want to be able to check the length of the variable
The part you're looking for is Where and not Select.
var a = Straight.Where(o => (o % 4) == 0).ToArray();
Select projects your list into a new returns type which in the case of the expression (o%4) == 0 is boolean.
Where returns you the same object that fulfill the desired expression.
You need Where, not Select
var a = Straight.Where(o => o % 4 == 0).ToArray();
Select creates a projection. In your example, it turns each element of Straight into a bool.

Categories