MATLAB: How to create structure fields with a name defined by a string. - matlab

I have a constructor like this:
function p = class_name(folder, file_name)
xmlfile = fullfile(folder, file_name);
xDoc = xmlread(xmlfile);
struct = xml2struct(xDoc);
%lots of repetitive code
end
It reads and XML file and transforms it into a struct. The struct elements are then assign to the class's properties/fields using some repetitive code like this:
if(isfield(struct.parameters, 'parameter_name'))
p.property_name = struct.parameters.parameter_name.Text;
else
p.property_name = '';
end
I would like to put this into a method/function which, given the name of the struct element (here 'parameter_name') and the name of the class field/property (here 'property_name') assigns the former to the latter.
Is it possible to write a generic function/method for this? Thanks.

You can implement such a function using dynamic field names. This is the recommended implementation for such cases:
function p = setparam(s, p, param_name, prop_name)
if(isfield(s.parameters, param_name))
param = s.parameters.(param_name);
p.(prop_name) = param.Text;
else
p.(prop_name) = '';
end
end
You can also use setfield and getfield on older versions of MATLAB:
function p = setparam(s, p, param_name, prop_name)
if(isfield(s.parameters, param_name))
param = getfield(s.parameters, param_name);
p = setfield(p, prop_name, getfield(param, 'Text'));
else
p = setfield(p, prop_name, '');
end
end
Do not use struct for structure name in your code - it is a reserved keyword.

Use dynamic field names:
yourStruct.parameters.('parameter_name')

Here is a a generic function:
Can be used for either structs or class objects
function obj_to = set_props(obj_to, obj_from)
% insert properties from obj_from into obj_to.
props_from = fieldnames(obj_from);
props_to = fieldnames(obj_to);
for k = 1 : length(props_to)
if ismember(props_to{k}, props_from)
obj_to.(props_to{k}) = obj_from.(props_to{k});
else
obj_to.(props_to{k}) = '';
end
end
Now, in the constructor:
function obj = class_name(folder, file_name)
xmlfile = fullfile(folder, file_name);
xDoc = xmlread(xmlfile);
s = xml2struct(xDoc); % do not use word "struct" for variables
set_props(obj, s); % if obj has handle, no need to return it from set_props()
end

Related

matlab with classes - save struct in empty double array

I try to create a class in matlab
It has a property children
properties
children
If this variable is written to - it is supposed to be an arrays of structs
it fails with
function obj = Init(obj, valueList)
%INIT Initialise with vector of new parameter sets
newSet = obj.ParamSet;
newSet.values = valueList;
obj.children(end + 1) = newSet; % <<< error
Error is :
Conversion to double from struct is not possible.
This is the struct that is used
methods(Static)
function paramset = ParamSet()
newset.('values') = [];
newset.('fitness') = 0;
paramset = newset;
end
end
The simple solution is to assign if it’s empty:
if isempty(obj.children)
obj.children = newSet;
else
obj.children(end + 1) = newSet;
end

Swap variable names and field names in a set of structs, so the field names become the struct names and vice versa?

