matlabmatlab-struct

Is there a way to iterate through nested structs with a varying number of fields in MATLAB?


I am looking for a way to iterate through nested structs in MATLAB, with each sub-struct containing a varying number of fields, some of which may contain further nested structs. For example, I have the following struct A:

    A.a.alpha=[2,3];
    A.a.beta=[8,9,10];
    A.a.delta.k=[5,6,7];
    A.a.delta.m=1;
    A.b.alpha=[22,32];
    A.b.beta.n=3;
    A.b.beta.o=[0,140];

Is there any way that I can automatically iterate through each of the end nodes of the struct A such that I can access A.a.alpha, A.a.beta, A.a.delta.k, A.a.delta.m, A.b.alpha, A.b.beta.n, and A.b.beta.o?

So far, I have seen nested for loops being used to access information that is a specific depth in (so accessing A.a.alpha, A.a.beta, A.b.alpha, but not A.a.delta.k, A.a.delta.m, A.b.beta.n, or A.b.beta.o), but my struct has those variable depths at which I need information access. I did consider using a "check if a sub-struct exists" method, but I can't figure out how to appropriately use that information to accomplish my goal here. Likewise, I also considered using a while loop that would stop at some A.end field but can't wrap my head around how to actually dig deeper into the variable depth sub-structs.


Solution

  • You can get the field names (or the data directly) using a recursive function, which calls itself on substructures.

    Then you can use getfield() to grab data out of nested structures, for example in your demo getfield( A, 'a', 'alpha' ) would return [2,3], but we can also do

    fieldSplit = {'a','alpha'};
    data = getfield( A, fieldSplit{:} );
    

    So by using a recursive function to generate all of the nested field names, we can generate fieldSplit for each subfield of your structure and pull out data at arbitrary depths.

    Full demo with commented code for more details:

    % Demo data
    A = struct();
    A.a.alpha=[2,3];
    A.a.beta=[8,9,10];
    A.a.delta.k=[5,6,7];
    A.a.delta.m=1;
    A.b.alpha=[22,32];
    A.b.beta.n=3;
    A.b.beta.o=[0,140];
    
    % Get the nested field names
    f = getNestedFields( A );
    
    % Extract any data you want, or all of it
    data = cell(1, numel(f));
    for ii = 1:numel(f)
        % Split the field name on the dot separators
        fieldSplit = strsplit( f{ii}, '.' ); 
        % Use getfield to get nested data
        data{ii} = getfield( A, fieldSplit{:} );
    end
    
    
    function fields = getNestedFields( strct )
        % Get the field names of this structure
        fields = fieldnames( strct );
        % Loop over the fields
        for ii = 1:numel(fields)        
            if isstruct( strct.(fields{ii}) )
                % This is a substructure, recursively fetch fields
                % Replace this field name with subfield list
                fields{ii} = strcat( fields{ii}, '.', getNestedFields(strct.(fields{ii})) );
            else
                % This is a non-struct field. Nest the name so that when we
                % "flatten" things in a few lines we get back to a cell
                fields{ii} = fields(ii);
            end
        end
        % Flatten the names out to get all fields in one denested cell array
        fields = [fields{:}];
    end
    

    Result:

    output