Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ and recursion

Tags:

c#

recursion

linq

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;
}

Is there any way that FindBoxBySize() can be compacted using LINQ? Also: comments on my code are welcome. I don't do much recursion, so I might have missed something in my implementation.

like image 554
cllpse Avatar asked Nov 23 '10 14:11

cllpse


2 Answers

I'd also take the extension method approach, but use an 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 FindBoxBySize method now becomes:

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

Your original calling code works without modification:

var small = FindBoxBySize(box, BoxSize.Small);
like image 75
Enigmativity Avatar answered Oct 01 '22 23:10

Enigmativity


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);
    }
}

Usage:

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);
like image 33
abatishchev Avatar answered Oct 01 '22 23:10

abatishchev