Recursive list contains property - c#

I have a class as follows:
public class Feature
{
public string Name { get; set; }
public string DisplayName { get; set; }
public List<Feature> SubFeatures { get; set; } = new List<Feature>();
}
I then have a List<Feature> features = new List<Feature>; where I store all my features.
Now, I'd like to know if a particular Feature (by name) exists in my features variable.
However, it can exist at any level (SubFeature of the SubFeature of the SubFeature for example).
The closest I've gotten was this:
public bool FeatureExists(Feature feature, string name)
{
return feature.Name == name || feature.SubFeatures.Select(subFeature => FeatureExists(subFeature, name)).Any(result => result);
}
But it involves having to use a for on the caller of FeatureExists() to pass one top level feature at a time to it.
I'm sure there's an easier way to do this, how can I do this properly?

Define a recursive method like this:
public IEnumerable<Feature> FeatureAndSubFeatures(Feature feature)
{
yield return feature;
foreach (var subFeature in feature.SubFeatures)
{
foreach (var child in FeatureAndSubFeatures(subFeature))
{
yield return child;
}
}
}
Then use it:
FeatureAndSubFeatures(feature).Any(x => x.Name == name);
Another option is to put this method on Feature itself, called something like SelfAndSubFeaturesRecursive().
This approach - writing a method to recursively flatten the tree, rather than writing a specific method to search for a Feature with the given name - is flexible, as you can use it to search the tree for any node based on any criterion, or any subset of nodes, rather than being specialised for just finding nodes with a particular name.
You can also write it to take a collection of features to start with. Something like:
public IEnumerable<Feature> FeaturesAndSubFeatures(IEnumerable<Feature> features)
{
foreach (var feature in features)
{
yield return feature;
foreach (var child in FeaturesAndSubFeatures(feature.SubFeatures))
{
yield return child;
}
}
}
This is only useful if you're always starting with a collection of features, but saves a SelectMany in the event that you do.

I try to avoid recursion whenever possible. This is a version without:
public bool FeatureExists(Feature feature, string name)
{
var featureQueue = new Queue<Feature>();
featureQueue.Enqueue(feature);
while (featureQueue.Count > 0)
{
Feature current = featureQueue.Dequeue();
if (feature.Name == name)
return true;
foreach (Feature f in current.SubFeatures)
featureQueue.Enqueue(f);
}
return false;
}
If you find this less readable as commented you can use a generic extension method and use it whenever you need recursive checks, for example:
public static class Extensions
{
public static bool RecursiveCheck<T>(this T rootItem, Func<T, IEnumerable<T>> getChildrenFunc, Func<T, bool> predicate)
{
var queue = new Queue<T>();
queue.Enqueue(rootItem);
while (queue.Count > 0)
{
T current = queue.Dequeue();
if (predicate(current))
return true;
foreach (T child in getChildrenFunc(current))
queue.Enqueue(child);
}
return false;
}
}
Test-Feature:
Feature f1 = new Feature
{
Name = "1", SubFeatures = new List<Feature> { new Feature {Name="1.1", SubFeatures = new List<Feature> {new Feature {Name= "thename" } } }}
};
This simple one-liner remains:
bool containsName = f1.RecursiveCheck<Feature>(f => f.SubFeatures, f => f.Name == "thename");

You want to make a separate recursive method that does this for you.'
Try this:
public bool FeatureExists(Feature feature, string name) {
if(feature.Name == name) {
return true;
}
if(!feature.SubFeatures.isEmpty) {
foreach(Feature subFeature in feature.SubFeatures){
FeatureExists(subFeature, name)
}
}
return false;
}

the following should recurse through all the lists and populate "result" with all the possible features contained in the entire hierarchy
public class Feature
{
public string Name { get; set; }
public string DisplayName { get; set; }
public List<Feature> SubFeatures { get; set; } = new List<Feature>();
}
other class
List<feature> result = new List<feature>();
public void FindItems(Feature yourFeature)
{
result.add(yourFeature);
foreach(Feature feature in yourFeature)
{
if(feature.SubFeatures.count != 0)
{
foreach(Feature subfeature in feature)
{
FindItems(subfeature);
}
}
else
{
result.add(feature);
}
}
}

Well I'm not sure if this is what you are asking but if you want cleaner code you may write your function as an extension method and use LINQ instead of for loops to get cleaner code.
public static bool FeatureExists(this Feature feature, string name)
{
return feature.Name == name || feature.SubFeatures.Select(subFeature => FeatureExists(subFeature, name)).Any(result => result);
}
And then
List<Feature> mainFeatures = new List<Feature>();
mainFeatures.Any(obj => obj.FeatureExists("abc"));
If you want event shorter and cleaner code you might consider having a feature as a parent of all features like a mother feature and then call your recursive method over that.
But anyway consider making your method an extension method.

