monty.py
monty.py

Reputation: 2789

Return and fill a collection from recursive method the right way

I´m about to write a method (or 2 methods) which should RETURN a List of all files in a folder hierarchy (with all subfolders). I know how to deal with it, when I just fill a global List. This works for me (this is not my problem). But I want to fill the List inside the Method and therefore I got a problem with the recursion. So far I´m here:

public List<string> GetAllFiles(string path)
{
    List<string> files = new List<string>();

    FindFiles(files, path);

    return files;
}

private void FindFiles(List<string> files, string path)
{
    if (Directory.Exists(path))
    {
        try
        {
            foreach (string folder in Directory.GetDirectories(path))
            {
                FindFiles(files, folder);
            }

            foreach (string file in Directory.GetFiles(path).ToArray())
            {
                files.Add(file);
            }
        }
        catch (Exception)
        {
            // ignored
        }
    }
}

This works works, but I pass the List as a reference, which is not a clean way in my opinion. How to deal with it "normally"? Thanks in advance.

Upvotes: 2

Views: 1180

Answers (2)

Ben Steeves
Ben Steeves

Reputation: 140

You could try using yield return to add to your list.

public List<string> GetAllFiles(string path)
{
    List<string> files = new List<string>();

    foreach (string file in FindFiles(path))
    {
        files.Add(file);
    }

    return files;
}

private IEnumerable<string> FindFiles(string path)
{
    if (Directory.Exists(path))
    {
        try
        {
            foreach (string folder in Directory.GetDirectories(path))
            {
                foreach (string file in FindFiles(files, folder))
                {
                    yield return file;
                }
            }

            foreach (string file in Directory.GetFiles(path).ToArray())
            {
                yield return file;
            }
        }
        catch (Exception)
        {
            // ignored
        }
    }
}

Upvotes: 2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726489

Your approach is pretty clean, because it lets you create your list once, fill it out, and return to the caller, without creating duplicates.

Given the method signature returning List<string>, another alternative would be slightly more wasteful, because it would create throw-away lists at each level of the hierarchy:

public List<string> GetAllFiles(string path) {
    var res = new List<string>();
    if (Directory.Exists(path)) {
        try {
            foreach (var folder in Directory.GetDirectories(path)) {
                res.AddRange(GetAllFiles(folder));
            }
            foreach (var file in Directory.GetFiles(path)) {
                res.Add(file);
            }
        } catch (Exception) {
            // ignored
        }
    }
    return res;
}

LINQ makes this even more concise, but the approach remains the same:

public List<string> GetAllFiles(string path) {
    if (!Directory.Exists(path)) {
        return new List<string>();
    }
    try {
        return Directory.GetDirectories(path).SelectMany(folder => GetAllFiles(folder))
            .Concat(Directory.GetFiles(path)).ToList();
    } catch (Exception) {
        return new List<string>();
    }
}

Upvotes: 3

Related Questions