I have a set of structs that all have the same field names. For example structs A, B, and C all have field names name and section. Is there a way to rearrange this organization of data so that the data goes from:
A.name = 'bb';
A.section = 199;
B.name = 'joe';
B.section = 101;
C.name = 'rob';
C.section = 33;
to this:
name =
A: 'bb'
B: 'joe'
C: 'rob'
section =
A: 199
B: 101
C: 33
The current code I have, for example, operates like this:
% ORIGINAL STRUCTS
A.name = 'bb';
A.section = 199;
B.name = 'joe';
B.section = 101;
C.name = 'rob';
C.section = 33;
% CREATE VARIABLE NAMES FOR NEW STRUCTS
oldFNames = fieldnames(A); % old field names
oldVNames{1} = varname(A); % old variable names
oldVNames{2} = varname(B);
oldVNames{3} = varname(C);
% RESTRUCTURE STRUCTS (SWITCH FIELDNAMES AND VARIABLE NAMES)
for j = 1:length(oldFNames)
for k = 1:length(oldVNames)
eval([oldFNames{j} '.' oldVNames{k} ' = ' oldVNames{k} '.' oldFNames{j}]);
end
end
function out = varname(var) % Function to get variable name
out = inputname(1);
end
I find the hack to use the varname function to be not great, and I don't know whether there is a way to make it easily adaptable to the number of variables I have. Any input on how to simplify this procedure would be great. Thanks.
It would be easier to do it all in a function, instead of just using a function to get the input variable name. Here's one solution:
function invert_struct(varargin)
varNames = arrayfun(#inputname, 1:nargin, 'UniformOutput', false); % Get input names
s = [varargin{:}]; % Combine inputs into a structure array
for f = fieldnames(s).' % Loop over fields
assignin('caller', f{:}, cell2struct({s.(f{:})}, varNames, 2));
end
end
And you would call it like so, with however many inputs you like:
invert_struct(A, B, C, ...);
The function uses assignin to create your new variables in the calling function and cell2struct to create a new structure from your field data and old variable names. Note that it also makes use of dynamic field names.

Get pointer of class object

I have an object apple created by my own class in MATLAB:
apple = classA();
The class looks like this:
classdef classA < handle
properties
color = 'red';
end
methods
function obj = classA()
% ...
end
end
end
The question: How do I get the object or handle pointer of apple? I want to search for objects by their properties, like:
isprop(eval(mat(i).name),'color')
with mat = whos. So I need to get the pointer of the object, represented by the struct mat(i).name. I just need the reference, not a copy of the desired object. The purpose is this:
If I get the pointer somehow, like
ptr_to_apple_object = get_pointer_fct( mat(i).name )
then I am able to change the properties of the apple-object like:
ptr_to_apple_object. color = 'yellow'
Do you have any ideas? Thanks.
There's really no good way to find all current objects of a particular class, but you could use whos to get a struct about all variables, loop through this and determine which ones have the property you and then modify
variables = whos;
for k = 1:numel(variables)
obj = eval(variables(k).name);
if isobject(obj) && isprop(obj, 'color')
obj.color = 'yellow';
end
end
If you're looking for a specific class, you can use the class field of the output of whos
is_class = ismember({variables.class}, 'classA');
instances = variables(is_class);
for k = 1:numel(instances)
obj = eval(instances(k).name);
obj.color = 'yellow';
end
Update
Since you are subclassing handle, when you assign your instance to a new variable (obj = val(variables(k).name) above), it does not create a copy of your instance, but rather a new reference to the same object.
b = classA;
c = b;
b.color = 'red';
c.color
% 'red'

Better code for accessing fields in a matlab structure array?

I have a matlab structure array Modles1 of size (1x180) that has fields a, b, c, ..., z.
I want to understand how many distinct values there are in each of the fields. i.e.
max(grp2idx([foo(:).a]))
The above works if the field a is a double. {foo(:).a} needs to be used in the case where the field a is a string/char.
Here's my current code for doing this. I hate having to use the eval, and what is essentially a switch statement. Is there a better way?
names = fieldnames(Models1);
for ix = 1 : numel(names)
className = eval(['class(Models1(1).',names{ix},')']);
if strcmp('double', className) || strcmp('logical',className)
eval([' values = [Models1(:).',names{ix},'];']);
elseif strcmp('char', className)
eval([' values = {Models1(:).',names{ix},'};']);
else
disp(['Unrecognized class: ', className]);
end
% this line requires the statistics toolbox.
[g, gn, gl] = grp2idx(values);
fprintf('%30s : %4d\n',names{ix},max(g));
end
Indeed, there is a better way. Surprisingly, MATLAB allows you to access the struct fields using a key string without eval, for instance:
Models1(1).(names{ix})
so instead, you can write this:
className = class(Models1(1).(names{ix});
...
values = [Models1(:).(names{ix})];
...
values = {Models1(:).(names{ix})};
Also, instead of using class and strcmp, you can just test the same conditions with isa:
v1 = Models1(1).(names{ix});
if (isa(v1, 'double') || isa(v1, 'logical'))
values = [Models1(:).(names{ix})];
% # ...
elseif (isa(v1, 'char'))
values = {Models1(:).(names{ix})};
% # ...
else
disp(['Unrecognized class: ', class(v1)]);
end
It should be much faster.

MATLAB - Cleanly read variables out of a function possibly using some type of varargout...?

I have function f1 which must contain subfunctions, so I can't use another script:
function vars = f1()
a = 1;
b = 'hello';
c = {[1 2 3]};
currvars = whos; %all variable info
for k = 1:size(currvars, 1)
eval(['vars.(currvars(k).name) = ' currvars(k).name ';']);
end
end
I call the function in a script, and then I create the variables in the script using genvarname() and eval():
vars = f1();
varnames = genvarname(fieldnames(vars));
for k = 1:size(varnames(:),1) %Creates vars with the struct's fieldnames.
eval([varnames{k} ' = vars.' varnames{k} ';']);
end
clearvars vars varnames
I'd like to fit the variable creation process into a function somehow while reading out all the variables only to the script calling it, but I don't want to assign a hundred different variable names to the output. Does anyone have any advice on how to do this?
DON'T CREATE zillions of automatically named variables. This is foolish and terribly poor programming style. Instead, learn to use arrays. Multidimensional arrays, cell arrays, struct arrays.
Or, do what you have done, and then be forced to find a kludge like you are looking for.
Take your pick. Eval is evil.
You could return a structure with "corrected" fieldnames:
function output = someFunc
vars = f1();
protected = { %# some list of varnames you don't want
};
names = fieldnames(vars);
newnames = genvarname(names, protected);
for k = 1:numel(newnames)
output.(newnames{k}) = vars.(names{k});
end
end
If you then use
output = someFunc;
varnames = fieldnames(output);
for k = 1:numel(fieldnames)
eval([varnames{k} ' = output.' varnames{k} ';']);
end
in the script/function where you want the variable names, you have a relatively clean way of locking everything up in the function someFunc without having to pre-define all output variable names.
You can also do this in one step:
function someFunc2
vars = f1();
protected = { %# some list of varnames you don't want
};
names = fieldnames(vars);
newnames = genvarname(names, protected);
for k = 1:numel(newnames)
assignin('caller', newnames{k}, vars.(names{k}));
end
end
Which means you just call the function
someFunc2;
in your script, and the function then defines and assigns all the variables in the script's workspace.
A tiny step forward, but forward nonetheless :)
CREDIT TO RODY...
#Old semi-"global" variables:
function vars = f1()
a = 1;
b = 'hello';
c = {[1 2 3]};
currvars = whos; %all variable info
for k = 1:size(currvars, 1)
eval(['vars.(currvars(k).name) = ' currvars(k).name ';']);
end
end
#Function to assign variables to the calling script's/function's workspace. Variable names are determined by the single structure's fieldnames:
function setvars(func)
vars = func;
protected = {'If needed, put variable names you do not want here'};
names = fieldnames(vars);
newnames = genvarname(names, protected);
for k = 1:numel(newnames)
assignin('caller', newnames{k}, vars.(names{k}));
end
end
#Script call:
setvars(f1);