Recursive Hierarchy - Recursive Query Using Linq

Recursive Hierarchy - Recursive Query using Linq

While it is possible to use a recursive method here, you can traverse this tree structure using an explicit stack instead to avoid using the stack space, which isn't always sufficient for large tree structures. Such a method is also very nice as an iterator block, and iterator blocks are much less expensive when recursive than regular methods, so this will perform better as well:

public static IEnumerable<T> Traverse<T>(this IEnumerable<T> items, 
Func<T, IEnumerable<T>> childSelector)
{
var stack = new Stack<T>(items);
while(stack.Any())
{
var next = stack.Pop();
yield return next;
foreach(var child in childSelector(next))
stack.Push(child);
}
}

Recursive LINQ query: select item and all children with subchildren

   public class Comment
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Text { get; set; }
public List<Comment> Children { get; set; }
}

class Program
{
static void Main()
{
List<Comment> categories = new List<Comment>()
{
new Comment () { Id = 1, Text = "Item 1", ParentId = 0},
new Comment() { Id = 2, Text = "Item 2", ParentId = 0 },
new Comment() { Id = 3, Text = "Item 3", ParentId = 0 },
new Comment() { Id = 4, Text = "Item 1.1", ParentId = 1 },
new Comment() { Id = 5, Text = "Item 3.1", ParentId = 3 },
new Comment() { Id = 6, Text = "Item 1.1.1", ParentId = 4 },
new Comment() { Id = 7, Text = "Item 2.1", ParentId = 2 }
};

List<Comment> hierarchy = new List<Comment>();
hierarchy = categories
.Where(c => c.ParentId == 0)
.Select(c => new Comment() {
Id = c.Id,
Text = c.Text,
ParentId = c.ParentId,
Children = GetChildren(categories, c.Id) })
.ToList();

HieararchyWalk(hierarchy);

Console.ReadLine();
}

public static List<Comment> GetChildren(List<Comment> comments, int parentId)
{
return comments
.Where(c => c.ParentId == parentId)
.Select(c => new Comment {
Id = c.Id,
Text = c.Text,
ParentId = c.ParentId,
Children = GetChildren(comments, c.Id) })
.ToList();
}

public static void HieararchyWalk(List<Comment> hierarchy)
{
if (hierarchy != null)
{
foreach (var item in hierarchy)
{
Console.WriteLine(string.Format("{0} {1}", item.Id, item.Text));
HieararchyWalk(item.Children);
}
}
}

Recursive/Hierarchical Query in LINQ

Try this:

var geo_hierarchy = new []
{
new { cat_id = "Root", geo_id = 1, parent_id = (int?)null, geo_name = "Pakistan", },
new { cat_id = "Province", geo_id = 2, parent_id = (int?)1, geo_name = "Punjab", },
new { cat_id = "District", geo_id = 3, parent_id = (int?)2, geo_name = "Attock", },
new { cat_id = "City", geo_id = 4, parent_id = (int?)3, geo_name = "Attock", },
new { cat_id = "City", geo_id = 5, parent_id = (int?)3, geo_name = "Fateh Jang", },
new { cat_id = "City", geo_id = 6, parent_id = (int?)3, geo_name = "Hasan Abdal", },
};

var map = geo_hierarchy.ToDictionary(x => x.geo_id);

Func<int, string, string> up = null;
up = (geo_id, cat_id) =>
{
var record = map[geo_id];
return
record.cat_id == cat_id
? record.geo_name
: (record.parent_id.HasValue
? up(record.parent_id.Value, cat_id)
: null);
};

var result = up(5, "Province"); // -> "Punjab"

Linq recursive parent child

For linq-to-objects you can define your own extension method on IEnumerable<T> that recursively gets all children.

public static class EnumerableExtensions
{
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
foreach (var parent in source)
{
yield return parent;

var children = selector(parent);
foreach (var child in SelectRecursive(children, selector))
yield return child;
}
}
}

