JSON из списка смежности? - PullRequest
1 голос
/ 21 марта 2012

У меня есть список смежности, подобный этому:

A   -  A1
A   -  A2
A   -  A3
A3  - A31
A31 - A311
A31 - A312

Я пытаюсь получить следующий вывод:

{
    "name": "A",
    "children": [{
        "name": "A1"
    }, {
        "name": "A2"
    }, {
        "name": "A3",
        "children": [{
            "name": "A31",
            "children": [{
                "name": "A311"
            }, {
                "name": "A312"
            }]
        }]
    }]
};

У меня скромно большой график, содержащий ссылки по 100 КБ. Каков хороший способ сделать это? Я думаю, что есть очень элегантный рекурсивный способ сделать это, но я не уверен, как напрямую создать строку JSON.

1 Ответ

4 голосов
/ 21 марта 2012

Что-то вроде должно работать:

static void Main(string[] args)
{
    var adjList = new List<Link>
    {
        new Link("A","A1"),
        new Link("A","A2"),
        new Link("A","A3"),
        new Link("A3","A31"),
        new Link("A31","A311"),
        new Link("A31","A312"),
    };

    var rootsAndChildren = adjList.GroupBy(x => x.From)
           .ToDictionary(x => x.Key, x => x.Select(y => y.To).ToList());
    var roots = rootsAndChildren.Keys
           .Except(rootsAndChildren.SelectMany(x => x.Value));

    using (var wr = new StreamWriter("C:\\myjson.json"))
    {
        wr.WriteLine("{");
        foreach (var root in roots)
            AppendSubNodes(wr, root, rootsAndChildren, 1);
        wr.WriteLine("};");
    }
}

static void AppendSubNodes(TextWriter wr, string root, 
          Dictionary<string, List<string>> rootsAndChildren, int level)
{
    string indent = string.Concat(Enumerable.Repeat(" ", level * 4));
    wr.Write(indent + "\"name\" : \"" + root + "\"");
    List<string> children;
    if (rootsAndChildren.TryGetValue(root, out children))
    {
        wr.WriteLine(",");
        wr.WriteLine(indent + "\"children\" : [{");
        for (int i = 0; i < children.Count; i++)
        {
            if (i > 0)
                wr.WriteLine(indent + "}, {");
            AppendSubNodes(wr, children[i], rootsAndChildren, level + 1);
        }
        wr.WriteLine(indent + "}]");
    }
    else
    {
        wr.WriteLine();
    }
}

С Link следующим классом:

class Link
{
    public string From { get; private set; }
    public string To { get; private set; }
    public Link(string from, string to)
    {
        this.From = from;
        this.To = to;
    }
}

Результат предыдущего кода:

{
    "name" : "A",
    "children" : [{
        "name" : "A1"
    }, {
        "name" : "A2"
    }, {
        "name" : "A3",
        "children" : [{
            "name" : "A31",
            "children" : [{
                "name" : "A311"
            }, {
                "name" : "A312"
            }]
        }]
    }]
};

РЕДАКТИРОВАТЬ:

Если вы хотите проверить существование циклов графов, вы можете сделать следующее (сразу после создания словаря rootsAndChildren)

var allNodes = rootsAndChildren.Keys.Concat(rootsAndChildren.SelectMany(x => x.Value)).Distinct();
Func<string, IEnumerable<string>> getSuccessors =
    (x) => rootsAndChildren.ContainsKey(x) ? rootsAndChildren[x] : Enumerable.Empty<string>();

var hasCycles = new Tarjan<string>().HasCycle(allNodes, getSuccessors);

с Tarjan следующим классом:

// Please note that Tarjan does not detect a cycle due to a node 
// pointing to itself. It's pretty trivial to account for that though...
public class Tarjan<T>
{
    private class Node
    {
        public T Value { get; private set; }
        public int Index { get; set; }
        public int LowLink { get; set; }
        public Node(T value)
        {
            this.Value = value;
            this.Index = -1;
            this.LowLink = -1;
        }
    }
    private Func<T, IEnumerable<T>> getSuccessors;
    private Dictionary<T, Node> nodeMaps;
    private int index = 0;
    private Stack<Node> stack;
    private List<List<Node>> SCC;
    public bool HasCycle(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
    {
        return ExecuteTarjan(nodes, getSuccessors).Any(x => x.Count > 1);
    }
    private List<List<Node>> ExecuteTarjan(IEnumerable<T> nodes, Func<T, IEnumerable<T>> getSuccessors)
    {
        this.nodeMaps = nodes.ToDictionary(x => x, x => new Node(x));
        this.getSuccessors = getSuccessors;
        SCC = new List<List<Node>>();
        stack = new Stack<Node>();
        index = 0;
        foreach (var node in this.nodeMaps.Values)
        {
            if (node.Index == -1)
                TarjanImpl(node);
        }
        return SCC;
    }
    private IEnumerable<Node> GetSuccessors(Node v)
    {
        return this.getSuccessors(v.Value).Select(x => this.nodeMaps[x]);
    }
    private List<List<Node>> TarjanImpl(Node v)
    {
        v.Index = index;
        v.LowLink = index;
        index++;
        stack.Push(v);
        foreach (var n in GetSuccessors(v))
        {
            if (n.Index == -1)
            {
                TarjanImpl(n);
                v.LowLink = Math.Min(v.LowLink, n.LowLink);
            }
            else if (stack.Contains(n))
            {
                v.LowLink = Math.Min(v.LowLink, n.Index);
            }
        }
        if (v.LowLink == v.Index)
        {
            Node n;
            List<Node> component = new List<Node>();
            do
            {
                n = stack.Pop();
                component.Add(n);
            } while (n != v);
            SCC.Add(component);
        }
        return SCC;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...