How do I access structure fields dynamically?

2020-01-24 03:58发布

问题:

I have a structure with many fields which are vectors of different lengths. I would like to access the fields within a loop, in order. I tried getfield as follows but MATLAB doesn't like that. How can I do this?

S = struct('A', [1 2], 'B',[3 4 5]);
SNames = fieldnames(S);
for loopIndex = 1:2
  field = getfield(S, SNames(loopIndex));
  %do stuff w/ field
end
??? Index exceeds matrix dimensions

I'm using structures in the first place because an array would have trouble with the different field lengths. Is there a better alternative to that?

回答1:

Try dynamic field reference where you put a string in the parenthesis as seen on the line defining stuff.

S = struct('A', [1 2], 'B',[3 4 5]); 
SNames = fieldnames(S); 
for loopIndex = 1:numel(SNames) 
    stuff = S.(SNames{loopIndex})
end 

I concur with Steve and Adam. Use cells. This syntax is right for people in other situations though!



回答2:

There are three points I'd like to make here:

  • The reason you are getting an error in your above code is because of how you are indexing SNames. The function fieldnames returns a cell array of strings, so you have to use content indexing (i.e. curly braces) to access the string values. If you change the fourth line in your code to this:

    field = getfield(S, SNames{loopIndex});
    

    then your code should work without error.

  • As suggested by MatlabDoug, you can use dynamic field names to avoid having to use getfield (which yields cleaner looking code, in my opinion).

  • The suggestion from Adam to use a cell array instead of a structure is right on the mark. This is generally the best way to collect a series of arrays of different length into a single variable. Your code would end up looking something like this:

    S = {[1 2], [3 4 5]};        % Create the cell array
    for loopIndex = 1:numel(S)   % Loop over the number of cells
      array = S{loopIndex};      % Access the contents of each cell
      % Do stuff with array
    end
    


回答3:

The getfield approach is okay (although I don't have MATLAB available right now and it's not clear to me why the above wouldn't work).

For an alternative data structure, you may also want to look into MATLAB cell arrays. They would also allow you to store and index vectors of varying length.



回答4:

You can use colon notation to avoid indexes:

S = struct('A', [1 2], 'B',[3 4 5]); 
SNames = fieldnames(S); 
for SName = [SNames{:}]
    stuff = S.(SName)
end


回答5:

If you need to use a structure what I found worked very well was to first convert to a cell then you have the best of both worlds.

S = struct('A', [1 2], 'B',[3 4 5]); 
S_Cell = struct2cell(S);
%Then as per gnovice
for loopIndex = 1:numel(S_Sell)   % Loop over the number of cells
    array = S{loopIndex};         % Access the contents of each cell
    %# Do stuff with array
end

I used something similar for something that was generated in a struct and then I needed to access it like a matrix, in that case it was as simple as

M = cell2mat(struct2cell(S));

To convert it to a matrix



回答6:

Just to add another answer to the mix. I like the solution from @Niver but it only works for fields with single letter names. The solution I used was:

S = struct('A', [1 2], 'B',[3 4 5], 'Cee', [6 7]); 
for SName = fieldnames(S)'
    stuff = S.(SName{1})
end

for will iterate through the columns of a cell array (hence the transpose on fieldnames(S)'. For each loop SName becomes a 1x1 cell array so we use content indexing to access the first and only element with SName{1}.