가장상위 노드 목록부터 가장하위노드목록 모두 린큐해서 Name해당하는 목록의 value 반환방법

안녕하세요
제가 조직도로 구성하는 코드를 짜고 있는데
노드 추가로
부모노드 및 자식노드, 그 자식노드의 자식노드, 그 자식노드의 자식노드…
이렇게 무제한으로 자식노드 추가되는데요
그런데
노드 추가할때
가장상위노드목록부터 가장하위노드목록까지 추가노드의 Name값을 린큐해서
해당하는 Name의 값이 중복되는 기존노드의 value를 반환(복붙)하면서 추가하게 할려는데.
코린이로 인해 코드가 안 먹히네요.
먼저 코드 보여드릴게요.

노드 클래스 코드

public class NodeVM : ABindableBase<FTANodeDTO>
{
    #region property (view)

    /// <summary>
    /// 노드 목록
    /// </summary>
    private ObservableCollection<UserControl> m_Nodes;
    public ObservableCollection<UserControl> Nodes
    {
        get
        {
            if (m_Nodes == null)
            {
                m_Nodes = new ObservableCollection<UserControl>();
            }
            return m_Nodes;
        }
        set
        {
            m_Nodes = value;
            RaisePropertyChanged("Nodes");
        }
    }

    /// <summary>
    /// 노드명
    /// </summary>
    public string Name
    {
        get
        {
            return Source.Name;
        }
        set
        {
            Source.Name = value;
            RaisePropertyChanged("Name");
        }
    }

    public string Value
    {
        get
        {
            return Source.Value;
        }
        set
        {
            Source.Value = value;
            RaisePropertyChanged("Value");
        }
    }
}

이렇게 해서

if (!string.IsNullOrEmpty(windc.Name))
{
    if((SelectedNode.DataContext as NodeVM).Source.Nodes.Any(x => x.Name == windc.Name))
    {
        MessageBox.Show("Gate 내에 중복된 Gate(Event)가 존재합니다.");

        var list = (SelectedNode.DataContext as NodeVM).Source.Nodes;
        while (list is IEnumerable<NodeVM>)
        {
            list = list.SelectMany<NodeVM, NodeVM>(x => x);
            // 위 ='list"에 에러가 남.
        }
        var nodes = list.AsQueryable().FirstOrDefault(x => x.Name == windc.Name);

        windc.Desc = nodes.Desc;
        windc.ValueInputType = nodes.ValueInputType;
        windc.ValueInputUnit = nodes.ValueInputUnit;
        windc.Value = nodes.Value;
    }
}

이렇게 했는데 에러가

Severity Code Description Project File Line Suppression State
Error CS1929 ‘List’ does not contain a definition for ‘SelectMany’ and the best extension method overload ‘Queryable.SelectMany<NodeVM, NodeVM>(IQueryable, Expression<Func<NodeVM, IEnumerable>>)’ requires a receiver of type ‘System.Linq.IQueryable<FTALib.View.Component.NodeVM>’ FTALib C:\Xworks\SAFETIA\3.Implementation\HydrogenProject\FTALib\View\OpenFTAView.xaml.cs 555 Active
이렇게 나와서요

어떻게 고치면 좋을까 선배님 조언 부탁드리겠습니다.

2개의 좋아요

아래를 참고하시기 바랍니다.

Depth-first search

Breadth-first search

4개의 좋아요

아래의 코드를 참고 바랄꼐요.

var root = new Node("root")
{
    Children = new()
    {
        new("node1") { Children = new() },
        new("node2")
        {
            Children = new()
            {
                new("target") { Children = new() }
            }
        },
    }
};


var allNode = GetAllNodes(root);
foreach (var node in allNode)
    Console.WriteLine(node);

Console.WriteLine();

var targetNode = GetAllNodes(root).FirstOrDefault(x => x.Name == "target");
Console.WriteLine(targetNode);



static IEnumerable<Node> GetAllNodes(Node root)
{
    var list = Enumerable.Append(Enumerable.Empty<Node>(), root);
    return Enumerable.Concat(list, root.Children.SelectMany(GetAllNodes));

    // 또는
    // return new[] { root }.Concat(root.Children.SelectMany(GetAllNodes));

}

class Node
{
    public string Name { get; }
    public required List<Node> Children { get; init; }

    public Node(string name) => Name = name;
}
4개의 좋아요
class Node
{
    Node? _parent;
    public Node? Parent 
    { 
        get => _parent; 
        set{
            if(value != _parent){

                _parent?.RemoveChild(this);
                _parent = value;
                _parent?.AddChild(this);

                // event propagating code here.
            }
        }
    }
    readonly List<Node> _children = new();

    public string Name { get; set; }
    public Node(string name) => Name = name;    
    private void AddChild(Node child) => _children.Add(child);
    private void RemoveChild(Node child) => _children.Remove(child); 

    public IEnumerable<Node> SelectAll(string name)
    {
        var result = new List<Node>();

        if(Name == name){
            result.Add(this);
        }

        var childrenNames = _children.SelectMany(x => x.SelectAll(name));
        
        result.AddRange(childrenNames);
        return result;
    }
}

사용코드

var com = new Node("회사");
var sales = new Node("영업부")
{
    Parent = com
};

new Node("1팀").Parent = sales;
new Node("2팀").Parent = sales;

var rnd = new Node("연구소")
{
    Parent = com
};
new Node("1팀").Parent = rnd;
new Node("2팀").Parent = rnd;

var team1s = com.SelectAll("1팀");

System.Console.WriteLine($"{com.Name}의 1팀 조직은 {team1s.Count()} 개입니다.");

결과

회사의 1팀 조직은 2 개입니다.

3개의 좋아요

LINQ를 쉽게 쓰기 위해서 yield 키워드로 Enumerator를 구현한 방법

public static IEnumerable<NodeVM> EnumerateAllChildren(NodeVM parent) 
    => EnumerateAllNodes(parent).Skip(1);

public static IEnumerable<NodeVM> EnumerateAllNodes(NodeVM node)
{
    yield return node;

    foreach (var child in node.Nodes)
        foreach (var childInner in EnumerateAllNodes(child.DataContext as NodeVM))
            yield return childInner;
}

사용법

var parent = SelectedNode.DataContext as NodeVM;

// 같은 이름을 가지는 노드가 있는지 검사
var exsiting = EnumerateAllChildren(parent).FirstOrDefault(x => x.Name); 

// 선택된 노드의 모든 하위 노드 반환
var all = EnumerateAllChildren(parent).ToArray(); 
4개의 좋아요

감사합니다!!

1개의 좋아요