从树或列表层次结构中查找对象

时间:2014-12-11 13:57:09

标签: c# linq recursion

我正在上课

public class Employee
{
    public Employee[] ChildOrg{get; set;}
    public string name {get; set;};
    public string id{get; set;};
}

如何从ID中找到特定员工?

我试图使用一些跟随功能。

private static Employee GetNode(Employee objEmployeeList, string id)
{
    if (objEmployeeList.ChildOrg==null)
    {
        return null;
    }
    foreach (var item in objEmployeeList.ChildOrg)
    {
        if (item.ID.Equals(id))
        {
            return (objEmployeeList)item;
        }
    }
    foreach (var item in objEmployeeList.ChildOrg)
    {
        return GetNode((objEmployeeList)item, id);
    }
    return null;
}

正如您所看到的,我正在尝试编写一些递归函数来获取员工。

如果你仔细看它只是第一个节点的底部。

然后它返回null而不会转到任何其他节点。

请告诉我如何使用linq纠正我的功能和其他方法来完成同样的工作?

编辑: -

我想访问特定节点及其兄弟节点。

6 个答案:

答案 0 :(得分:5)

课程中的一些变化和例程中的一些变化。

public class Employee
{
    public List<Employee> ChildOrg { get; set; }
    public string Name { get; set; }
    public string Id { get; set; }

    public Employee(string id, string name)
    {
        Id = id;
        Name = name;
        ChildOrg = new List<Employee>();
    }

    public Employee AddChildOrg(string id, string name)
    {
        var newEmployee = new Employee(id, name);
        ChildOrg.Add(newEmployee);
        return newEmployee;
    }

    public static Employee GetNode(Employee father, string id)
    {
        if (father != null)
        {
            if (father.Id.Equals(id))
                return father;


            if (father.ChildOrg != null)
                foreach (var child in father.ChildOrg)
                {
                    if (child.Id.Equals(id))
                        return child;

                    var employee = Employee.GetNode(child, id);

                    if (employee != null)
                        return employee;
                }
        }
        return null;
    }
}

一个小测试程序:

class Program
{
    static void Main(string[] args)
    {
        Employee root = new Employee(1.ToString(), "root");
        var e2 = root.AddChildOrg(2.ToString(), "2 second level");
        var e3 = e2.AddChildOrg(3.ToString(), "3 third level");
        var e1 = root.AddChildOrg(4.ToString(), "4 second level");
        var e5 = e1.AddChildOrg(5.ToString(), "5 third level");

        Console.WriteLine("Id 3 -> {0}", Employee.GetNode(root, "3").Name);
        Console.WriteLine("Id 1 -> {0}", Employee.GetNode(root, "1").Name);
        Console.WriteLine("Id 5 -> {0}", Employee.GetNode(root, "5").Name);
        Console.ReadKey();
    }
}

答案 1 :(得分:1)

我认为这里最好的方法是编写一个返回IEnumerable<Employee>的递归方法,该方法迭代树中的所有节点。

给定Employee类看起来像这样:

public class Employee
{
    public Employee[] ChildOrg { get; set; }
    public string     name     { get; set; }
    public string     id       { get; set; }
}

您可以编写这样的递归枚举器:

public static IEnumerable<Employee> AllEmployees(Employee root)
{
    if (root == null)
        yield break;

    yield return root;

    if (root.ChildOrg == null)
        yield break;

    foreach (var child in root.ChildOrg.SelectMany(AllEmployees))
        yield return child;
}

请注意,这将返回所有员工。如果要按ID过滤它,只需过滤枚举,例如:

var allNodesWithIdEndingIn0 = AllEmployees(root).Where(node => node.id.EndsWith("0"));

返回所有员工的枚举要灵活得多,因为您可以根据需要使用Linq处理或过滤它。

这是一个完整的可编辑控制台应用程序,演示了这种方法:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication4
{
    public class Employee
    {
        public Employee[] ChildOrg { get; set; }
        public string     name     { get; set; }
        public string     id       { get; set; }
    }

    public static class Program
    {
        public static IEnumerable<Employee> AllEmployees(Employee root)
        {
            if (root == null)
                yield break;

            yield return root;

            if (root.ChildOrg == null)
                yield break;

            foreach (var child in root.ChildOrg.SelectMany(AllEmployees))
                yield return child;
        }

        private static void Main()
        {
            Employee root = new Employee  { name = "root", id = "root" };
            createChildren(root, 4, 4, 0);

            Console.WriteLine("All employees:");
            Console.WriteLine(string.Join("\n", AllEmployees(root).Select(node => node.id)));

            Console.WriteLine("\nAll nodes with an id that ends in 0:");
            var allNodesWithIdEndingIn0 = AllEmployees(root).Where(node => node.id.EndsWith("0"));
            Console.WriteLine(string.Join("\n", allNodesWithIdEndingIn0.Select(node => node.id)));
        }

        private static int createChildren(Employee root, int depth, int width, int count)
        {
            if (depth == 0)
                return count;

            root.ChildOrg = new Employee[width];

            for (int i = 0; i < width; ++i)
            {
                var node = new Employee { id = count.ToString() };
                root.ChildOrg[i] = node;
                count = createChildren(node, depth-1, width, count+1);
            }

            return count;
        }
    }
}

访问节点的兄弟姐妹

如果您想要节点的兄弟节点,您可以返回节点的父节点ChildOrg列表。

