So I've been scavenging the forums for a solution to this problem and I have this method for finding a node in a graph given a predicate:
public static class Extensions
{
public static IEnumerable<T> FindWhere<T>(this T root, Func<T, bool> predicate,
Func<T, IEnumerable<T>> getChildren)
where T : class
{
var stack = new Stack<T>();
stack.Push(root);
while (stack.Count != 0)
{
T item = stack.Pop();
if (item != null)
{
yield return item;
}
foreach (var child in getChildren(item))
stack.Push(child);
}
}
}
This should work most of the way, but how do I get the predicate into the method so that I can find any node in the graph that I wish to find? Any hints appreciated.
You can use a lambda expression like this:
myClass.FindWhere(x => x.HasSomeProperty, y => y.GetChildren())
Or you can declare methods with the right signature and pass those in.
bool IsOkay(MyClass mc) { ... }
IEnumerable<MyClass> GetChildren(MyClass mc) { ... }
...
myClass.FindWhere(IsOkay, GetChildren)
Related
I need to accumulate values into a collection, based on an arbitrary function. Each value is derived from calling a function on the previous value.
My current attempt:
public static T[] Aggregate<T>(this T source, Func<T, T> func)
{
var arr = new List<T> { };
var current = source;
while(current != null)
{
arr.Add(current);
current = func(current);
};
return arr.ToArray();
}
Is there a built-in .Net Framework function to do this?
This operation is usually called Unfold. There's no built-in version but it is implemented in FSharp.Core, so you could wrap that:
public static IEnumerable<T> Unfold<T, TState>(TState init, Func<TState, T> gen)
{
var liftF = new Converter<TState, Microsoft.FSharp.Core.FSharpOption<Tuple<T, TState>>>(x =>
{
var r = gen(x);
if (r == null)
{
return Microsoft.FSharp.Core.FSharpOption<Tuple<T, TState>>.None;
}
else
{
return Microsoft.FSharp.Core.FSharpOption<Tuple<T, TState>>.Some(Tuple.Create(r, x));
}
});
var ff = Microsoft.FSharp.Core.FSharpFunc<TState, Microsoft.FSharp.Core.FSharpOption<Tuple<T, TState>>>.FromConverter(liftF);
return Microsoft.FSharp.Collections.SeqModule.Unfold<TState, T>(ff, init);
}
public static IEnumerable<T> Unfold<T>(T source, Func<T, T> func)
{
return Unfold<T>(source, func);
}
however writing your own version would be simpler:
public static IEnumerable<T> Unfold<T>(T source, Func<T, T> func)
{
T current = source;
while(current != null)
{
yield return current;
current = func(current);
}
}
You are referring to an anamorphism as mentioned here linq-unfold-operator, which is the dual of a catamorphism.
Unfold is the dual of Aggregate. Aggregate exists in the .Net Framework; Unfold does not (for some unknown reason). Hence your confusion.
/// seeds: the initial data to unfold
/// stop: if stop(seed) is True, don't go any further
/// map: transform the seed into the final data
/// next: generate the next seed value from the current seed
public static IEnumerable<R> UnFold<T,R>(this IEnumerable<T> seeds, Predicate<T> stop,
Func<T,R> map, Func<T,IEnumerable<T>> next) {
foreach (var seed in seeds) {
if (!stop(seed)) {
yield return map(seed);
foreach (var val in next(seed).UnFold(stop, map, next))
yield return val;
}
}
}
Usage Example:
var parents = new[]{someType}.UnFold(t => t == null, t => t,
t => t.GetInterfaces().Concat(new[]{t.BaseType}))
.Distinct();
in an mvc application I've got a menu composed of the following structure
public class MenuItem
{
public string Action {get;set;}
public string Controller {get;set;}
public string Text {get;set;}
public List<MenuItem> Children {get;set;}
}
Consider a simple compisition of
Root
\-Item1
\-Item2
\-Item2_1
\-Item2_2
\-Item_2_2_1
\-Item_2_2_2
\-Item3
I want to get the item Item_2_2_2 (consider it has Action="Index", Controller="ABC")
How can I write a function (or better extension method of T) that iterates trought the collection and get the item it matches that condition ?
Thanks
You can write an extension method to "flatten" hierarchies...
public static IEnumerable<T> Flatten<T>(this T value, Func<T, IEnumerable<T>> inner) {
foreach (var i in inner(value)) {
foreach (var j in Flatten(i, inner)) {
yield return j;
}
}
yield return value;
}
...then use ordinary LINQ:
items.Flatten(i => i.Children).Where(i => ...
Since the question is about "get the items matches on a condition", it's possible to directly include the search criteria into the recursion, and by this way, optimize the graph traversal by yielding only once over the collection :
Moreover, since you can use a .Take(10) operator over the result, and since there are different ways of walking over a graph, I included a depth-first / breadth-first option.
Moreover bis, since we are on Stackoverflow, and even if the default max stack size on .Net is of 1MB, it's improbable but not impossible to throw a StackOverflowException. So here's also a safe, non-recursive version of the depth-first graph traversal.
public static IEnumerable<T> Search<T>(this T node, Func<T, IEnumerable<T>> childs, Func<T, bool> condition, GraphTraversal mode = GraphTraversal.DepthFirst)
{
if (node == null || childs == null || condition == null)
throw new ArgumentNullException();
if (mode == GraphTraversal.DepthFirst)
return node.depthFirstTraversal(childs, condition);
else if (mode == GraphTraversal.DepthFirstNoStackOverflow)
return node.depthFirstTraversalWithoutStackoverflow(childs, condition);
else
return node.breadthFirstTraversal(childs, condition);
}
private static IEnumerable<T> depthFirstTraversal<T>(this T node, Func<T, IEnumerable<T>> childs, Func<T, bool> condition)
{
IEnumerable<T> childrens = childs(node);
if (childrens == null)
yield break;
if (condition(node))
yield return node;
foreach (T i in childrens)
{
foreach (T j in depthFirstTraversal(i, childs, condition))
{
if (condition(j))
yield return j;
}
}
}
private static IEnumerable<T> breadthFirstTraversal<T>(this T node, Func<T, IEnumerable<T>> childs, Func<T, bool> condition)
{
Queue<T> queue = new Queue<T>();
queue.Enqueue(node);
while (queue.Count > 0)
{
T currentnode = queue.Dequeue();
if (condition(currentnode))
yield return currentnode;
IEnumerable<T> childrens = childs(currentnode);
if (childrens != null)
{
foreach (T child in childrens)
queue.Enqueue(child);
}
}
}
private static IEnumerable<T> depthFirstTraversalWithoutStackoverflow<T>(this T node, Func<T, IEnumerable<T>> childs, Func<T, bool> condition)
{
Stack<T> stack = new Stack<T>();
stack.Push(node);
while (stack.Count > 0)
{
T currentnode = stack.Pop();
if (condition(currentnode))
yield return currentnode;
var childrens = childs(currentnode);
if (childrens != null)
{
foreach (var child in childrens)
stack.Push(child);
}
}
}
public enum GraphTraversal { DepthFirst, DepthFirstNoStackOverflow, BreadthFirst }
You use it like this :
var found = rootItem.Search(i => i.Children, i => i.Action == "Index" && i.Controller == "ABC");
Alex's solution is good, but when doing
items.Flatten(i => i.Children).Where(i => ...
the collection is fully yielded one time with Flatten, and one other time with Where, and lacks some null checks.
As Alex pointed it, the iteration is deferred, but since each IEnumerable<T> operator (and each foreach) need to call enumerator.GetNext(), so there will be performance improvement by integrating the predicate directly into the recursion loop.
This question already has answers here:
How to search Hierarchical Data with Linq
(8 answers)
Closed 9 years ago.
Imagine an object with the properties:
class TestObject
{
public string Name { get; set; }
public Collection<TestObject> Children { get; set; }
}
Now initialize some in a jagged fashion:
var person1 = new TestObject(){
Name = "Joe",
Children = new Collection<TestObject>(){ childCollection1 };
};
var person2 = new TestObject(){
Name = "Mary",
Children = new Collection<TestObject>(){ childCollection2 };
};
Where Joe's childCollection is only one level deep, but Mary's children have children, who also have children.
I have attempted to use SelectMany with no luck.
// Works
var joe = person1.Children.SelectMany(c => c.Children).Concat(person1.Children);
// Does not work - only returns 1 level deep
var mary = person2.Children.SelectMany(c => c.Children).Concat(person2.Children);
What is the best way to retrieve a result containing every child, to an unknown depth?
You can write a generic traverse method like this:
public static IEnumerable<T> Traverse<T>(T root,
Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
yield return next;
foreach (var child in childSelector(next))
stack.Push(child);
}
}
This is a general model that's useful for traversing trees in general. Note that this will do a depth first search. If you want a breath first search you would use a Queue<T> instead of a Stack<T>.
Helper method
public static IEnumerable<T> Traversal<T>(
T root,
Func<T, IEnumerable<T>> getChildren)
{
if (root == null)
{
yield break;
}
yield return root;
var children = getChildren(root);
if (children == null)
{
yield break;
}
foreach (var child in children)
{
foreach (var node in Traversal(child, getChildren))
{
yield return node;
}
}
}
//Or if you don't need all those null checks, here's a more compact version.
public static IEnumerable<T> Traversal<T>(
T root,
Func<T, IEnumerable<T>> getChildren)
{
yield return root;
foreach (var child in getChildren(root))
foreach (var node in Traversal(child, getChildren))
yield return node;
}
//If you like a LINQ/functional style better, this is also equivalent.
public static IEnumerable<T> Traversal<T>(
T root,
Func<T, IEnumerable<T>> getChildren)
{
return new T[] { root }
.Concat(getChildren(root)
.SelectMany(child => Traversal(child, getChildren)));
}
Usage
var everybody = Traversal(person, x => x.Children);
Comments
You can easily modify the Traversal method to behave exactly the way you want. For example, if you only want leaf nodes, then you should only yield return root; when children is null or empty.
Performance concerns
If performance is any kind of issue, consider the LINQ/functional implementation above or take a look at Servy's answer, either of which should be more efficient than the version using yield ....
I have a tree created from this class.
class Node
{
public string Key { get; }
public List<Node> Children { get; }
}
I want to search in all children and all their children to get the ones matching a condition:
node.Key == SomeSpecialKey
How can I implement it?
It's a misconception that this requires recursion. It will require a stack or a queue and the easiest way is to implement it using recursion. For sake of completeness I'll provide a non-recursive answer.
static IEnumerable<Node> Descendants(this Node root)
{
var nodes = new Stack<Node>(new[] {root});
while (nodes.Any())
{
Node node = nodes.Pop();
yield return node;
foreach (var n in node.Children) nodes.Push(n);
}
}
Use this expression for example to use it:
root.Descendants().Where(node => node.Key == SomeSpecialKey)
If you want to maintain Linq like syntax, you can use a method to obtain all the descendants (children + children's children etc.)
static class NodeExtensions
{
public static IEnumerable<Node> Descendants(this Node node)
{
return node.Children.Concat(node.Children.SelectMany(n => n.Descendants()));
}
}
This enumerable can then be queried like any other using where or first or whatever.
Searching a Tree of Objects with Linq
public static class TreeToEnumerableEx
{
public static IEnumerable<T> AsDepthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
{
yield return head;
foreach (var node in childrenFunc(head))
{
foreach (var child in AsDepthFirstEnumerable(node, childrenFunc))
{
yield return child;
}
}
}
public static IEnumerable<T> AsBreadthFirstEnumerable<T>(this T head, Func<T, IEnumerable<T>> childrenFunc)
{
yield return head;
var last = head;
foreach (var node in AsBreadthFirstEnumerable(head, childrenFunc))
{
foreach (var child in childrenFunc(node))
{
yield return child;
last = child;
}
if (last.Equals(node)) yield break;
}
}
}
You can try this extension method to enumerate the tree nodes:
static IEnumerable<Node> GetTreeNodes(this Node rootNode)
{
yield return rootNode;
foreach (var childNode in rootNode.Children)
{
foreach (var child in childNode.GetTreeNodes())
yield return child;
}
}
Then use that with a Where() clause:
var matchingNodes = rootNode.GetTreeNodes().Where(x => x.Key == SomeSpecialKey);
Why not use an IEnumerable<T> extension method
public static IEnumerable<TResult> SelectHierarchy<TResult>(this IEnumerable<TResult> source, Func<TResult, IEnumerable<TResult>> collectionSelector, Func<TResult, bool> predicate)
{
if (source == null)
{
yield break;
}
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
var childResults = SelectHierarchy(collectionSelector(item), collectionSelector, predicate);
foreach (var childItem in childResults)
{
yield return childItem;
}
}
}
then just do this
var result = nodes.Children.SelectHierarchy(n => n.Children, n => n.Key.IndexOf(searchString) != -1);
Perhaps you need just
node.Children.Where(child => child.Key == SomeSpecialKey)
Or, if you need to search one level deeper,
node.Children.SelectMany(
child => child.Children.Where(child => child.Key == SomeSpecialKey))
If you need to search on all levels, take the following:
IEnumerable<Node> FlattenAndFilter(Node source)
{
List<Node> l = new List();
if (source.Key == SomeSpecialKey)
l.Add(source);
return
l.Concat(source.Children.SelectMany(child => FlattenAndFilter(child)));
}
public class Node
{
string key;
List<Node> children;
public Node(string key)
{
this.key = key;
children = new List<Node>();
}
public string Key { get { return key; } }
public List<Node> Children { get { return children; } }
public Node Find(Func<Node, bool> myFunc)
{
foreach (Node node in Children)
{
if (myFunc(node))
{
return node;
}
else
{
Node test = node.Find(myFunc);
if (test != null)
return test;
}
}
return null;
}
}
And then you can search like:
Node root = new Node("root");
Node child1 = new Node("child1");
Node child2 = new Node("child2");
Node child3 = new Node("child3");
Node child4 = new Node("child4");
Node child5 = new Node("child5");
Node child6 = new Node("child6");
root.Children.Add(child1);
root.Children.Add(child2);
child1.Children.Add(child3);
child2.Children.Add(child4);
child4.Children.Add(child5);
child5.Children.Add(child6);
Node test = root.Find(p => p.Key == "child6");
And just for fun (almost a decade later) an answer also using Generics but with a Stack and While loop, based off the accepted answer by #vidstige.
public static class TypeExtentions
{
public static IEnumerable<T> Descendants<T>(this T root, Func<T, IEnumerable<T>> selector)
{
var nodes = new Stack<T>(new[] { root });
while (nodes.Any())
{
T node = nodes.Pop();
yield return node;
foreach (var n in selector(node)) nodes.Push(n);
}
}
public static IEnumerable<T> Descendants<T>(this IEnumerable<T> encounter, Func<T, IEnumerable<T>> selector)
{
var nodes = new Stack<T>(encounter);
while (nodes.Any())
{
T node = nodes.Pop();
yield return node;
if (selector(node) != null)
foreach (var n in selector(node))
nodes.Push(n);
}
}
}
Given a collection one can use like this
var myNode = ListNodes.Descendants(x => x.Children).Where(x => x.Key == SomeKey);
or with a root object
var myNode = root.Descendants(x => x.Children).Where(x => x.Key == SomeKey);
A while back I wrote a codeproject article which describes how to use Linq to query tree-like structures:
http://www.codeproject.com/KB/linq/LinqToTree.aspx
This provides a linq-to-XML style API where you can search descendants, children, ancestors etc...
Probably overkill for your current problem, but might be of interest to others.
You can use this extension method to query the tree.
public static IEnumerable<Node> InTree(this Node treeNode)
{
yield return treeNode;
foreach (var childNode in treeNode.Children)
foreach (var flattendChild in InTree(childNode))
yield return flattendChild;
}
I have a generic extension method that can flatten any IEnumerable<T> and from that flattened collection, you can get the node you want.
public static IEnumerable<T> FlattenHierarchy<T>(this T node, Func<T, IEnumerable<T>> getChildEnumerator)
{
yield return node;
if (getChildEnumerator(node) != null)
{
foreach (var child in getChildEnumerator(node))
{
foreach (var childOrDescendant in child.FlattenHierarchy(getChildEnumerator))
{
yield return childOrDescendant;
}
}
}
}
Use this like this:
var q = from node in myTree.FlattenHierarchy(x => x.Children)
where node.Key == "MyKey"
select node;
var theNode = q.SingleOrDefault();
I use the following implementations for enumerating Tree items
public static IEnumerable<Node> DepthFirstUnfold(this Node root) =>
ObjectAsEnumerable(root).Concat(root.Children.SelectMany(DepthFirstUnfold));
public static IEnumerable<Node> BreadthFirstUnfold(this Node root) {
var queue = new Queue<IEnumerable<Node>>();
queue.Enqueue(ObjectAsEnumerable(root));
while (queue.Count != 0)
foreach (var node in queue.Dequeue()) {
yield return node;
queue.Enqueue(node.Children);
}
}
private static IEnumerable<T> ObjectAsEnumerable<T>(T obj) {
yield return obj;
}
BreadthFirstUnfold in implementation above uses queue of node sequences instead of nodes queue. This is not classic BFS algorithm way.
Is there an elegant way of doing this? Perhaps with Linq?
For something like this:
List<ControlCollection> list = { ... }
List<Control> merged = list.MergeAll();
EDIT: The final collection will be single dimensional in a sense that all controls will be there, not in a nested way.
How about this:
public static void Append(this System.Windows.Forms.Control.ControlCollection collection, System.Windows.Forms.Control.ControlCollection newCollection)
{
Control[] newControl = new Control[newCollection.Count];
newCollection.CopyTo(newControl, 0);
collection.AddRange(newControl);
}
Usage:
Form form1 = new Form();
Form form2 = new Form();
form1.Controls.Append(form2.Controls);
This will flatten the control tree:
public static void FlattenAndAppend(this System.Windows.Forms.Control.ControlCollection collection, System.Windows.Forms.Control.ControlCollection newCollection)
{
List<Control> controlList = new List<Control>();
FlattenControlTree(collection, controlList);
newCollection.AddRange(controlList.ToArray());
}
public static void FlattenControlTree(System.Windows.Forms.Control.ControlCollection collection, List<Control> controlList)
{
foreach (Control control in collection)
{
controlList.Add(control);
FlattenControlTree(control.Controls, controlList);
}
}
Linq has Concat and Union methods. Is one of those what you're looking for?
Tree extension method
static class TreeExtensions
{
public static IEnumerable<R>TraverseDepthFirst<T, R>(
this T t,
Func<T, R> valueselect,
Func<T, IEnumerable<T>> childselect)
{
yield return valueselect(t);
foreach (var i in childselect(t))
{
foreach (var item in i.TraverseDepthFirst(valueselect, childselect))
{
yield return item;
}
}
}
}
Usage:
Control c = root_form_or_control;
var allcontrols = c.TraverseDepthFirst(
x => x,
x => x.Controls.OfType<Control>())).ToList();
var merged = (from cc in list
from Control control in cc
select cc)
.ToList();
or (same thing with explicit LINQ method calls):
var merged = list.SelectMany(cc => cc.Cast<Control>()).ToList();
[EDIT] Reflecting the newly added nesting requirement:
static IEnumerable<T> FlattenTree<T>(
this IEnumerable<T> nodes,
Func<T, IEnumerable<T>> childrenSelector)
{
foreach (T node in nodes)
{
yield return node;
foreach (T child in childrenSelector(node))
{
yield return child;
}
}
}
var merged = list.SelectMany(cc => cc.Cast<Control>())
.FlattenTree(control => control.Controls.Cast<Control>())
.ToList();