Usage:

var lookup = col.ToLookup(x => x.Parent_Id);
var res = lookup[null].SelectRecursive(x => lookup[x.Id]).ToList();

LINQ recursive query to return hierarchical set of groups

So first, your TeamGrouping is actually a bit more complex than it needs to be. All it needs is the Team object and a sequence of itself for children:

public class TeamNode
{
public Team Value { get; set; }
public IEnumerable<TeamNode> Children { get; set; }
}

Next we'll take our sequence of teams and create a node for each one. Then we'll use ToLookup to group them by their parent ID. (Your use of GroupBy is pretty darn close to this, but ToLookup will be easier.) Finally we can just set each node's children to be the lookup value for that node (note that ILookup will return an empty sequence if the key doesn't exist, so our leaves will be handled perfectly). To finish it off we can return all of the top level nodes by just looking up all nodes with a parent ID of null.

public static IEnumerable<TeamNode> CreateTree(IEnumerable<Team> allTeams)
{
var allNodes = allTeams.Select(team => new TeamNode() { Value = team })
.ToList();
var lookup = allNodes.ToLookup(team => team.Value.ParentTeamId);
foreach (var node in allNodes)
node.Children = lookup[node.Value.TeamId];
return lookup[null];
}

Recursive LINQ get hierarchy?

Try following recursive code. You tables doesn't have a lot of parent/child rows so I didn't get a lot of results. You probably will get better results with actual data

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace ConsoleApplication65
{
class Program
{
static DataTable dt = null;
static void Main(string[] args)
{
dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Per_Router" , typeof(int));
dt.Columns.Add("Prod_No", typeof(int));
dt.Columns.Add("Rout_No", typeof(int));

dt.Rows.Add(new object[] {null, 1, 81235, 77976});
dt.Rows.Add(new object[] {null, 1, 67907, 77976});
dt.Rows.Add(new object[] {null, 1, 66772, 77976 });
dt.Rows.Add(new object[] {21202, 2, null, 77976 });
dt.Rows.Add(new object[] {41978, 1, null, 77976 });
dt.Rows.Add(new object[] {41979, 1, null, 77976});
dt.Rows.Add(new object[] {20373, 11, null, 81253 });
dt.Rows.Add(new object[] {20377, 1, null, 81253 });
dt.Rows.Add(new object[] {20379, 1, null, 81253 });
dt.Rows.Add(new object[] {20388, 4, null, 81253});
dt.Rows.Add(new object[] {20265, 1, null, 81235});
dt.Rows.Add(new object[] {28957, 1, null, 81235});
dt.Rows.Add(new object[] {null, 1, 53755, 81235});
dt.Rows.Add(new object[] {null, 1, 53788, 81235});
dt.Rows.Add(new object[] {null, 1, 59516, 81235});

Console.WriteLine("Id Per_Router Prod_No Rout_No");

ParseTableRecursive(null);

Console.ReadLine();
}

static void ParseTableRecursive(int? parent)
{
List<DataRow> group = dt.AsEnumerable().Where(x => x.Field<int?>("Prod_No") == parent).ToList();
if(group.Count > 0)
{
Console.WriteLine();
Console.WriteLine("Sub-components of {0}", parent == null ? "NULL" : parent.ToString());
Console.WriteLine();

foreach (DataRow row in group)
{
Console.WriteLine("{0:5} {1:5} {2:5} {3:5}",
row.Field<int?>("Id") == null ? "NULL" : row.Field<int?>("Id").ToString(),
row.Field<int?>("Per_Router") == null ? "NULL" : row.Field<int?>("Per_Router").ToString(),
row.Field<int?>("Prod_No") == null ? "NULL" : row.Field<int?>("Prod_No").ToString(),
row.Field<int?>("Rout_No") == null ? "NULL" : row.Field<int?>("Rout_No").ToString()
);

}

foreach (int? child in group.Select(x => x.Field<int?>("Rout_No")).Distinct())
{
ParseTableRecursive(child);
}
}
}
}
}

