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

17 visualizaciones (últimos 30 días)
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

Respuesta aceptada

Stephen23
Stephen23 el 11 de En. de 2020
Editada: Stephen23 el 12 de En. de 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 comentario
Raphael Guzman
Raphael Guzman el 13 de En. de 2020
Wow, thank you so much! I was not aware of subsref and subsasgn. This fits my need perfectly.

Iniciar sesión para comentar.

Más respuestas (0)

Categorías

Más información sobre Data Type Conversion en Help Center y File Exchange.

Productos


Versión

R2018b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by