Dac2020
Dac2020

Reputation: 165

How to walk through directory in Erlang to take only folders?

-module(tut).
-export([main/0]).

main() -> 

    folders("C:/Users/David/test/").
    

folders(PATH) ->
    {_,DD} = file:list_dir(PATH),
    A = [{H,filelib:is_dir(PATH ++ H)}|| H <-DD],
    % R is a list of all folders inside PATH
    R = [PATH++X|| {X,Y} <- A, Y =:= true],
    io:fwrite("~p~n", [R]),
    case R of
        [] -> ok;
        % How call again folders function with the first element of the list?
        % And save the result in some kind of structure 
    end.

Sorry for the beginner question, but I'm still new to Erlang. I would like to know how I can call the function again until saves the results in a kind of list, tuple or structure...

Like:

  [
    {"C:/Users/David/test/log", 
      {"C:/Users/David/test/log/a", "C:/Users/David/test/log/b"}},
    {"C:/Users/David/test/logb", 
      {"C:/Users/David/test/logb/1", "C:/Users/David/test/logb/2","C:/Users/David/test/logb/3"}},
 ]  

Upvotes: 2

Views: 326

Answers (1)

Agus
Agus

Reputation: 689

Few things:

  1. These 2 calls can be simplified.
A = [{H,filelib:is_dir(PATH ++ H)}|| H <-DD],
R = [PATH++X|| {X,Y} <- A, Y =:= true],

into

A = [H || H <- DD, filelib:is_dir(PATH ++ H) =:= true],
  1. In terms of representation, sub-folders should be in list format, not tuple. It will be difficult to work with if they were tuples. Sample structure: {Folder, [Subfolder1, Subfolder2, ...]}, where SubfolderX will have the same definition and structure, recursively.
  2. Folders are like tree, so need to have recursive call here. Hope you are already familiar with the concept. Below is one way to do it using list comprehension - there are other ways anyway, e.g. by using lists:foldl function.
folders(PATH) ->
    {_, DD} = file:list_dir(PATH), 
    A = [H || H <- DD, filelib:is_dir(PATH ++ "/" ++ H) =:= true],
    %%io:format("Path: ~p, A: ~p~n", [Path, A]), 
    case A of
        [] ->   %%Base case, i.e. folder has no sub-folders -> stop here
                {PATH, []}; 
         _ ->   %%Recursive case, i.e. folder has sub-folders -> call @folders
                {PATH, [folders(PATH ++ "/" ++ H2) || H2 <- A]}
    end.

For consistency reason, you need to call the main function without a forward slash at the end, as this will be added in the function itself.

Folders = folders("C:/Users/David/test"). %% <- without forward slash

A helper function pretty_print below can be used to visualize the output on the Erlang shell

Full code:

-export([folders/1]).
-export([main/0]).

main() -> 
    Folders = folders("C:/Users/David/test"),
    pretty_print(Folders, 0),
    ok.
    
folders(PATH) ->
    {_, DD} = file:list_dir(PATH), 
    A = [H || H <- DD, filelib:is_dir(PATH ++ "/" ++ H) =:= true], %%please note the "/" is added here
    %%io:format("Path: ~p, A: ~p~n", [Path, A]), 
    case A of
        [] ->   %%Base case, i.e. folder has no sub-folders -> stop here
                {PATH, []}; 
         _ ->   %%Recursive case, i.e. folder has sub-folders -> call @folders
                {PATH, [folders(PATH ++ "/" ++ H2) || H2 <- A]}
    end.

pretty_print(Folders, Depth) ->
    {CurrrentFolder, ListSubfolders} = Folders,
    SignTemp = lists:duplicate(Depth, "-"),
    case Depth of
        0 -> Sign = SignTemp;
        _ -> Sign = "|" ++ SignTemp
    end,
    io:format("~s~s~n", [Sign, CurrrentFolder]),
    [pretty_print(Subfolder, Depth+1) || Subfolder <- ListSubfolders].

Upvotes: 3

Related Questions