Usage recursive hierarchy tree structure in Linq query with multible tables and return some Json Value

The RecursiveJoin Extension looks pretty nice, but I don't think you need the full power of that code to accomplish your objective. If I understand your question correctly, you need to enumerate over all lists of nodes from root to leaf, dumping each list into a JSON object. To do that, first create the following extension methods:

public static class RecursiveExtensions
{
public static IEnumerable<TValue> SelfAndParents<TKey, TValue>(this TValue value, IDictionary<TKey, TValue> dictionary, Func<TValue, TKey> getParentKey)
{
HashSet<TValue> returned = new HashSet<TValue>();
do
{
yield return value;
if (!dictionary.TryGetValue(getParentKey(value), out value))
yield break;
if (returned.Contains(value))
throw new InvalidOperationException("Circular reference");
returned.Add(value);
}
while (true);
}

public static IEnumerable<List<TValue>> FlattenTree<TKey, TValue>(this IEnumerable<TValue> nodes, Func<TValue, TKey> getKey, Func<TValue, TKey> getParentKey)
{
var list = nodes.ToList(); // Don't iterate through the node list more than once.
var parentKeys = new HashSet<TKey>(list.Select(getParentKey)); // Built a set of parent keys.
var dict = list.ToDictionary(getKey); // Build a dictionary of key to value
var results = list
.Where(node => !parentKeys.Contains(getKey(node))) // Filter out non-leaf nodes
.Select(node => node.SelfAndParents(dict, getParentKey).Reverse().ToList()); // For each leaf return a list going from root to leaf.
return results;
}
}

Then use them as follows:

public static class TestFlatten
{
public static IEnumerable<FlatData> GetFlatData()
{
// Get sample data for testing purposes.
var list = new List<FlatData>
{
new FlatData { Id = 1, ParentId = 0, Text = "Some Root Node" },
new FlatData { Id = 2, ParentId = 0, Text = "Anadolu Satış Merkezi" },
new FlatData { Id = 3, ParentId = 2, Text = "Emrullah Çelik" },
new FlatData { Id = 4, ParentId = 3, Text = "Ahmet İşler" },
new FlatData { Id = 5, ParentId = 4, Text = "Elpa Pazarlama Nazmi Appak" },
new FlatData { Id = 6, ParentId = 4, Text = "Elpa Pazarlama Nazmi Appak Redux" },
new FlatData { Id = 11, ParentId = 1, Text = "Some Child of Some Root Node" },
};
return list;
}

public static void Test()
{
var nodes = GetFlatData();
var flatNodes = nodes.FlattenTree(d => d.Id, d => d.ParentId);
var results = flatNodes.Select(
list => list
.Select((d, i) => new KeyValuePair<int, FlatData>(i, d))
.ToDictionary(pair => string.Format("level{0}", pair.Key + 1), pair => pair.Value.Text))
.ToList();
var json = JsonConvert.SerializeObject(new { data = results }, Formatting.Indented);
Debug.WriteLine(json);
}
}

This produces the JSON output:

{
"data": [
{
"level1": "Anadolu Satış Merkezi",
"level2": "Emrullah Çelik",
"level3": "Ahmet İşler",
"level4": "Elpa Pazarlama Nazmi Appak"
},
{
"level1": "Anadolu Satış Merkezi",
"level2": "Emrullah Çelik",
"level3": "Ahmet İşler",
"level4": "Elpa Pazarlama Nazmi Appak Redux"
},
{
"level1": "Some Root Node",
"level2": "Some Child of Some Root Node"
}
]
}

Is this what you want? Here I am taking advantage of the fact that Json.NET serializes a dictionary as a JSON object to dynamically create the "levelI" properties as dictionary keys. Also, as you did not define the "val" value, I didn't include it in the dictionary.



Related Topics



Leave a reply



Submit