0

I have a source list like this:

class Foo {
    public int ID;
    public List<Foo> Children;
}

// Then somewhere else...
Foo root = new Foo(); // The Children is a nested list

Then I have a new type that I have access to make changes to:

class Bar : Foo {
    public string Name;
}

What I'd like to do is create a new Bar that is a replica of the root variable above, however has a value for the Name property in each item. It can be a default value for this example.

I've tried LINQ's Select(...) off the Children of the root but I can't get the syntax down, if that's even the correct route.

I have this so far, which only works on the first level of child items in the root list. I'd need it to go through all levels of children.

Bar = new ObservableCollection<Bar>(root.Children.Select(c => new Bar { Name = "Foo" }));
mariocatch
  • 8,305
  • 8
  • 50
  • 71
  • 1
    I think this is the answer you're looking for: http://stackoverflow.com/a/30325216/424129 – 15ee8f99-57ff-4f92-890c-b56153 Feb 23 '17 at 20:49
  • That's flattening the list it seems. I'm looking to maintain the depth of the original list, just iterate through each item in it and create a new list out of it while filling in a new property – mariocatch Feb 23 '17 at 20:53
  • How exactly are you going to fill that property? And what's the actual question here, how to write a recursive function? – Ivan Stoev Feb 23 '17 at 20:56
  • I'm a little confused. Are you trying to do Bar bar = foo with the name property? that should be as simple as bar = new bar{Name = "whatever", Children = root.Children, ID = root.ID}; Or are you asking something different? – Hack Feb 23 '17 at 20:57
  • Recursion won't work, as it's thousands and thousands of levels deep and goes into a stackoverflow. I'd fill it hopefully like how I have above in the example of "Foo" – mariocatch Feb 23 '17 at 20:57
  • 2
    Well, if the recursion doesn't work, you can always implement it iteratively :) – Ivan Stoev Feb 23 '17 at 20:58
  • It's thousands of levels deep, was hoping LINQ can simply iterate through all items in a list and fill in a property, while returning a new item/list – mariocatch Feb 23 '17 at 20:59
  • 1
    Getting LINQ involved doesn't make the recursion any less recursive. You may be stuck doing it iteratively, if recursion really is an issue. Also btw, I don't see how you're going to populate `bar.Children` with instances of `Bar` since `Children` is `List`, inherited from `Foo`. – 15ee8f99-57ff-4f92-890c-b56153 Feb 23 '17 at 21:03
  • @EdPlunkett Yep, regarding your last point I was hoping that LINQ could select each item in that list and return a new item. – mariocatch Feb 23 '17 at 21:04
  • This fiddle illustrates how I'd do it, if I didn't have to worry about the stack overflowing. But then Children is a problem. https://dotnetfiddle.net/qWRDBk – 15ee8f99-57ff-4f92-890c-b56153 Feb 23 '17 at 21:07
  • @EdPlunkett Yeah your fiddle is basically exactly what I have in the OP, where I'm stuck at going beyond the first level. – mariocatch Feb 23 '17 at 21:09
  • See update in the Fiddle, `namespace Version2` – 15ee8f99-57ff-4f92-890c-b56153 Feb 23 '17 at 21:12
  • It's not what you had in your question, by the way. The `Bar(Foo)` constructor recursively recreates the whole tree. The sticking point is only the type of `Children`. The `new` gimmick compiles but it's ugly. The answer might be a cast property. – 15ee8f99-57ff-4f92-890c-b56153 Feb 23 '17 at 21:13

1 Answers1

2

If the depth is reasonable, it can easily be done with recursive lambda and LINQ:

Func<Foo, Bar> resultSelector = null;
resultSelector = source => new Bar
{
    ID = source.ID,
    Name = "Foo",
    Children = source.Children.Select(resultSelector).ToList<Foo>()
};
Foo result = resultSelector(root);

If the depth is so big and is causing stack overflow, then LINQ is not applicable, you can use iterative approach with two explicit stacks:

var resultStack = new Stack<Bar>();
var childrenStack = new Stack<List<Foo>.Enumerator>();
var result = new Bar { ID = root.ID, Name = "Foo", Children = new List<Foo>() };
var children = root.Children.GetEnumerator();
while (true)
{
    while (children.MoveNext())
    {
        var child = children.Current;
        var resultChild = new Bar { ID = child.ID, Name = "Foo", Children = new List<Foo>() };
        result.Children.Add(resultChild);
        if (child.Children.Count == 0) continue;
        resultStack.Push(result);
        childrenStack.Push(children);
        result = resultChild;
        children = child.Children.GetEnumerator();
    }
    if (resultStack.Count == 0) break;
    result = resultStack.Pop();
    children = childrenStack.Pop();
}
// Here the result is fully populated
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343