I have a nested menu - here a simplified class:
public class NestedNode
{
public string Url { get; set; }
public List<NestedNode> Children { get; set; }
}
Given that I have a recursive list of NestedNode, I'm trying to ascertain whether any descendant is active at any level.
Here's the code to test:
protected void Page_Load(object sender, EventArgs e)
{
// The url of the current page
var currentUrl = Request.Url.GetLeftPart(UriPartial.Path);
// This is a list of nested nodes
var nodes = SiloNodes;
// Start loop
RecursiveCall(nodes, currentUrl);
}
void RecursiveCall(IEnumerable<NestedNode> nodes, string currentUrl)
{
if (nodes == null) return;
foreach (var n in nodes)
{
// This can test current level only
//var isActive = n.Url == currentUrl;
// This can test next level down
//var isActive = n.Children.Any(c => c.Url == currentUrl);
// How can I test all levels in one go?
RecursiveCall(n.Children, currentUrl);
}
}
What I need to be able to do is work out if any of the parents children are active (at the top lavel) so that I can add classes. At the moment, my ideas only go one level deep.
How about something like
void Main()
{
var nodes = new List<NestedNode>();
var isActive = nodes.Any(n => n.AnyActive("url"));
}
public class NestedNode
{
public NestedNode()
{
Children = Enumerable.Empty<NestedNode>();
}
public string Url { get; set; }
public IEnumerable<NestedNode> Children { get; set; }
public bool AnyActive(string url){ return Url==url || Children.Any(c => c.AnyActive(url));}
}
In this situation I would probably add a method to the NestedNode to check the condition recursively - something like this:
public bool ExistsRecursive(Func<NestedNode, bool> predicate)
{
if(predicate(this))
{
return true;
}
foreach(var node in Children)
{
return predicate(node);
}
return false;
}
And then, in your Page_Load, all you need is this:
if(nodes.ExistsRecursive(n => n.Url == currentUrl))
{
// current url is found in at least one node
}
Related
I have an object with multiple nested objects in it. The classes:
public class Element
{
public string Key { get; set; }
public List<Element> SubElement { get; set; }
[XmlIgnore]
public ElementConfig ElementConfig { get; set; }
[XmlIgnore]
public Element ParentElement { get; set; }
}
public class SubElement
{
public string Key { get; set; }
public List<Element> Elements { get; set; }
}
[Serializable]
public class ElementConfig {
public string Key { get; set; }
public string Label { get; set; }
public string TypeName { get; set; }
public string IconName { get; set; }
}
The base is that an Element can have multiple SubElements with their own ElementConfig. I want to populate the ElementConfig for each Element, doing it like this:
private void Recursion(Element element)
{
//TODO: use recursion ??
foreach (Element item in element.SubElement)
{
item.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(x => x.Key == item.Key);
item.ParentElement = element;
foreach (Element x in item.SubElement)
{
x.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(e => e.Key == x.Key);
x.ParentElement = item;
foreach (Element y in x.SubElement)
{
y.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(e => e.Key == y.Key);
y.ParentElement = x;
}
}
}
}
This is working, but I want to use a Recursive method to do this. Is this possible?
EDIT
From the suggestion from #johnathan Barclay I use following approach:
element.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(x => x.Key == element.Key);
foreach (var item in element.SubElement)
{
item.ParentElement = element;
Recursion(item);
}
This helps getting the ElementConfig for each first SubElement. What happens now is that the following SubElements are not filled.
Assuming ElementConfig also needs to be set for the root element:
private void Recursion(Element element)
{
element.ElementConfig = navigationStructureConfig.ElementConfigs
.SingleOrDefault(x => x.Key == element.Key);
foreach (var item in element.SubElement)
{
item.ParentElement = element;
Recursion(item); // Recurse here
}
}
Here is an generic iterative solution:
public void PupulateConfigAndParent(Element element){
foreach(var (item, parent) in DepthFirstWithParent(element, e => e.SubElement)){
item.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(x => x.Key == item.Key);
item.ParentElement = parent;
}
}
public static IEnumerable<(T Node, T Parent)> DepthFirstWithParent<T>(T self, Func<T, IEnumerable<T>> selector)
{
var stack = new Stack<(T Node, T Parent)>();
stack.Push((self, self));
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach (var child in selector(current.Node))
{
stack.Push((child, current.Node ));
}
}
}
An advantage of this is the ability to reuse the generic method for any kind of tree like structure. Note that the root node will have itself as the parent.
I have a TreeView containing some TreeNode, as presented below:
My idea is to use the textBox1 as a search engine to show only the TreeNodes that are containing the text of textBox1.
I already have a function that can parse the different nodes and look if the text contained in textBox1 is contained of each node:
private void textBox1_TextChanged(object sender, EventArgs e)
{
foreach (var node in Collect(treeView1.Nodes))
{
if (node.Text.ToLower().Contains(textBox1.Text.ToLower()))
{
//I want to show those nodes
Debug.Write("Contained : ");
Debug.WriteLine(node.Text);
}
else
{
//I want to hide those nodes
Debug.Write("Not contained : ");
Debug.WriteLine(node.Text);
}
}
}
As the property isVisible for TreeNode is only a getter, how to hide the TreeNodes that do not contain the searched text?
From the documantation there is no way to hide treenode. But instead you can remove and re-add that node.
You can do this with the below approach:
public class RootNode : TreeNode
{
public List<ChildNode> ChildNodes { get; set; }
public RootNode()
{
ChildNodes = new List<ChildNode>();
}
public void PopulateChildren()
{
this.Nodes.Clear();
var visibleNodes =
ChildNodes
.Where(x => x.Visible)
.ToArray();
this.Nodes.AddRange(visibleNodes);
}
//you would use this instead of (Nodes.Add)
public void AddNode(ChildNode node)
{
if (!ChildNodes.Contains(node))
{
node.ParentNode = this;
ChildNodes.Add(node);
PopulateChildren();
}
}
//you would use this instead of (Nodes.Remove)
public void RemoveNode(ChildNode node)
{
if (ChildNodes.Contains(node))
{
node.ParentNode = null;
ChildNodes.Remove(node);
PopulateChildren();
}
}
}
public class ChildNode : TreeNode
{
public RootNode ParentNode { get; set; }
private bool visible;
public bool Visible { get { return visible; } set { visible = value;OnVisibleChanged(): } }
private void OnVisibleChanged()
{
if (ParentNode != null)
{
ParentNode.PopulateChildren();
}
}
}
I am facing an issue where I have to drill down through the list till I find the desired contentId. Once the contentId is matched, I need to get its ParentIds. I am able to get the contentId but not its ParentId.
Currently I am using recursion to get the ParentIds of a child node. But failed to get the desired results.
Can anyone make it run, or provide the correct code to get this issue fixed. I'm trying to get the childnode and its parentIds. I need to get the parentIds and then want to insert into a List.
I'm able to drill down into the loop but don't know how and when to store the parentIds into a list.
In this code, I am trying to get the parents of contentId "5".
class Program
{
static void Main(string[] args)
{
Program obj = new Program();
var data = obj.GetAllChildCats();
foreach (var item in data)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
public List<int> GetAllChildCats()
{
var ret = getdata();
var data = GetAllChildCats(4, ret.contentObjects, 0);
return data;
}
List<int> parentIdsList1 = new List<int>();
private List<int> GetAllChildCats(int id, ContentObjects data, int Parentid)
{
if (!string.IsNullOrEmpty(data.ContentObjectId.ToString()))
{
parentIdsList1.Add(Parentid);
if (data.ContentObjectId == id)
{
return parentIdsList1;
}
else
{
if (data.ChildContentObjects != null)
{
foreach (ContentObjects cat in data.ChildContentObjects)
{
GetAllChildCats(id, cat, data.ContentObjectId);
}
}
}
}
return parentIdsList1;
}
public Heirarchy getdata()
{
Heirarchy ret = new Heirarchy()
{
_id = 11,
contentObjects = new ContentObjects()
{
ContentObjectId = 1,
NodeId = 34,
ChildContentObjects = new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=2,
NodeId=34,
ChildContentObjects= new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=3,
NodeId=34,
ChildContentObjects= null
}
}
},
new ContentObjects() {
ContentObjectId=4,
NodeId=34,
ChildContentObjects= new List<ContentObjects>()
{
new ContentObjects() {
ContentObjectId=5,
NodeId=34,
ChildContentObjects= null
}
}
},
}
},
HierarchyId = 2
};
return ret;
}
}
public class Heirarchy
{
public int _id { get; set; }
public ContentObjects contentObjects { get; set; }
public int HierarchyId { get; set; }
}
public class ContentObjects
{
public int ContentObjectId { get; set; }
public int NodeId { get; set; }
public List<ContentObjects> ChildContentObjects { get; set; }
}
In this code, I am trying to get the parents of contentId "5".
This can be solved by a simple depth-first search. We just have to check the children's ID before digging deeper into the tree, so that we can still return the parent reference:
static void Main(string[] args)
{
Program obj = new Program();
var parents = obj.GetParentsOf(5, obj.getdata().contentObjects);
Console.WriteLine(parents.Count()); // yields 1
Console.WriteLine(parents.First().ContentObjectId); // yields 4
Console.ReadLine();
}
private IEnumerable<ContentObjects> GetParentsOf(int id, ContentObjects root)
{
if (root.ChildContentObjects != null)
{
foreach (ContentObjects c in root.ChildContentObjects)
{
// If a direct child has the requested ID, we are a parent.
if (c.ContentObjectId == id)
{
yield return root;
}
// Recurse deeper down.
foreach (ContentObjects found in GetParentsOf(id, c))
{
yield return found;
}
}
}
}
If, on the other hand, by "parents" you mean the complete path down the tree, we need to modify the method as follows. We again have a recursive depth-first search, but we insert our own id into the path when returning from a successful recursive step:
static void Main(string[] args)
{
Program obj = new Program();
var path = obj.GetPathTo(5, obj.getdata().contentObjects);
// prints 1, 4
foreach (ContentObjects o in path)
{
Console.WriteLine(o.ContentObjectId);
}
Console.ReadLine();
}
// returns null if id could not be found
private IEnumerable<ContentObjects> GetPathTo(int id, ContentObjects root)
{
if (root.ChildContentObjects != null)
{
foreach (ContentObjects c in root.ChildContentObjects)
{
if (c.ContentObjectId == id)
{
// If a direct child has the requested ID, we are the first parent.
return new[] { root };
}
else
{
// Recurse deeper down.
var found = GetPathTo(id, c);
if (found != null)
{
// We found something deeper down. Since we are part of the
// path, append own id.
return new[] { root }.Concat(found);
}
}
}
}
return null;
}
I have 2 lists that I need to check for common objects that are being passed to a generic wrapper.
The first list (selList) is a typed entity list. The ID field in this list is different, based on what the base type for the list being created.
The second list (masterList) is an anonymous IList that I know has 2 properties {ID, DESC} - ID (could be int or string), and description (string). I can get the value of the ID property in this list.
I would like to return an extension of the master list that has a boolean field indicating whether the item in the master list is contained in the selList.
I'm thinking that I'm somewhere along the lines of the Visitor pattern.
public class SelectionCriteria<T> : where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
}
LookupList = new List<object>
{
new { ID = "fid", DESC = "Record 1"},
new { ID = "Record2", DESC = "Record 2"},
new { ID = "Record3", DESC = "Record 3"},
new { ID = "Record4", DESC = "Record 4"},
};
EligibleList = new List<AssetClass>
{
new AssetClass { FEE_ID = "fid", ASSET_CLASS = "A" },
};
I should get the following results:
LookupList[0] == true
LookupList[1] == false
LookupList[2] == false
LookupList[3] == false
Is there a better way to solve this problem?
var results = LookupList.Select(l => EligibleList.Any(e => e.FEE_ID==l.ID))
.ToList();
Using this as a definition for SelectionCriteria<T>
public class SelectionCriteria<T>
where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
public SelectionCriteria(IList lookupList, IList<T> eligibleList)
{
LookupList = lookupList;
EligibleList = eligibleList;
}
public bool this[int index]
{
get
{
var element = LookupList[index];
foreach (var item in EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
}
And this as a definition for AssetClass
public class AssetClass : IEquatable<AssetClass>
{
public string FEE_ID { get; set; }
public string ASSET_CLASS { get; set; }
public bool Equals(AssetClass other)
{
return !ReferenceEquals(other, null) && other.FEE_ID == FEE_ID && other.ASSET_CLASS == ASSET_CLASS;
}
//Check to see if obj is a value-equal instance of AssetClass, if it's not, proceed
// to doing some reflection checks to determine value-equality
public override bool Equals(object obj)
{
return Equals(obj as AssetClass) || PerformReflectionEqualityCheck(obj);
}
//Here's where we inspect whatever other thing we're comparing against
private bool PerformReflectionEqualityCheck(object o)
{
//If the other thing is null, there's nothing more to do, it's not equal
if (ReferenceEquals(o, null))
{
return false;
}
//Get the type of whatever we got passed
var oType = o.GetType();
//Find the ID property on it
var oID = oType.GetProperty("ID");
//Get the value of the property
var oIDValue = oID.GetValue(o, null);
//If the property type is string (so that it matches the type of FEE_ID on this class
// and the value of the strings are equal, then we're value-equal, otherwise, we're not
return oID.PropertyType == typeof (string) && FEE_ID == (string) oIDValue;
}
}
You can get elements that are found in the list of eligible items that exist in the list of lookup items like so:
for (var i = 0; i < assetClassSelectionCriteria.LookupList.Count; ++i)
{
Console.WriteLine("LookupList[{0}] == {1}", i, assetClassSelectionCriteria[i]);
}
You could also use the following for PerformReflectionEqualityCheck in AssetClass if you don't like seeing the reflection goodness
private bool PerformReflectionEqualityCheck(object o)
{
if (ReferenceEquals(o, null))
{
return false;
}
dynamic d = o;
try
{
return FEE_ID == (string) d.ID;
}
catch
{
return false;
}
}
If by "extension of the master list" you meant an extension method, then, instead of declaring an indexer on SelectionCriteria<T> to get the results, you could do something like this:
public static class SelectionCriteriaExtensions
{
public static bool IsLookupItemEligible<T>(this SelectionCriteria<T> set, int index)
where T : class
{
var element = set.LookupList[index];
foreach (var item in set.EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
and call it like this:
assetClassSelectionCriteria.IsLookupItemEligible(0);
I am using Lucene.NET and able to search get hit results as ScoreDoc[].
I need to know specific item position in ScoreDoc[]. All items in ScoreDoc[] are unique.
Sample code:
luceneSearcher.Search(query, collector);
ScoreDoc[] scores = collector.TopDocs().scoreDocs
For example, I need to get find item position in ScoreDoc[], which has custom ID property where value could be '99999'.
I can iterate through item in scores[] and check for ID property which matches '99999' then return the position, but this can have performance hit because scores[] can have thousands of items.
Is there any better technique?
Thanks
I came up with creating new ExtendedCollector which stores CollectedDocuments.
public class ExtendedCollector : Collector
{
private Scorer _scorer;
private Int32 _docBase;
private List<CollectedDocument> _documents;
public ExtendedCollector()
{
_documents = new List<CollectedDocument>();
}
public override void SetScorer(Scorer scorer)
{
_scorer = scorer;
}
public override void Collect(int doc)
{
var docId = _docBase + doc;
var score = _scorer.Score();
var currentDoc = _documents.FirstOrDefault(d => d.DocId == docId);
if (currentDoc == null)
_documents.Add(new CollectedDocument()
{DocId = docId, Score = score, OriginalIndex = _documents.Count, Index = _documents.Count});
else
currentDoc.Score = score;
}
public override void SetNextReader(IndexReader reader, int docBase)
{
_docBase = docBase;
}
public override bool AcceptsDocsOutOfOrder()
{
return false;
}
public List<CollectedDocument> Documents
{
get { return _documents; }
}
public List<CollectedDocument> DocumentsByScore
{
get
{
var result = _documents.OrderByDescending(d => d.Score).ToList();
var itemId = 0;
foreach (var collectedDocument in result)
{
itemId++;
collectedDocument.Index = itemId;
}
return result;
}
}
}
CollectedDocument looks like this
public class CollectedDocument
{
public Int32 DocId { get; set; }
public float Score { get; set; }
public int OriginalIndex { get; set; }
public int Index { get; set; }
}
Whenever you want to get results you would do
var myCollector = new ExtendedCollector();
searcher.Search(searchQuery, myCollector);
foreach (var doc in myCollector.Documents)
{
var docIndex = doc.Index; //this is the current index in a list
var originalIndex = doc.OriginalIndex; //this is item Id set when doc was collected
}
You can also get the documents ordered by score using
myCollector.DocumentsByScore
This might not be the easiest solution, but it works. If anyone has a better solution, please post it as I'd like to know that as well.