MATLAB Answers

How can I dynamically index into a nested struct that contains cell arrays

4 views (last 30 days)
For a package, I need a global config to be configured by users from Command Window and available to all package functions and classes.
I need to be able to both get and set a config setting.
Suppose that config is currently defined as:
```
config = struct();
config.stores = { ...
struct( ...
'name', 'local', ...
'type', 'file', ...
'location', '/tmp' ...
), ...
struct( ...
'name', 'external', ...
'type', 's3', ...
'access_key', 'aws_key', ...
'access_secret', 'aws_secret' ...
)
}
```
When a user makes a call to Settings('stores{2}.name'), I would like to have it return 'external'.
If a user needs to update a setting, they should be able to do so with Settings('stores{2}.name', 'aws').
To solve this, I have created a Settings function that simply provides an entrypoint into a Settings class that has a static method with a persistent struct variable, config. This works fine utilizing getfield(config, fieldPath{:}) and setfield(config, fieldPath{:}, value), however, it breaks down when accessing a field containing a cell array (as above). This is because a get such as:
fieldPath = {'stores' {[2]} 'name'};
getfield(config, fieldPath{:})
is translated to
config.stores(2).name
The exact error is "Dot indexing is not supported for variables of this type."
Unfortunately, I cannot guarantee the array will have uniform objects as this configuration is actually being loaded from an external process.
My question is: How can I dynamically index into a nested struct over fields containing cell arrays?
This is my first post, so hoping I can get any feedback and tips on best MATLAB practice to resolve such a use case. So far I have been toying with alternatives utilizing recursive loops and eval.
Many thanks in advance!
Raphael

  0 Comments

Sign in to comment.

Accepted Answer

Stephen Cobeldick
Stephen Cobeldick on 11 Jan 2020
Edited: Stephen Cobeldick on 12 Jan 2020
Essentially you are attempting to write a MATLAB parser using MATLAB. Such a task is not trivial, but for a very limited subset of known commands it can be possible to create code that performs operations based on the user input without requiring eval (which gets the MATLAB parser to parse arbitrary MATLAB code, but has significant disadvantages related to efficiency and debugging).
You could use subsref and subsasgn, which allow for indexing into any kind of array, including nested structures and cell arrays, no recursion nor eval is required. For example:
% User input character vector:
str = 'stores{2}.name';
% Create a 2xN cell array {index type ; field/index} from that char vector:
tkn = regexp(['.',str],'(\W)(\w+)','tokens'); % assumes the first part is always a field.
tkn = vertcat(tkn{:}).';
tkn(1,:) = strrep(strrep(tkn(1,:),'{','{}'),'(','()');
% Convert linear indices to numeric:
vec = str2double(tkn(2,:));
idx = ~isnan(vec);
tkn(2,idx) = num2cell(num2cell(vec(idx)));
% Create indexing structure from the cell array:
sbs = substruct(tkn{:});
Now you can use that indexing structure as often as you like with subsref (get data out of array):
>> val = subsref(config,sbs)
val = 'external'
Or with subsasgn (allocate data to array):
config = subsasgn(config,sbs,'aws'); % allocate 'aws' to that location
Tip: if the user supplies the indexing structure itself or a cell array something like tkn, then you can simplfiy your code.

  1 Comment

Raphael Guzman
Raphael Guzman on 13 Jan 2020
Wow, thank you so much! I was not aware of subsref and subsasgn. This fits my need perfectly.

Sign in to comment.

More Answers (0)

Sign in to answer this question.

Products


Release

R2018b

Translated by