LINQ and recursion

Consider the following:

public class Box
{
    public BoxSize Size { get; set; }

    public IEnumerable<Box> Contents { get; set; }
}

Box FindBoxBySize(Box box, BoxSize size)
{
    Box _foundBox = null;

    Action<IEnumerable<Box>> _recurse = null;

    _recurse = new Action<IEnumerable<Box>>(boxes =>
    {
        foreach (var _box in boxes)
        {
            if (_box.Size == size)
            {
                _foundBox = _box;

                return;
            }

            if (_box.Contents != null) _recurse(_box.Contents);
        }
    });

    _recurse(box.Contents);

    return _foundBox;
}

Can I extrude FindBoxBySize()using LINQ? Also: comments on my code are welcome. I don't have much recursion, so I might have missed something in my implementation.

+5
source share
4 answers

I would also use the extension method approach, but use the iterator method:

public static class BoxEx
{
    public static IEnumerable<Box> Flatten(this Box box)
    {
        yield return box;
        if (box.Contents != null)
        {
            foreach (var b in box.Contents.SelectMany(b2 => Flatten(b2)))
            {
                yield return b;
            }
        }
    }
}

Your method FindBoxBySizewill now look like this:

Box FindBoxBySize(Box box, BoxSize size)
{
    return (from b in box.Flatten()
            where b.Size == size
            select b).FirstOrDefault();
}

The original call code works without changes:

var small = FindBoxBySize(box, BoxSize.Small);
+5
source

Extension Methods:

class Box
{
    public IEnumerable<Box> GetBoxes()
    {
        // avoid NullReferenceException
        var contents = this.Contents ?? Enumerable.Empty<Box>();

        // do the recursion
        return contents.SelectMany(b => b.GetBoxes()).Concat(contents);
    }

    public Box GetBox(int size)
    {
        return this.GetBoxes().FirstOrDefault(b => b.Size == size);
    }
}

Using:

var box_with_box = new Box { Contents = new[] { new Box() { Size = 10 } } };
var box = new Box { Contents = new[] { box_with_box, box_with_box } };

Box box10 = box.GetBox(10);
+2
source

LINQ, - (untested):

public static IEnumerable<Box> GetBoxesRecursive(this Box box)
{
  if(box == null)
      throw new ArgumentNullException("box");

  var children = box.Contents ?? Enumerable.Empty<Box>();

                           // use lambda in C# 3.0      
  var recursiveChildren = children.SelectMany(GetBoxesRecursive);  

  return new[] { box }.Concat(recursiveChildren);                                    
}

...    

Box desiredBox = myBox.GetBoxesRecursive()
                      .FirstOrDefault(b => b.Size == desiredSize);
+1

( ),

Box FindBoxBySize(Box box, BoxSize size)
{
    if (box.Size == size)
        return box;

    foreach (var child in box.Contents)
    {
        var foundBox = FindBoxBySize(child, size);
        if(foundBox != null)
            return foundBox;
    }

    return null;
} 

Regarding LINQ: LINQ does not have a good way to handle recursive data structures. A few different extension methods that can be fixed can be found by asking google for "LINQ recursion", but I'm not sure if they will add clarity in this case.

0
source

All Articles