I'm trying to build a structure like this :
- Parent 1
- Child 1
- Super child 1
- Child 2
- Super Child 2
- Super super Child 2
- Super Child 2
- Child 1
I don't know how many branches there will be.
I need to build my structure with an array of paths like this :
- Parent1/Child1/SuperChild1
- Parent1/Child2/SuperChild2/SupersuperChild2
I made a Model as following :
public class Person
{
public string Name { get; set; }
public string FullPath { get; set; }
public List<Person> Children { get; set; }
}
I split every paths to an array and then I interate inside with foreach :
string[] list =
{
"Parent1/Child1/SuperChild1/SupersuperChild1",
"Parent1/Child1/SuperChild1/SupersuperChild2",
"Parent2/Child2/SuperChild2/SupersuperChild3",
"Parent2/Child3/SuperChild3/SupersuperChild4",
"Parent2/Child3/SuperChild3/SupersuperChild5"
};
var branches = new List<Person>();
foreach (var tag in list)
{
var tagSlash = tag.Replace("[", "").Replace("]", "/").Split('/');
Person parent = null;
foreach (var step in tagSlash)
{
Person branch = null;
if (parent == null && branches.Find(b => b.Name.Equals(step)) == null)
{
branch = new Person
{
Name = step,
FullPath = tagSlash.Last().Equals(step) ? tag : null,
Children = new List<Person>()
};
branches.Add(branch);
}
else if (parent == null && branches.Find(b => b.Name.Equals(step)) != null)
{
branch = branches.Find(b => b.Name.Equals(step));
}
else if (parent.Children.Find(c => c.Name.Equals(step)) != null)
{
branch = parent.Children.Find(c => c.Name.Equals(step));
}
else
{
var ancestor =
branch = new Person
{
Name = step,
FullPath = tagSlash.Last().Equals(step) ? tag : null,
Children = new List<Person>()
};
}
parent = branch;
}
}
The previous code isn't finished because I'm stuck.
Do you have an idea on how to build that structure ?
Thank you.
CodePudding user response:
something recursive? like:
string[] list =
{
"Parent1/Child1/SuperChild1/SupersuperChild1",
"Parent1/Child1/SuperChild1/SupersuperChild2",
"Parent2/Child2/SuperChild2/SupersuperChild3",
"Parent2/Child3/SuperChild3/SupersuperChild4",
"Parent2/Child3/SuperChild3/SupersuperChild5"
};
var branches = new List<Person>();
foreach (var tag in list)
{
var tagSlash = tag.Replace("[", "").Replace("]", "/").Split('/');
var p1 = branches.FirstOrDefault(x => x.Name == tagSlash[0]);
if (p1 == null)
{
p1 = new Person()
{
Name = tagSlash[0],
FullPath = tagSlash[0]
};
branches.Add(p1);
}
MakeList.IterateListStep(p1, tagSlash, 1);
}
public static class MakeList
{
public static void IterateListStep(Person parent, string[] tags, int level)
{
if(tags.Length <= level)
return;
var pers = parent.Children.FirstOrDefault(x => x.Name == tags[level]);
if (pers == null)
{
pers = new Person()
{
Name = tags[level],
FullPath = parent.FullPath "//" tags[level],
};
parent.Children.Add(pers);
}
IterateListStep(pers, tags, level 1);
}
}
Do make sure you initialize your children list
public List<Person> Children { get; set; } = new List<Person>();
CodePudding user response:
Here is tested code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Data;
namespace ConsoleApplication51
{
class Program
{
static void Main(string[] args)
{
string[] list =
{
"Parent1/Child1/SuperChild1/SupersuperChild1",
"Parent1/Child1/SuperChild1/SupersuperChild2",
"Parent2/Child2/SuperChild2/SupersuperChild3",
"Parent2/Child3/SuperChild3/SupersuperChild4",
"Parent2/Child3/SuperChild3/SupersuperChild5"
};
List<List<string>> people = list.Select(x => x.Split(new char[] {'/'}).ToList()).ToList();
Person root = Person.BuildTree(people);
}
}
public class Person
{
public string Name { get; set; }
public string FullPath { get; set; }
public List<Person> Children { get; set; }
public static Person BuildTree(List<List<string>> people)
{
Person root = new Person();
root.Name = "Root";
int level = 0;
BuildTreeRecursive(root, people, level);
return root;
}
public static void BuildTreeRecursive(Person parent, List<List<string>> people, int level)
{
var groups = people.GroupBy(x => x[level]).ToList();
foreach (var group in groups)
{
if(parent.Children == null) parent.Children = new List<Person>();
Person child = new Person();
parent.Children.Add(child);
child.Name = group.Key;
child.FullPath = string.Join("/", group.First().Take(level 1));
List<List<string>> descendnats = group.Where(x => x.Count() > level 1).ToList();
if (descendnats.Count > 0)
{
BuildTreeRecursive(child, descendnats, level 1);
}
}
}
}
}
CodePudding user response:
I've isolated an INode
interface and made a generic tree builder function, compiling here.
public interface INode<T>
{
public string Name { get; }
public string FullPath { get; }
public IList<T> Children { get; }
}
public static class Extensions
{
public static IEnumerable<T> BuildTrees<T>(
this IEnumerable<string> paths,
Func<string, string, T> nodeFactory,
string delimiter = "/")
where T : INode<T>
{
var nodes = new Dictionary<string, T>();
var roots = new List<T>();
foreach(var path in paths)
{
string fullPath = null;
T parent = default;
T node = default;
foreach(var name in path.Split(delimiter))
{
var root = false;
if (fullPath == null)
{
root = true;
fullPath = name;
}
else
{
fullPath = $"{fullPath}{delimiter}{name}";
parent = node;
}
if (nodes.ContainsKey(fullPath))
{
node = nodes[fullPath];
}
else
{
node = nodeFactory(name, fullPath);
nodes.Add(fullPath, node);
if (root)
{
roots.Add(node);
}
else
{
parent.Children.Add(node);
}
}
}
}
return roots;
}
}
Which you could use like this,
public class Person : INode<Person>
{
public string Name { get; set; }
public string FullPath { get; set; }
public IList<Person> Children { get; set; } = new List<Person>();
}
public class Program
{
public static void Main()
{
string[] list =
{
"Parent1/Child1/SuperChild1/SupersuperChild1",
"Parent1/Child1/SuperChild1/SupersuperChild2",
"Parent2/Child2/SuperChild2/SupersuperChild3",
"Parent2/Child3/SuperChild3/SupersuperChild4",
"Parent2/Child3/SuperChild3/SupersuperChild5"
};
var roots = list.BuildTrees<Person>(
(name, fullPath) => new Person { Name = name, FullPath = fullPath });
}
}