为此,我将编写一个名为`EmployeeAndParent的包装类,以包含节点及其父节点。

这是修改后的代码。它还演示了如何使用id ==“100”访问节点的兄弟节点:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication4
{
    public class Employee
    {
        public Employee[] ChildOrg
        {
            get;
            set;
        }
        public string name
        {
            get;
            set;
        }
        public string id
        {
            get;
            set;
        }
    }

    public class EmployeeAndParent
    {
        public EmployeeAndParent(Employee employee, Employee parent)
        {
            Employee = employee;
            Parent   = parent;
        }

        public readonly Employee Employee;
        public readonly Employee Parent;
    }

    public static class Program
    {
        public static IEnumerable<EmployeeAndParent> AllEmployees(Employee root, Employee parent)
        {
            if (root == null)
                yield break;

            yield return new EmployeeAndParent(root, parent);

            if (root.ChildOrg == null)
                yield break;

            foreach (var child in root.ChildOrg.SelectMany(child => AllEmployees(child, root)))
                yield return child;
        }

        private static void Main()
        {
            Employee root = new Employee
            {
                name = "root",
                id = "root"
            };
            createChildren(root, 4, 4, 0);

            Console.WriteLine("All employees:");
            Console.WriteLine(string.Join("\n", AllEmployees(root, null).Select(node => node.Employee.id)));

            Console.WriteLine("\nAll nodes with an id that ends in 0:");
            var allNodesWithIdEndingIn0 = AllEmployees(root, null).Where(node => node.Employee.id.EndsWith("0"));
            Console.WriteLine(string.Join("\n", allNodesWithIdEndingIn0.Select(node => node.Employee.id)));

            Console.WriteLine("\nAll siblings of the node with id == 100, along with that node itself:");
            var foundNode = AllEmployees(root, null).Single(node => node.Employee.id == "100");
            Console.WriteLine(string.Join("\n", foundNode.Parent.ChildOrg.Select(node => node.id)));
        }

        private static int createChildren(Employee root, int depth, int width, int count)
        {
            if (depth == 0)
                return count;

            root.ChildOrg = new Employee[width];

            for (int i = 0; i < width; ++i)
            {
                var node = new Employee
                {
                    id = count.ToString()
                };
                root.ChildOrg[i] = node;
                count = createChildren(node, depth-1, width, count+1);
            }

            return count;
        }
    }
}

答案 2 :(得分:0)

无需使用Linq ......

private Employee GetNode(Employee employee, string id)
{
    if (employee.id.Equals(id))
    {
        return employee;
    }
    if (employee.ChildOrg != null)
    {
        foreach (var item in employee.ChildOrg)
        {
            var emp = GetNode(item, id);
            if (emp != null)
                return emp;
        }
    }
    return null;
}

答案 3 :(得分:0)

public class Employee
{
    public Employee()
    {
        ChildOrg = new List<Employee>();
    }

    public Employee(string toString, string root)
    {
        ChildOrg = new List<Employee>();
        Id = toString;
        Name = root;
    }

    public IList<Employee> ChildOrg { get; set; }
    public string Name { get; set; }
    public string Id { get; set; }

    public static Employee GetNode(Employee e, string theId)
    {
        if (e.Id.Equals(theId))
        {
            return e;
        }

        var employeeInList = (e.ChildOrg == null) ? null : e.ChildOrg.FirstOrDefault(item => item.Id.Equals(theId));
        return employeeInList ??
               e.ChildOrg.Select(item => GetNode(item, theId)).FirstOrDefault(employee => employee != null);
    }
}

测试

static void Main(string[] args)
    {
        var root = new Employee(1.ToString(), "root");
        var e1 = new Employee(2.ToString(), "1 second level");
        var e2 = new Employee(3.ToString(), "2 third level");
        var e3 = new Employee(4.ToString(), "3 second level");
        var e4 = new Employee(5.ToString(), "4 third level");
        e3.ChildOrg.Add(e4);
        e2.ChildOrg.Add(e3);
        e1.ChildOrg.Add(e2);
        root.ChildOrg.Add(e1);

        Console.WriteLine("Id 3 -> {0}", Employee.GetNode(root, "3").Name);
        Console.WriteLine("Id 1 -> {0}", Employee.GetNode(root, "1").Name);
        Console.WriteLine("Id 5 -> {0}", Employee.GetNode(root, "5").Name);
        Console.ReadKey();
    }

答案 4 :(得分:0)

定义遍历函数:

public static IEnumerable<Employee> Traverse(Employee employee)
{       
    yield return employee;

    if (employee.ChildOrg == null) yield break;

    var subordinates = employee
                      .ChildOrg
                      .SelectMany(Traverse);

    foreach(var s in subordinates)
        yield return s;
}

然后找到匹配元素:

var root = new Employee(...);
...

var searchId = "42";
var employee42 = Traverse(root)
                .Single(e => e.id == searchId);

执行Depth-first Pre-order搜索。

答案 5 :(得分:0)

试试这个

private static Employee GetNode(Employee employee, string id){
    if (employee.id == id)        
        return employee;

    if (employee.ChildOrg != null)
    {
        foreach (var item in employee.ChildOrg)
        {
            if(item.id == id)
                return item;

            var child = GetNode(item, id);

            if(child != null)
               return child;
        }
    }
    return null;
}