使用Linq遍历列表

时间:2018-10-25 12:46:04

标签: c# linq recursion

我的课程如下:

public class SiloNode
{
    public string Key { get; private set; }
    public string ParentKey { get; private set; }
    public string Text { get; private set; }
    public string Url { get; private set; }
}

所有节点都包含在列表中:

List<SiloNode> nodes = new List<SiloNode>();

如您所见,该类包含ParentKey属性,因此可以找到该项的父项,祖父母等,直到列表顶部被选中为止。

目前,我需要遍历2个级别,从下面的代码中您可以看到,它看起来已经很笨重了。现在,我需要修改代码以遍历3个级别,而且我担心它会变得混乱。

有没有更清洁的方法来实现我想要的?

    string GetStartGroup(string currentUrl)
    {
        string startGroup = null;

        var currentNode = Silos.Silo.SingleOrDefault(x => x.Url == currentUrl);

        if (currentNode != null)
        {
            var parentNode = Silos.Silo.SingleOrDefault(x => x.Key == currentNode.ParentKey);
            if (parentNode != null) startGroup = parentNode.ParentKey;
        }

        return startGroup;
    }

4 个答案:

答案 0 :(得分:1)

重复使用列表上的SingleOrDefault会使算法变慢:为n个节点找到父节点需要O(n 2 )时间。

您应该先创建一个Dictionary<string,SiloNode>,然后遍历字典中的层次结构:

var lookup= nodes.ToDictionary(n => n.Key);
...
SiloNode FindParent(SiloNode node, int levelsUp, IDictionary<string,SiloNode> lookup) {
    while (node != null && levelsUp != 0) {
        if (node.ParentKey == null || !lookup.TryGetValue(node.ParentKey, out var parent)) {
            return node;
        }
        node = parent;
        levelsUp--;
    }
    return node;
}

这将查找最高levelsUp级的父级。如果您正在寻找最后一个可能的父代,请按如下所示修改代码:

SiloNode FindParent(SiloNode node, IDictionary<string,SiloNode> lookup) {
    while (true) {
        if (node?.ParentKey == null || !lookup.TryGetValue(node.ParentKey, out var parent)) {
            return node;
        }
        node = parent;
    }
}

或递归

SiloNode FindParent(SiloNode node, IDictionary<string,SiloNode> lookup) {
    return node?.ParentKey != null && lookup.TryGetValue(node.ParentKey, out var parent)
         ? FindParent(parent, lookup)
         : node;
}

答案 1 :(得分:1)

您可以通过递归来做到这一点。

string GetStartGroup(string currentUrl)
{
    var node = nodes.Single(x => x.Url == currentUrl);
    if (node.ParentKey == null)
        return node.Key;

    return GetStartGroup(nodes.Single(x => x.Key == node.ParentKey).Url);
}

或者:

string GetStartGroup(string currentUrl)
{
    return GetStartNode(nodes.Single(x => x.Url == currentUrl)).Key;
}

SiloNode GetStartNode(SiloNode node)
{
    if (node.ParentKey == null)
        return node;

    return GetStartNode(nodes.Single(x => x.Key == node.ParentKey));
}

答案 2 :(得分:0)

您可以更改

if (parentNode != null) startGroup = parentNode.ParentKey;

if (parentNode != null) startGroup = GetStartGroup(currentNode.parentUrl /*or something similar*/);

但是,最好使用迭代循环。我不太了解您的问题,无法给您提示,但是伪代码看起来像这样:

while (parentNode != null) {
    currentNode = currentNode.parentNode;
    parentNode = currentNode.parentNode;
}

您可能需要致电SingleOrDefault,但如果您有直接参考,则应改用该参考。

答案 3 :(得分:0)

只需将其放入方法中即可

public static Silo Up(Silo current, IEnumerable<Silo> collection)
{
    return collection.FirstOrDefault((it) => it.ParentKey == it.Key);
}

或作为扩展方法:

public static SiloExtensions
{
    public static Silo Up(this Silo current, IEnumerable<Silo> collection)
    {
        return collection.FirstOrDefault((it) => it.ParentKey == it.Key);
    }
}

因此您只需silo.Up()?.Up()

请注意,这相当慢。 根据实际操作,您可能希望将实际的父对象引入字段或提供对其访问的包装对象。

这样的包装对象可能看起来像这样:

public class SiloWrapper
{
    public Silo Wrapped { get; }
    public Silo Parent { get; }
    private SiloWrapper(Silo silo, Silo parent)
    {
        this.Wrapped = silo;
        this.Parent = parent;
    }
    public IEnumerable<SiloWrapper> Map(IEnumerable<Silo> silos)
    {
        var dict = silos.ToDictionary((s) => s.Key);
        foreach(var s in silos)
        {
            yield return new SiloWrapped(s, s.ParentKey == null ? null : dict[s.ParentKey]);
        }
    }
}

然后上下移动,只需要调用SiloWrapped.Map(<methodToGetSiloCollection>)并准备好所有包装的筒仓即可使用。

如果可能需要考虑GarbageCollection,则也可以改用WeakReference<Silo> ParentWeak