chum of chance
chum of chance

Reputation: 6300

How do I convert a recursive object to a Collection in C#?

I have a recursive object, a linked list really:

public class LinkedList 
{
    public string UniqueKey { get; set; }
    public LinkedList LinkedList { get; set; }
}

LinkedList will have some object graph that will eventually end in LinkedList.LinkedList == null.

I would like to take all the objects in the graph and put them into a LinkedList collection so that I can iterate over them. How do I do this in C#? I feel as if there's a really easy way of going about this using yield or Linq voodoo?

Upvotes: 2

Views: 325

Answers (4)

Joost00719
Joost00719

Reputation: 754

I have a data structure in my project where an object can have a parent, and multiple children of itself. To get all the parents I can use this function:

    public static IEnumerable<TModel> EnumerateParents<TModel>(TModel model, Func<TModel, TModel> parentSelector)
    {
        var curr = model;
        while ((curr = parentSelector.Invoke(curr)) != null)
        {
            yield return curr;
        }
    }

Usage looks like this: _parents = ParentHelper.EnumerateParents(item, x => x.Parent).ToList();

I also made a function to enumerate all children which can be done with this function:

    public static IEnumerable<TModel> EnumerateChildren<TModel>(TModel root, Func<TModel, IEnumerable<TModel>> childSelector)
    {
        var children = childSelector.Invoke(root);
        if (children == null)
        {
            yield break;
        }

        foreach (var child in children)
        {
            yield return child;

            foreach (var grandChild in EnumerateChildren(child, childSelector))
            {
                yield return grandChild;
            }
        }
    }

which can be used like this: var children = ParentHelper.EnumerateChildren(item, x => x.Children)

The last one is not asked in the question, but posted it anyways for if someone might need it.

Upvotes: 0

Tomas Petricek
Tomas Petricek

Reputation: 243096

There are no nice LINQ methods in the standard .NET library to allow some elegant LINQ voodoo, but you can use Generate method from MoreLINQ project and write this:

Enumerable
  .Generate(list, l => l.LinkedList)
  .TakeWhile(l => l != null).Select(l => l.UniqueKey);

It uses Generate to create "infinite" list of all elements - it is not actually infinite, because it is generated lazily and we stop using it as soon as we find null value at the end (using TakeWhile). Then we use Select to return a sequence of values (instead of linked list nodes).

This is essentially a nice declarative way of expressing the while loop solution posted by Matthew (and it should have roughly similar performance).

EDIT The Generate method looks like this:

IEnumerable<T> Generate(T current, Func<T, T> generator) {
  while(true) { 
    yield return current;
    current = generator(current);
  }
}

Upvotes: 0

mqp
mqp

Reputation: 71985

Is this what you want?

public class LinkedList
{
    public string UniqueKey { get; set; }
    public LinkedList LinkedList { get; set; }

    public IEnumerable<LinkedList> GetAllNodes()
    {
        if (LinkedList != null)
        {
            yield return LinkedList;
            foreach (var node in LinkedList.GetAllNodes())
                yield return node;
        }
    }
}

Upvotes: 0

Matthew Flaschen
Matthew Flaschen

Reputation: 284907

Something like this should work. If you have control over the class you can make it IEnumerable directly.

public class LinkedListEnumerable : IEnumerable<string>
{
    LinkedList list;
    public LinkedListEnumerable(LinkedList l)
    {
        this.list = l;
    }

    public IEnumerator<string> GetEnumerator()
    {
        LinkedList l = list;
        while(l != null)
        {
            yield return l.UniqueKey;
            l = l.Next;
        }
    }
}

Then you can iterate over LinkedListEnumerable with a for-each loop.

Upvotes: 2

Related Questions