user3883001
user3883001

Reputation: 109

dynamically create for loops matlab

I am given a structure with variable names L1dirs, L2dirs...etc all the way up to however many levels the user wants. Each Lxdirs contains a cell array of the names of the directories to be created.

The end result should be the creation of a nested set of directories where each level 1 directory contains all level 2 directories and all level 2 directories contain all level 3 directories, etc. How can I dynamically create this hierarchy?

From the code below, I have already found out through try-catch statements how many levels the user specified.

Now given that we know how many levels the user specified, how can we generate a list of all the unique filepath combinations? The end result should be a column cell array of m paths where mis the number of L1 directories times the number of L2 directories times.....times the number of Lx directories.

Can MATLAB do this? I attempted to use eval() by creating a dynamically created string macro, but eval doesn't like the use of the end statement when trying to dynamically nest for loops. Is there another way?

Here is a sample piece of code of what I have so far:

Main Code

userinputs.L1dirs = {'Level 1 Dir 1';
  'Level 1 Dir 2';
  'Level 1 Dir 3'};
userinputs.L2dirs = {'Level 2 Dir 1';
  'Level 2 Dir 2';
  'Level 2 Dir 3'};
userinputs.L3dirs = {'Level 3 Dir 1';
  'Level 3 Dir 2';
  'level 3 Dir 3'};

userinputs.top_level_dir = strcat(pwd,'\Results\');

pathlist1 = check_results_dirs(userinputs)


userinputs.L4dirs = {'Level 4 Dir 1';
             'Level 4 Dir 2'};
userinputs.top_level_dir = strcat(pwd,'\Results 2\');
pathlist2 = check_results_dirs(userinputs)

Support Function

function pathlist = check_results_dirs(inputdata)
%{
This function checks if the file directory exists in the top level
directory as specified by the inputs structure.  If the directory already
exists, then it checks whether these files are to be overwritten or not.  

This function dynamically checks how many levels of directories the user
specified.  

Inputs
inputdata - structure containing the following variable names
inputdata.LXdirs - X is an integer value.  Variable(s) contain cell
arrays of directory names

inputdata.top_level_dir - top level destination directory to
create this file structure.  If this folder does not exist, it will be
created.
%}

%check if top level directory exists
if ~exist(inputdata.top_level_dir,'file')
    mkdir(inputdata.top_level_dir);
end

%determine how many directory levels there are as determined by the user
numDirLevels = 1;
numDirsPerLevel = [];
moreDirsFlag = 1;
while moreDirsFlag
try
    eval(sprintf('temp = inputdata.L%idirs;',numDirLevels));
    numDirsPerLevel = [numDirsPerLevel; length(temp)];
    numDirLevels = numDirLevels + 1;
catch err
    if strcmp(err.identifier,'MATLAB:nonExistentField')
        %no more directory levels
        numDirLevels = numDirLevels - 1;
        moreDirsFlag = 0;
    else
        rethrow(err);
    end
end
end

numUniqueDirs = prod(numDirsPerLevel);

%Generate Path list

beginstr = '';
midstr = 'pathlist{numUniqueDirs} = strcat(';
endstr = '';
for ii = 1:numDirsPerLevel
    beginstr = strcat(beginstr,sprintf('for x%i=1:numDirsPerLevel(%i) ',ii,ii));
    midstr = strcat(midstr,sprintf('inputdata.L%idirs(x%i),''\\'',',ii,ii)); 
    endstr = strcat(' end ',endstr);
end
midstr = strcat(midstr,''''');');
evalstr = ' numUniqueDirs = numUniqueDirs+1;'
midstr = strcat(midstr,evalstr);

evalstr = 'numUniqueDirs = 1; '
beginstr = strcat(evalstr,beginstr);


eval(strcat(beginstr,midstr,endstr));  %error is thrown here due to an illegal 
%use of 'end'.  Can I not 
%use for loops using eval?

%debug statements
disp(beginstr)
disp(midstr)
disp(endstr)

end

Note how in the main code the function is called twice. Once calls it to make three levels of directories another call is to make four levels of directories. The output pathlist should contain numL1dirs times numL2dirs times .... times numLxdirs so for the first example it should create 27 distinct directories such that each level 1 dir contains all three level 2 dirs and each level 2 dir contains all three level 3 dirs. For the second example, pathlist should contain 54 unique directory paths.

Upvotes: 0

Views: 153

Answers (1)

Hoki
Hoki

Reputation: 11792

Well the reason why you struggled so much is probably your attachment to eval. You need to really limit the use of that to a strict minimum ... ideally never. I can see why you thought you had to resort to it (your dynamic field/variable names) but fortunately Matlab gave us a few tools to overcome that.

You should have a good look at the Dynamic field names functionalities from Matlab. This will probably be of great use given your style of programming, and use it as much as you can to avoid any use of the evil eval.

Once you use this. Do a recursive call to generate a list of strings representing all the paths of the folders to generate, then mkdir them and you're done.

function pathList = Generate_PathList(userinputs)

%% // check if top level directory exists
if ~exist(userinputs.top_level_dir,'dir')
    mkdir(userinputs.top_level_dir);
end

%% // determine how many directory levels there are
fnames = fieldnames(userinputs) ;
maxDirLevel = 1 ;
for iField = 1:numel(fnames)
    lvlDir = sscanf(fnames{iField},'L%idirs') ;
    if lvlDir > maxDirLevel
        maxDirLevel = lvlDir ;
    end
end

%% // Generate string list
pathList = { userinputs.top_level_dir } ;
for iLvl = 1:maxDirLevel % // loop on all directory level
    dirLevelName = ['L' num2str(iLvl) 'dirs'] ;
    pathList = addPathToList( pathList , userinputs.(dirLevelName) ) ;     
end

%% // Generate the directories
for iDir = 1:numel(pathList)
    mkdir( pathList{iDir} ) ;
end

%% // Helper function called recursively
function pathListOut = addPathToList( pathList , dirNames )
    nTop = numel(pathList) ;
    nLow = numel(dirNames) ;
    pathListOut = cell( nTop*nLow , 1 ) ;
    iPath = 0 ;
    for iTopDir = 1:nTop
        for iLowDir = 1:nLow
            iPath = iPath + 1 ;
            pathListOut{iPath} = [pathList{iTopDir} '\' dirNames{iLowDir}] ;
        end
    end

You can call this function the same way you used to call your initial function:

userinputs.top_level_dir = strcat(pwd,'\Results');
pathList = Generate_PathList(userinputs)

userinputs.L4dirs = {'Level 4 Dir 1';
             'Level 4 Dir 2'};
userinputs.top_level_dir = strcat(pwd,'\Results 2');
pathList = Generate_PathList(userinputs)

Will create your 27 and respectively 54 unique folders.

Just note that I removed the last '\' in your top level folder definition (to be consistent with the folder path generation used later on ...)

Upvotes: 1

Related Questions