Reputation: 163
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:
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
Upvotes: 4
Views: 477
Reputation: 31
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;
}
Upvotes: 0
Reputation: 1691
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();
}
Upvotes: 0
Reputation: 2161
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.
Upvotes: 2
Reputation: 1608
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;
Upvotes: 0