Related

Search value in List<T> inside another List<T>

i have class strucur something like this
List<MainCat> AllCat;
public class MainCat
{
public string id { get; set; }
public string name { get; set; }
public List<subcat> subcat { get; set; }
}
public class subcat
{
public string id { get; set; }
public string name { get; set; }
public List<subsubcat> subsubcat { get; set; }
}
public class subsubcat
{
public string id { get; set; }
public string name { get; set; }
}
i want to get name by id,
for example i know the id is 69
i want get output like this
MainCat.name > subcat.name > subsubcat.name (if 69 found in subsubcat)
MainCat.name > subcat.name (if 69 found in subcat)
MainCat.name (if 69 found in MainCat)
If I've understood your requirement properly, this is a case where the query syntax can work wonders:
IEnumerable<string> MyFunc(IEnumerable<MainCat> mainCategories, string idToMatch)
{
return (from main in mainCategories
where main.id == idToMatch
select main.name)
.Concat(from main in mainCategories
from sub in main.subcat
where sub.id == idToMatch
select string.Format("{0} > {1}", main.name, sub.name))
.Concat(from main in mainCategories
from sub in main.subcat
from subsub in sub.subsubcat
where subsub.id == idToMatch
select string.Format("{0} > {1} > {2}", main.name, sub.name, subsub.name));
}
If you're only interested in the first match, this can be called like
string resultName = MyFunc(AllCat, "69").FirstOrDefault();
Because the query uses deferred execution, this will avoid calling the more complex queries if a match is found in the main category.
It is also possible to use the SelectMany function with the function call syntax, however, it gets much harder to follow e.g. the following is how I re-wrote the contents of the second .Concat(...) call in order to illustrate:
mainCategories.SelectMany(main => main.subcat, (main, sub) => new { Main = main, Sub = sub })
.SelectMany(pair => pair.Sub.subsubcat, (pair, subsub) => new { Main = pair.Main, Sub = pair.Sub, SubSub = subsub})
.Where(triplet => triplet.SubSub.id == idToMatch)
.Select(triplet => string.Format("{0} > {1} > {2}", triplet.Main, triplet.Sub, triplet.SubSub));
As I understand it, the query syntax compiles to something very similar to this behind the scenes.
Update after answer accepted, and I came back to look at my code again:
Another possibility would be to add an interface to all 3 classes (or unify them into a single class or derive from a common base class depending on real use case).
This allows a recursive implementation that can search to arbitrary depth (below are 2 different Linq-based implementations depending on whether you have a preference for one or other syntax):
public interface ITreeCat
{
string id { get; }
string name { get; }
IEnumerable<ITreeCat> subcat { get; }
}
// add explicit interface implemetantion to existing 3 classes
// e.g.
// IEnumerable<ITreeCat> ITreeCat.subcat { get { return subsubcat; } }
// IEnumerable<ITreeCat> ITreeCat.subcat { get { return Enumerable.Empty<ITreeCat>(); } }
IEnumerable<string> MyFunc(IEnumerable<ITreeCat> categories, string idToMatch, string prefix = "")
{
return (from cat in categories
where cat.id == idToMatch
select prefix + cat.name)
.Concat(from cat in categories
from recursiveResult in MyFunc(cat.subcat, idToMatch, prefix + cat.name + " > ")
select recursiveResult);
}
IEnumerable<string> MyFunc2(IEnumerable<ITreeCat> categories, string idToMatch, string prefix = "")
{
return categories.Where(cat => cat.id == idToMatch)
.Select(cat => prefix + cat.name)
.Concat(categories.SelectMany(cat => MyFunc2(cat.subcat, idToMatch, prefix + cat.name + " > ")));
}
This has the advantage that it continues to work if you later add a subsubsubcat etc.
All of the above code examples use a breadth-first search, and repeatedly enumerate the "parent" categories each time they go one level deeper.
In some applications a depth-first search may be a better choice, as each list is only enumerated once, in which case it's much easier to use foreach rather than Linq. Again, a recursive version is more concise than 3 nested loops with different classes:
IEnumerable<string> MyFuncDepthFirst(IEnumerable<ITreeCat> categories, string idToMatch)
{
foreach(var cat in categories)
{
if (cat.id == idToMatch)
yield return cat.name;
foreach (var subResult in MyFuncDepthFirst(cat.subcat, idToMatch))
yield return string.Format("{0} > {1}", cat.name, subResult);
}
}
This still assumes that multiple matches can occur. If we're just after the first match, then there's no need to use an iterator block at all, and the above function can be modified to return a simple string:
string FirstMatchingIdDepthFirst(IEnumerable<ITreeCat> categories, string idToMatch)
{
foreach(var cat in categories)
{
if (cat.id == idToMatch)
return cat.name;
string subResult = FirstMatchingIdDepthFirst(cat.subcat, idToMatch);
if(subResult != null)
return string.Format("{0} > {1}", cat.name, subResult);
}
return null;
}
var list = this.AllCat.Where(t=>t.subcat.Any(s=> subsubcat.contains(s));
You can go for a method like below
public static Type Find(string id, MainCat m)
{
if (m.id.Equals(id))
{
return m.GetType();
}
if (m.subcat.Any(a => a.id.Equals(id)))
{
return typeof(subcat);
}
if (m.subcat.Any(a => a.subsubcat.Any(b => b.id.Equals(id))))
{
return typeof(subsubcat);
}
return null;
}
and perform the search. Find the gist, https://gist.github.com/IshamMohamed/33d75064789d77d88404b8ffc9a17e94
In this way you can increase the number of inner lists (eg: subsubsubcat)

For loop to add image to imagelist

I'm cleaning up my code trying to short in some things
Now I've stumbled across:
ImageList.Add(test.Properties.Resources.test1);
ImageList.Add(test.Properties.Resources.test2);
ImageList.Add(test.Properties.Resources.test3);
ImageList.Add(test.Properties.Resources.test4);
ImageList.Add(test.Properties.Resources.test5);
(There are 15 of these)
Was wondering if this could be shortened with a for loop
Something like:
for(int i=1; i<=15; i++)
ImageList.Add(test.Properties.Resources.test +i);
Now ofcourse this won't work but I have no clue how to do this (if even possible)
You can iterate over resources via this code
using System.Collections;
using System.Globalization;
using System.Resources;
...
ResourceSet resourceSet = MyResourceClass.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
foreach (DictionaryEntry entry in resourceSet)
{
string resourceKey = entry.Key;
object resource = entry.Value;
}
You can use reflection, to get the values:
public class Something
{
public int Test1 { get; set; }
public int Test2 { get; set; }
public int Test3 { get; set; }
public int Test4 { get; set; }
}
var thing = new Something();
var imageProperties = typeof(Something)
.GetProperties()
.Where(p => p.Name.StartsWith("Test"));
var imagesToAdd = imageProperties
.Select(property => property.GetValue(thing))
.ToList();
You could define a property of type IEnumerable<Image> in the class of Resources object
public IEnumerable<Image> Images
{
get
{
yield return test1;
yield return test2;
yield return test3;
yield return test4;
yield return test5;
...
}
}
and then use it to fill ImageList
foreach(var image in test.Properties.Resources.Images)
{
ImageList.Add(image);
}
I just found out that there is a library for evaluating C# expression called Flee. Apparently you can use it to evaluate C# code so that you can loop over variable names, just like JavaScript, but the need for it most likely means a design flaw.
http://flee.codeplex.com/

How to filter a recursive object?

In my current project, a method I don't control sends me an object of this type:
public class SampleClass
{
public SampleClass();
public int ID { get; set; }
public List<SampleClass> Items { get; set; }
public string Name { get; set; }
public SampleType Type { get; set; }
}
public enum SampleType
{
type1,
type2,
type3
}
I display those data in a TreeView, but I would like to display only the path ending with SampleClass objects having their Type property set to type3, no matter the depth of this leaf.
I have absolutely no clue on how to do that, can someone help me ?
Thanks in advance !
Edit
To explain the problem I meet with the solutions proposed by Shahrooz Jefri and dasblinkenlight, here is a picture. The left column is the original data, without filtering, and the right one is the data filtered. Both methods provide the same result.
In red is the problem.
Use this Filter method:
public void Filter(List<SampleClass> items)
{
if (items != null)
{
List<SampleClass> itemsToRemove = new List<SampleClass>();
foreach (SampleClass item in items)
{
Filter(item.Items);
if (item.Items == null || item.Items.Count == 0)
if (item.Type != SampleType.type3)
itemsToRemove.Add(item);
}
foreach (SampleClass item in itemsToRemove)
{
items.Remove(item);
}
}
}
In addition to initially determining which items to show, if the datasize is substantial and you expect users to frequently collapse and expand sections then filtering after every click my result in slow ui response.
Consider the Decorator pattern or some other way of tagging each node with relevant info so that the filtering is not required after every click.
Try this approach:
static bool ShouldKeep(SampleClass item) {
return (item.Type == SampleType.type3 && item.Items.Count == 0)
|| item.Items.Any(ShouldKeep);
}
static SampleClass Filter(SampleClass item) {
if (!ShouldKeep(item)) return null;
return new SampleClass {
Id = item.Id
, Name = item.Name
, Type = item.Type
, Items = item.Items.Where(ShouldKeep).Select(x=>Filter(x)).ToList()
};
}
The above code assumes that Items of leaves are empty lists, rather than nulls.

Simple question: Reflections in C#

I am learning the reflections concepts in c#. I have a class like this
public class pdfClass
{
public List<AttributeProperties> TopA { get; set; }
public List<AttributeProperties> TopB { get; set; }
public List<AttributeProperties> TopC { get; set; }
}
In another class I would like to extract the values from the list. I have stupid ways to do it like
public void ExtractValue (pdfClass incomingpdfClass, string type)
{
switch (type)
{
case "TopA":
foreach (var listitem in incomingPdfClass.TopA)
{...}
breaks;
case "TopB":
foreach (var listitem in incomingPdfClass.TopB)
{...}
breaks;
...
}
}
The operations in the foreach loops are similar. How can I do this in a clear way by using reflections?
public void ExtractValue(pdfClass incomingpdfClass, string type)
{
PropertyInfo pinfo = typeof(pdfClass).GetProperty("Top" + type);
var yourList = pinfo.GetValue(incomingpdfClass);
foreach (var listitem in yourList)
{ ... }
}
This is how you should do this using reflection. However, you should note that my code differs from yours in the fact that you are writing code that isn't clear nor would it compile. AS
public class ExtractValue (pdfClass incomingpdfClass, string type)
is non valid C# syntax if that is supposed to be a function as per my example this will work for you
Or if this is supposed to happen in the Constructor for the class it should look as follows
public class ExtractValue
{
public ExtractValue(pdfClass incomingpdfClass, string type)
{
PropertyInfo pinfo = typeof(pdfClass).GetProperty("Top" + type);
var yourList = pinfo.GetValue(incomingpdfClass);
foreach (var listitem in yourList)
{ ... }
}
}
var property = this.GetType().GetProperty(type);
foreach (var item in (List<AttributeProperties>)property.GetValue(this, null))
{
}
If you have instance of pdfClass you do not need to use reflection for accessing lists.
I would suggest to decouple type from strategy itself by persisting such a dictionary:
IDictionary<string, Func<pdfClass, AttributeProperties, bool>> strategy;
Once add relations like
strategy.Add("TopA", (pdf, item) =>
{
return pdf.TopA.IndexOf(item) >= 0;
});
and use like
string itemType = "TopA";
if (strategy.ContainsKey(itemType) )
{
bool found = strategy[itemType](incommingPdfClass, listItem);
}

question about linq select and ToList()

I have a problem with returning a list by executing a Select LINQ query. This is the query:
var data = Repository<EducationString>
.Find()
.ToList()
.Select(p => new EducationStringModel() {
Id = p.Id,
Title = p.Title,
EducationDegree=p.EducationDegree })
.ToList();
As you can see I used ToList() 2 times. I don't know why but when I delete the first ToList() I see this error, "Index was outside the bounds of the array", but by having both ToList() there is no problem.
Would it help if I said EducationDegree in EducationStringModel is an IList<EducationDegree>?
Is there anybody who knows the reason?
#Mark :its L2O
if u need to see the classes:
public class EducationStringModel
{
private IList _educationDegree = new List();
public IList EducationDegree
{
get
{
if (_educationDegree == null)
{
_educationDegree = new List();
}
return _educationDegree;
}
set { _educationDegree = value; }
}
public int? Id { get; set; }
public string Title { get; set; }
}
public class EducationString{
private string _title;
private IList _educationExperiences;
private IList _educationDegree;
virtual public string Title
{
get { return _title; }
set { _title = value; }
}
virtual public IList<EducationExperience> EducationExperiences
{
get
{
if (_educationExperiences == null)
{
_educationExperiences = new List<EducationExperience>();
}
return _educationExperiences;
}
set
{
_educationExperiences = value;
}
}
virtual public IList<EducationDegree> EducationDegree
{
get
{
if (_educationDegree == null)
{
_educationDegree = new List<EducationDegree>();
}
return _educationDegree;
}
set
{
_educationDegree = value;
}
}
}
Is that the actual code? The only unclear thing there is: what does Find() return?
It sounds like the ToList is helping here by breaking composition and using LINQ-to-Objects, in which case AsEnumerable() should work just as well. After that you just do a Select (which for L2O just takes each item in turn and applies the map). If Find() is something more exotic, it sounds like a bug in that LINQ provider (or perhaps more fairly: that provider struggling to cope with an atypical construct). Hard to say more without a fully reproducible example.

Categories

Resources