I have sucha a situation. I have list inside which I am holding objects of one class (this objects has 6 properties "Name", "Type", "Index", "Value", "Status", "Parameter"). Later I am binding it with gridView.
Now I would like to be able to make a filter for each of this properties. I want to be able for example to insert into textbox "Name" : John, and I would like gridView to display only rows where I have John.
Second thing is that I would like to be able to mix filters, so have for example "Name" set to : 'John' and "Index" to : 5, and display "John" with "Index" : 5.
How can I do this?
Now I only have function to insert everything into list. This objects are stored inside Middle property of class WholeLine.
Correction method = new Correction();
while ((line = sr.ReadLine()) != null)
{
string[] _columns = line.Split(",".ToCharArray());
object returnValue;
MyColumns mc = new MyColumns();
mc.Time = _columns[0];
mc.Information = _columns[1];
mc.Description = _columns[2];
if (String.IsNullOrEmpty(mc.Information) )
{ continue; }
else
{
returnValue = method.HandleRegex(mc.Information);
}
Line main = new Line();
main.LeftColumn = mc.Time;
main.Middle= returnValue;
main.RightColumn = mc.Description;
list3.Add(main);
}
EDIT:
It is not that simple in my situation (I think...), because I have main class where I have this while shown above. Later I call method HadleRegex from class Correction . Bellow I will show how it looks like:
class Correction
{
private MoreInfor _MoreInfor = new MoreInfor();
public MoreInfor MoreInfor { get { return _ID; } }
Correction sk = new Correction();
Match matches = Regex.Match(newStr, #"\b(?:complete\b)", RegexOptions.IgnoreCase);
if (matches.Success)
{
string[] lines = newStr.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
Regex regex1 = new Regex(#"^(?:(?<C0>Command) (?:answer|sent) from (?<C1>\S+) to (?<C2>\S+) (?<C3>.+))$");
var matches1 = lines.Select(line => regex1.Match(line));
foreach (var match in matches1)
{
sk._MoreInfor.Name= match.Groups["C1"].ToString();
sk._MoreInfor.Type = match.Groups["C2"].ToString();
sk._MoreInfor.Value = match.Groups["C3"].ToString();
sk._MoreInfor.Index = match.Groups["C4"].ToString();
}
}
return sk;
}
public class MoreInfo
{
public string Name{ get; set; }
public string Type { get; set; }
public string Index { get; set; }
public string Value { get; set; }
public string Status { get; set; }
public string Parameter { get; set; }
}
This returned vale of class Correction is later passed to returnValue from my main class and added to Middle property of class Line
I'm sorry if I really messed up!
You can use linq - where
var result = list.Where(x => x.Name == textBoxName.Text).ToList();
This assumes your textbox will search for a name
For multiple filters,
list.Where(x => x.Property == "something" && x.Name == textBoxName.Text).ToList();
list.Where(x => result.Where(ix => x.Property == "something").ToList();
You may define some "NoFilter" values. For instance index may be -1, or Name = ""
public List<person> FilterPersons (List<person> persons,
string name = "",
string type = "",
int index = -1,
int value = -1,
int status = -1,
string parameter = "")
{
return persons
.Where
(
x=>
(name == "" || x.Name == name)
&& (type == "" || x.Type == type)
&& (index == -1 || x.Index == index)
&& (value == -1 || x.Value == value)
&& (status == -1 || x.Status == status)
&& (parameter == "" || x.Parameter == parameter)
)
.Select(x=>x);
}
And then you may call it as:
filterPersons = FilterPersons(persons: persons,name: "John");
or
filterPersons = FilterPersons(persons: persons,index: 10, status: 5);
use linq to filer
yourlist.Select(x=>x.Name == "John");
Related
I am trying to check if more than 2 properties in a object has value and return invalid string if so
Example :
Class Student
{
string Name
string Code
string SNumber
}
Considering above code example , below scenarios can be written
if(studentObj.Name != null && studentObj.Code != null)
return "invalid";
if(studentObj.Name != null && studentObj.SNumber != null)
return "invalid";
if(studentObj.Code != null && studentObj.SNumber != null)
return "invalid";
Is there a way this can be simplified?
Thanks
typeof(Student).GetFields() // use GetProperties() if you have them, and not fields
.Select(field => field.GetValue(studentObj) as string)
.Count(value => !string.IsNullOrEmpty(value))
You could actually count:
class Student
{
public string Name {get; set;}
public string Code {get; set;}
public string SNumber {get; set;}
}
class StudentValidator
{
public string Validate(Student student)
{
int nonZeroCount = 0;
if( student.Name is object ) nonZeroCount++;
// in C# 9: if( student.Name is not null )
if( student.Code is object ) nonZeroCount++;
if( student.SNumber is object ) nonZeroCount++;
return (nonZeroCount > 2)? "invalid" : "valid";
}
}
Mind that you might prefer to check either with
string.IsNullOrEmtpty(string) or
string.IsNullOrWhitespace(string)
Here you can optionally optimize the select statement, but as an example, I offer this option for discussion:
class Program
{
static void Main(string[] args)
{
var students = new List<Student>
{
new Student{Name = "Mary", Code = "1", SNumber = "001" },
new Student{Name = "Sarah", Code = "", SNumber = "002" },
new Student{Name = "", Code = "3", SNumber = "003" },
new Student{Name = "Katherine", Code = "4", SNumber = "004" },
new Student{Name = "Eva", Code = "", SNumber = "" }
};
var listedProperty = from student in students
select new List<string> { student.Name, student.Code, student.SNumber };
listedProperty
.ToList()
.ForEach(l =>
{
var nullPropertyCount = l.Count(i => string.IsNullOrWhiteSpace(i));
if (nullPropertyCount <= 1)
{
Console.WriteLine(string.Join(", ", l.ToArray()));
}
});
Console.ReadLine();
}
}
Output result to the console:
I want to check the record in list based on trackId and InUse property because I want the unique data which has Inuse property = false but I want to exclude the record of Track="S" how do I exlude that so my condition does not check the record that has Track="S" because SidList also contains the record with track="S". Below is my condition
if (SidList.Any(q => q.TrackId == item.TrackId && q.InUse == true))
{
//Not unique logic goes here
}
I tried this condtion but it does not work
if (SidList.Any(q => q.TrackId == item.TrackId && q.InUse == true && !q.Track.Contains('S')))
{
}
string.Contains is case sensitive.If Track has data with lower case s,the statement !q.Track.Contains('S') will return true.
If you want to ignore the case,change like below:
!q.Track.Contains('S', StringComparison.InvariantCultureIgnoreCase)
Here is the whole working demo:
Model:
public class TrackModel
{
public int TrackId { get; set; }
public bool InUse { get; set; }
public string Track { get; set; }
}
Controller:
var SidList = new List<TrackModel>() {
new TrackModel(){TrackId=1,InUse=false,Track="dfdfds"}
};
if (SidList.Any(q => q.TrackId == 1 && q.InUse == false&& !q.Track.Contains('S', StringComparison.InvariantCultureIgnoreCase)))
{
//...
}
Result:
var filteredList = SidList.Where(q => q.TrackId == item.TrackId && q.InUse && !q.Track.Contains('S'))
foreach ( var filteredElement in filteredList)
{
// not unique action goes here
}
How can I convert expression tree to Dictionary?
For example:
class Dummy
{
public int Id { get; set; }
public string Name { get; set; }
}
Example 1:
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2)
//Result is dictionary with item:
<key, value>Id,2
Example 2:
var s = "Foo";
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2 && t.Name == s)
//Result is dictionary with items:
<key, value>Id,2
<key, value>Name,"Foo"
I know EF Core does this, but don't know how, and source code is to complicated for me to parse it.
I should say expression doesn't contain || and ().
For example:
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2 || t.Id == 3)
or
MyStaticClass.ParseExpression<Dummy>(t => t.Id == 2 && (Name == "Foo" || Id Name == "Test")
If you are sure that expressions will be in provided format only - you can do something like this:
public class Dummy
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class ExpressionConverter
{
public static Dictionary<string, string> Convert<T>(Expression<Func<T,bool>> expression)
{
var result = new Dictionary<string,string>();
var current = (BinaryExpression)expression.Body;
while (current.NodeType != ExpressionType.Equal)
{
ParseEquals((BinaryExpression)current.Right);
current = (BinaryExpression)current.Left;
}
ParseEquals(current);
void ParseEquals(BinaryExpression e)
{
var key = (MemberExpression) e.Left;
var value = (ConstantExpression) e.Right;
result.Add(key.Member.Name, value.Value.ToString());
}
return result;
}
}
Usage:
var test = ExpressionConverter.Convert<Dummy>(x => x.Id == 5 && x.Name == "dummy" && x.Age == 11);
Or replace ParseEquals:
void ParseEquals(BinaryExpression e)
{
var key = (MemberExpression) e.Left;
object value;
switch (e.Right)
{
case ConstantExpression constantExpression:
value = constantExpression.Value;
break;
case MemberExpression memberExpression:
var obj = ((ConstantExpression)memberExpression.Expression).Value;
value = obj.GetType().GetField(memberExpression.Member.Name).GetValue(obj);
break;
default:
throw new UnknownSwitchValueException(e.Right.Type);
}
result.Add(key.Member.Name, value);
}
To support:
var myVar = "dummy";
var test = ExpressionConverter.Convert<Dummy>(x => x.Id == 5 && x.Name == myVar && x.Age == 11);
For sure very simple question for most of you.
But I am struggling with a solution at the moment.
Imagine you have a list of cats (List) where each cat has a list of babys (Kitten)
public class Cat
{
public string Name { get; set; }
public int Age { get; set; }
public string Race { get; set; }
public bool Gender { get; set; }
public List<Kitten> Babys { get; set; }
}
public class Kitten
{
public string Name { get; set; }
public double Age { get; set; }
public bool Gender { get; set; }
}
now I want to find the Cat that has the most matches for given requirements. It could easily be the case that a cat matches only 2 of 3 requirements. I simple want to find the cat that has the most matches to my requirements.
where my requirements could be:
Name has to be "Micky"
Age is 42
Has a Kitten named "Mini"
My actual solution would be to compare all properties and take the one with the highest count of matching properties. But this is not generic and I am sure there are mutch better ways to do it.
Thanks in advance
Well, I have no opportunity to test this solution, but you can try this:
Assume that you have a list of cats:
var cats = new List<Cat>();
Now you have defined what are your criteria:
var desiredName = "Micky";
var desiredAge = 42;
var desiredKitten = "Mini";
And then you have to get your desired cat:
var desiredCat = cats
.Select(c => new {
Rating =
Convert.ToInt32(c.Age == desiredAge) + // Here you check first criteria
Convert.ToInt32(c.Name == desiredName) + // Check second
Convert.ToInt32(c.Babys.Count(b => b.Name == desiredKitten) > 0), // And the third one
c })
.OrderByDescending(obj => obj.Rating) // Here you order them by number of matching criteria
.Select(obj => obj.c) // Then you select only cats from your custom object
.First(); // And get the first of them
Please check if this works for you.
And if you need more specific answer or some edits for me to add.
If you really will compare 2 ou 3 requirements you can simplify using Linq by:
// try to find with 3 requirements
var foundCats = catList.Where(t => t.Name == desiredName &&
t.Age == desiredAge &&
t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
if (foundCats.Any())
{
// you found the desired cat (or cats)
return foundCats;
}
// try to find with 2 requirements
foundCats = catList.Where(t =>
(t.Name == desiredName && t.Age == desiredAge) ||
(t.Name == desiredName && t.Babys.Any(k => k.Name == desiredKitten)) ||
(t.Age == desiredAge && t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
if (foundCats.Any())
{
// you found the desired cat (or cats)
return foundCats;
}
// try to find with only 1 requirement
foundCats = catList.Where(t => t.Name == desiredName ||
t.Age == desiredAge ||
t.Babys.Any(k => k.Name == desiredKitten)
).ToList();
return foundCats;
So, I see that the problem is you don't know if in any near future you will have more properties, so I will suggest going to the hardway and make reflection, the following is ugly af but you can probably (you will) make it better and hopefully serves you well as guiadance:
public static List<Cat> CheckProperties(List<Cat> inCatList, Cat inQueryCat)
{
Dictionary<Cat, List<PropertyInfo>> dict = new Dictionary<Cat, List<PropertyInfo>>();
foreach (PropertyInfo pI in inQueryCat.GetType().GetProperties())
{
var value = pI.GetValue(inQueryCat);
if (value != null)
{
var cats = inCatList.Where(cat => cat.GetType().GetProperty(pI.Name).GetValue(cat).Equals(value));
foreach (Cat cat in cats)
{
if (dict.ContainsKey(cat))
{
dict[cat].Add(pI);
}
else
{
dict.Add(cat, new List<PropertyInfo>() {pI});
}
}
}
}
int max = Int32.MinValue;
foreach (KeyValuePair<Cat, List<PropertyInfo>> keyValuePair in dict)
{
if (keyValuePair.Value.Count > max)
{
max = keyValuePair.Value.Count;
}
}
return dict.Where(pair => pair.Value.Count == max).Select(pair => pair.Key).ToList();
}
While this is the most generic solution there is (need some edge case improvements):
public class ReflectCmpare
{
public PropertyInfo PropertyInfo { get; set; }
public dynamic Value { get; set; }
}
public Cat GetBestCat(List<Cat> listOfCats, List<ReflectCmpare> catParamsToCompare, List<ReflectCmpare> kittensParamsToCompare)
{
var bestScore = 0;
var ret = listOfCats[0];
foreach (var cat in listOfCats)
{
var score = catParamsToCompare.Sum(param => param.PropertyInfo.GetValue(cat, null) == param.Value ? 1 : 0);
foreach (var baby in cat.Babys)
{
score+= kittensParamsToCompare.Sum(param => param.PropertyInfo.GetValue(baby, null) == param.Value ? 1 : 0);
}
if (score <= bestScore) continue;
bestScore = score;
ret = cat;
}
return ret;
}
You should really think about just doing simple compare function
considering this objects is not dynamic this is the way to go:
public Cat GetBestCat(List<Cat> listOfCats, string name , int? age , bool? gender, string race ,string babyName,int? babyAge,bool? babyGender )
{
var ret = listOfCats[0];
var highestScore = 0;
foreach (var cat in listOfCats)
{
var score = 0;
score += name != null && cat.Name.Equals(name) ? 1 : 0;
score += age.HasValue && cat.Age.Equals(age.Value) ? 1 : 0;
score += gender.HasValue && cat.Gender.Equals(gender.Value) ? 1 : 0;
score += race != null && cat.Race.Equals(race) ? 1 : 0;
score += name != null && cat.Name.Equals(name) ? 1 : 0;
score += cat.Babys
.Where(k => babyName==null || k.Name.Equals(babyName))
.Where(k => !babyAge.HasValue || k.Age.Equals(babyAge.Value))
.Any(k => !babyGender.HasValue || k.Gender.Equals(babyGender.Value))?1:0;
if (score <= highestScore) continue;
highestScore = score;
ret = cat;
}
return ret;
}
I am trying to search the object from its list using entity framework.
I have following structure of the object
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public double Size { get; set; }
}
and I create the list of the product
private List<Product> AddProducts()
{
List<Product> ProductList = new List<Product>();
oProduct Product = new Product();
oProduct.Id = 1;
oInventory.ProductName = "Product1";
oProduct.Size = 25;
ProductList.Add(oProduct);
oProduct Product = new Product();
oProduct.Id = 2;
oInventory.ProductName = "Product2";
oProduct.Size = 25;
ProductList.Add(oProduct);
return ProductList;
}
Now, I am trying to search the object form the above list form ProductName parameter.
I have used below code.
public ActionResult Index(string productname = "", string size = "")
{
var oProdList = from ProdList in AddProducts() select oProdList;
oProdList = oProdList.Where(oProd => oProd.ProductName.ToUpper().Contains(ProductName.ToUpper().Trim())|| oProd.Size.ToString().ToUpper().Contains(size.ToUpper().Trim()));
ViewBag.ProductList = oProdList;
return View();
}
Now Please check picture below, I have list of products and I am trying to find the Product1 by typing it in text box and I keep the size textbox blank.
now when I click on submit , I pass these two varibles for searching in Index method (above one) but the LINQ command returns the both records, Product1 and Product 2
but it should return only one record
How to deal with this ?
This worked for me and is how a search algorithm should work. You want to && the parameters if they are both provided but || them if one of the arguments is missing.
Also its good to note that an input of "2" will match "25" for the size because we are preforming a Contains. If you want it only to match when you enter "25" and not "2" set it to == instead of Contains. Same goes for productname.
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public double Size { get; set; }
}
private List<Product> AddProducts()
{
List<Product> ProductList = new List<Product>();
var p = new Product();
p.Id = 1;
p.ProductName = "Product1";
p.Size = 25;
ProductList.Add(p);
var p2 = new Product();
p2.Id = 2;
p2.ProductName = "Product2";
p2.Size = 25;
ProductList.Add(p2);
return ProductList;
}
public ActionResult Index(string productname = "", string size = "")
{
var oProdList = from p in AddProducts() select p;
if (!string.IsNullOrWhiteSpace(productname) && !string.IsNullOrWhiteSpace(size))
{
oProdList = oProdList.Where(p => p.ProductName.ToUpper().Trim().Contains(productname.ToUpper().Trim()) && p.Size.ToString().Contains(size.Trim()));
}
else
{
oProdList = oProdList.Where(p => !string.IsNullOrWhiteSpace(productname) ? p.ProductName.ToUpper().Trim().Contains(productname.ToUpper().Trim()) : p.ProductName.ToUpper().Trim() == productname.ToUpper().Trim() || !string.IsNullOrWhiteSpace(size) ? p.Size.ToString().Contains(size.Trim()) : p.Size.ToString() == size.Trim());
}
ViewBag.ProductList = oProdList;
return View();
}
that happen because every string value contain string empty
and your parameter size default value set to string.empty
so you should check it for filtering the result like this
oProdList = oProdList.Where(
oProd =>(productname == string.Empty ? false:
oProd.ProductName.ToUpper().Contains(productname.ToUpper().Trim()))
|| (size == string.Empty ? false:oProd.Size.ToString().
ToUpper().Contains(size.ToUpper().Trim()))
);
I think the cause might be because of you are converting size to string
Try this
public ActionResult Index(string productname = "", string size = "0")
{
var oProdList = from ProdList in AddProducts() select oProdList;
oProdList = oProdList.Where(oProd => oProd.SectionName.ToUpper().Contains(ProductName.ToUpper().Trim())|| oProd.Size == (double)size));
ViewBag.ProductList = oProdList;
return View();
}
You can use the null coalescing operator here.
oProdList = oProdList.Where(oProd => (!String.IsNullOrEmpty((ProductName??"").Trim()) &&
oProd.ProductName.ToUpper().Contains(ProductName.ToUpper().Trim())
|| (!String.IsNullOrEmpty((size??"").Trim()) && oProd.Size.ToString().ToUpper().Contains(size.ToUpper().Trim())));
You need to specify to ignore search if the search params are empty.
Try this:
var productList = from ProdList in AddProducts()
where (productname.Trim() == string.Empty || ProdList.ProductName.ToUpper().Contains(ProductName.ToUpper().Trim()))
&& (size.Trim() == string.Empty || ProdList.Size.ToString().ToUpper().Contains(size.ToUpper().Trim()))
select ProdList;
if(!String.IsNullOrWhiteSpace(oProd.SectionName)){
oProdList = oProdList.Where(oProd => oProd.SectionName.ToUpper().Contains(ProductName.ToUpper().Trim());
}
if(!String.IsNullOrWhiteSpace(oProd.Size)){
oProdList = oProdList.Where(oProd => oProd.Size.ToUpper().Contains(size.ToUpper().Trim());
}