Nested structure access using dynamic fieldnames - matlab

I'd like to achieve the following using dynamic fieldnames instead of setfield:
Say a struct 'myStruct' has a set of nested structures, i.e.
myStruct.a.b.c = 0
myStruct.a.d = 0
myStruct.a.e.f.g = 0
I want to be able to flexibly set the leaf structure values as follows:
fields = {'a', 'b', 'c'}
paramVal = 1
setfield(myStruct, fields{:}, paramVal)
This works using setfield. Is there a syntax that will do this using dynamic fieldnames? The following obviously doesn't work because the fieldname needs to be a string not an array, but demonstrates what I want:
myStruct.(fields{:}) = 0
Which would be equivalent to:
myStruct.('a').('b').('c') = 0

Recursive solution without eval, ripped from one of my old utility functions:
function s = setsubfield(s, fields, val)
if ischar(fields)
fields = regexp(fields, '\.', 'split'); % split into cell array of sub-fields
end
if length(fields) == 1
s.(fields{1}) = val;
else
try
subfield = s.(fields{1}); % see if subfield already exists
catch
subfield = struct(); % if not, create it
end
s.(fields{1}) = setsubfield(subfield, fields(2:end), val);
end
I guess the try/catch can be replaced with if isfield(s, fields{1}) ..., I don't remember why I coded it like that.
Usage:
>> s = struct();
>> s = setsubfield(s, {'a','b','c'}, 55);
>> s = setsubfield(s, 'a.b.d.e', 12)
>> s.a.b.c
ans =
55
>> s.a.b.d.e
ans =
12

Below is a simple, if crude, solution that works for scalar structs. Applying it to your example,
S=setfld(myStruct,'a.b.c',1)
>> S.a.b.c
ans =
1
In general, though, deeply nested structs are unrecommended.
function S=setfld(S,fieldpath,V)
%A somewhat enhanced version of setfield() allowing one to set
%fields in substructures of structure/object S by specifying the FIELDPATH.
%
%Usage: setfld(S,'s.f',V) will set S.s.f=V
%
%
%%Note that for structure S, setfield(S.s,'f') would crash with an error if
%S.s did not already exist. Moreover, it would return a modified copy
%of S.s rather than a modified copy of S, behavior which would often be
%undesirable.
%
%
%Works for any object capable of a.b.c.d ... subscripting
%
%Currently, only single structure input is supported, not structure arrays.
try
eval(['S.' fieldpath '=V;']);
catch
error 'Something''s wrong.';
end

Related

find string in non-scalar structure matlab

Here's a non-scalar structure in matlab:
clearvars s
s=struct;
for id=1:3
s(id).wa='nko';
s(id).test='5';
s(id).ad(1,1).treasurehunt='asdf'
s(id).ad(1,2).treasurehunt='as df'
s(id).ad(1,3).treasurehunt='foobar'
s(id).ad(2,1).treasurehunt='trea'
s(id).ad(2,2).treasurehunt='foo bar'
s(id).ad(2,3).treasurehunt='treasure'
s(id).ad(id,4).a=magic(5);
end
is there an easy way to test if the structure s contains the string 'treasure' without having to loop through every field (e.g. doing a 'grep' through the actual content of the variable)?
The aim is to see 'quick and dirtily' whether a string exists (regardless of where) in the structure. In other words (for Linux users): I'd like to use 'grep' on a matlab variable.
I tried arrayfun(#(x) any(strcmp(x, 'treasure')), s) with no success, output:
ans =
1×3 logical array
0 0 0
One general approach (applicable to any structure array s) is to convert your structure array to a cell array using struct2cell, test if the contents of any of the cells are equal to the string 'treasure', and recursively repeat the above for any cells that contain structures. This can be done in a while loop that stops if either the string is found or there are no structures left to recurse through. Here's the solution implemented as a function:
function found = string_hunt(s, str)
c = reshape(struct2cell(s), [], 1);
found = any(cellfun(#(v) isequal(v, str), c));
index = cellfun(#isstruct, c);
while ~found && any(index)
c = cellfun(#(v) {reshape(struct2cell(v), [], 1)}, c(index));
c = vertcat(c{:});
found = any(cellfun(#(c) isequal(c, str), c));
index = cellfun(#isstruct, c);
end
end
And using your sample structure s:
>> string_hunt(s, 'treasure')
ans =
logical
1 % True!
This is one way to avoid an explicit loop
% Collect all the treasurehunt entries into a cell with strings
s_cell={s(1).ad.treasurehunt, s(2).ad.treasurehunt, s(3).ad.treasurehunt};
% Check if any 'treasure 'entries exist
find_treasure=nonzeros(strcmp('treasure', s_cell));
% Empty if none
if isempty(find_treasure)
disp('Nothing found')
else
disp(['Found treasure ',num2str(length(find_treasure)), ' times'])
end
Note that you can also just do
% Collect all the treasurehunt entries into a cell with strings
s_cell={s(1).ad.treasurehunt, s(2).ad.treasurehunt, s(3).ad.treasurehunt};
% Check if any 'treasure 'entries exist
find_treasure=~isempty(nonzeros(strcmp('treasure', s_cell)));
..if you're not interested in the number of occurences
Depending on the format of your real data, and if you can find strings that contain your string:
any( ~cellfun('isempty',strfind( arrayfun( #(x)[x.ad.treasurehunt],s,'uni',0 ) ,str)) )

Dynamically assign structure field name with hierarchy

I would like to be able to dynamically assign fields to a structure array, including hierarchy, in MATLAB (r2014a). I'm not sure how best to describe the problem except through the following example.
I know how to do this:
field_name1 = 'bar1';
field_name2 = 'bar2';
% ... etc.
foo.(field_name1) = pi;
foo.(field_name2) = exp(1);
% ... etc.
results in a structure variable foo with fields bar1 and bar2.
What I would like to be able to do is this:
field_name1 = 'bar1';
field_name2 = 'bar2';
% ... etc.
if true_false_statement
extra_level = '';
else
extra_level = 'baz.';
end
foo.([extra_level field_name1]) = pi;
foo.([extra_level field_name2]) = exp(1);
% ... etc.
where depending on a previous condition, the fields can be stored either in the top level of the structure or else under a sub-field.
(Note that if I was doing this from scratch, I would design things differently to avoid this problem. However, I am modifying some code that is the middle step in a much larger workflow, so I have to keep the data structure consistent.)
Currently, the code looks something like this:
field_name1 = 'bar1';
field_name2 = 'bar2';
% ... etc.
if true_false_statement
foo.(field_name) = pi;
foo.(field_name) = exp(1);
% ... etc.
else
foo.baz.(field_name) = pi;
foo.baz.(field_name) = exp(1);
% ... etc.
end
Perhaps fine for one or two field, but there are a lot of fields, which results in a lot of copy/pasted code. I have tried making extra_level an empty field, and I have tried making field_name of the form *.* as above, but both options throw an error because the result is not a valid variable name.
Is there a good way to do this?
The simplest manner I can think of to achieve your desired use (the middle block of code) is to use setfield (which is mostly a wrapper for subsasgn these days) and the fact that an empty cell array expands to nothing (something I've answered about in the past and what I like to call a feature):
>> foo = struct();
>> extra_level = {};
>> foo = setfield(foo,extra_level{:},'bar1',1)
foo =
bar1: 1
>> extra_level = {'baz'};
>> foo = setfield(foo,extra_level{:},'bar2',2)
foo =
bar1: 1
baz: [1x1 struct]
>> foo.baz.bar2
ans =
2

Matlab coder & dynamic field references

I'm trying to conjure up a little parser that reads a .txt file containing parameters for an algorithm so i don't have to recompile it everytime i change a parameter. The application is C code generated from .m via coder, which unfortunately prohibits me from using a lot of handy matlab gimmicks.
Here's my code so far:
% read textfile
string = readfile(filepath);
% do fancy rearranging
linebreaks = zeros(size(string));
equals = zeros(size(string));
% find delimiters
for n=1:size(string,2)
if strcmp(string(n),char(10))
linebreaks(n) = 1;
elseif strcmp(string(n), '=')
equals(n) = 1;
end
end
% write first key-value pair
idx_s = find(linebreaks);idx_s = [idx_s length(string)];
idx_e = find(equals);
key = string(1:idx_e(1)-1);
value = str2double(string(idx_e(1)+1:idx_s(1)-1));
parameters.(key) = value;
% find number of parameters
count = length(idx_s);
% write remaining key-value pairs
for n=2:count
key = string(idx_s(n-1)+1:idx_e(n)-1);
value = str2double(string(idx_e(n)+1:idx_s(n)-1));
parameters.(key) = value;
end
The problem is that seemingly coder does not support dynamic fieldnames for structures like parameters.(key) = value.
I'm a bit at a loss as to how else i am supposed to come up with a parameter struct that holds all my key-value pairs without hardcoding it. It would somewhat (though not completely) defeat the purpose if the names of keys were not dynamically linked to the parameter file (more manual work if parameters get added/deleted, etc.). If anybody has an idea how to work around this, i'd be very grateful.
As you say, dynamic fieldnames for structures aren't allowed in MATLAB code to be used by Coder. I've faced situations much like yours before, and here's how I handled it.
First, we can list some nice tools that are allowed in Coder. We're allowed to have classes (value or handle), which can be quite handy. Also, we're allowed to have variable sized data if we use coder.varsize to specifically designate it. We also can use string values in switch statements if we like. However, we cannot use coder.varsize for properties in a class, but you can have varsized persistent variables if you like.
What I'd do in your case is create a handle class for storing and retrieving the values. The following example is pretty basic, but will work and could be expanded. If a persistent variable were used in a method, you could even create a varsized allocated storage for the data, but in my example, it's a property and has been limited in the number of values it can store.
classdef keyval < handle %# codegen
%KEYVAL A key and value class designed for Coder
% Stores an arbitrary number of keys and values.
properties (SetAccess = private)
numvals = 0
end
properties (Access = private)
intdata
end
properties (Constant)
maxvals = 100;
maxkeylength = 30;
end
methods
function obj = keyval
%KEYVAL Constructor for keyval class
obj.intdata = repmat(struct('key', char(zeros(1, obj.maxkeylength)), 'val', 0), 1, obj.maxvals);
end
function result = put(obj, key, value)
%PUT Adds a key and value pair into storage
% Result is 0 if successful, 1 on error
result = 0;
if obj.numvals >= obj.maxvals
result = 1;
return;
end
obj.numvals = obj.numvals + 1;
tempstr = char(zeros(1,obj.maxkeylength));
tempstr(1,1:min(end,numel(key))) = key(1:min(end, obj.maxkeylength));
obj.intdata(obj.numvals).key = tempstr;
obj.intdata(obj.numvals).value = value;
end
function keystring = getkeyatindex(obj, index)
%GETKEYATINDEX Get a key name at an index
keystring = deblank(obj.intdata(index).key);
end
function value = getvalueforkey(obj, keyname)
%GETVALUEFORKEY Gets a value associated with a key.
% Returns NaN if not found
value = NaN;
for i=1:obj.numvals
if strcmpi(keyname, deblank(obj.intdata(i).key))
value = obj.intdata(i).value;
end
end
end
end
end
This class implements a simple key/value addition as well as lookup. There are a few things to note about it. First, it's very careful in the assignments to make sure we don't overrun the overall storage. Second, it uses deblank to clear out the trailing zeros that are necessary in the string storage. In this situation, it's not permitted for the strings in the structure to be of different length, so when we put a key string in there, it needs to be exactly the same length with trailing nulls. Deblank cleans this up for the calling function.
The constant properties allocate the total amount of space we're allowed in the storage array. These can be increased, obviously, but not at runtime.
At the MATLAB command prompt, using this class looks like:
>> obj = keyval
obj =
keyval with properties:
numvals: 0
>> obj.put('SomeKeyName', 1.23456)
ans =
0
>> obj
obj =
keyval with properties:
numvals: 1
>> obj.put('AnotherKeyName', 34567)
ans =
0
>> obj
obj =
keyval with properties:
numvals: 2
>> obj.getvalueforkey('SomeKeyName')
ans =
1.2346
>> obj.getkeyatindex(2)
ans =
AnotherKeyName
>> obj.getvalueforkey(obj.getkeyatindex(2))
ans =
34567
If a totally variable storage area is desired, the use of persistent variables with coder.varsize would work, but that will limit the use of this class to a single instance. Persistent variables are nice, but you only get one of them ever. As written, you can use this class in many different places in your program for different storage. If you use a persistent variable, you may only use it once.
If you know some of the key names and are later using them to determine functionality, remember that you can switch on strings in MATLAB, and this works in Coder.

MATLAB -> struct.field(1:end).field?

Is there a way that I get all the structure subsubfield values of a subfield in one line ? Something like this :
struct.field(1:end).field
If I understand your question aright, you want to collect all the fields of the second-level structure, with the name 'field', into a single output array. It doesn't quite meet your request for a one-liner, but you can do it like this:
a.field1.a = 1;
a.field1.b = 2;
a.field2.a = 3;
a.field2.b = 4;
result = [];
for x = fieldnames(a)'
result = horzcat(result, a.(x{:}).a);
end
The ending value of result is [1 3]
Simple Structure Example
aStruct.subField = struct('subSubField', {1;2;3;4})
So that
aStruct.subField(1).subSubField == 1
aStruct.subField(1).subSubField == 2
Etc. Then the values of the leaf nodes can be obtained via a one-liner as
valueLeafs = [aStruct.subField.subSubField];
Which can be checked via assert(all(valueLeafs == [1,2,3,4])).
Non-Scalar Structure Example
The above one-liner also works when the leaf node values are non-scalar such that they can be horizontally concatenated. For example
bStruct.subField = struct('subSubField', {[1,2];[3,4]})
valueLeafs_b = [bStruct.subField.subSubField]; % works okay
cStruct.subField = struct('subSubField', {[1,2];[3;4]})
valueLeafs_c = [cStruct.subField.subSubField]; % error: bad arg dims
Distinct Class Structure Example
The one-line solution given previously does not work whenever the leaf node values are different class since they cannot - in general, be concatenated. However, use of arrayfun and a tricky anonymous function typically provide the required indexing technique:
dStruct.subField = struct('subSubField', {[1;2];'myString'});
valueLeafs_d = arrayfun(#(x) x.subSubField, dStruct.subField, 'UniformOutput', false)

What are some efficient ways to combine two structures in MATLAB?

I want to combine two structures with differing fields names.
For example, starting with:
A.field1 = 1;
A.field2 = 'a';
B.field3 = 2;
B.field4 = 'b';
I would like to have:
C.field1 = 1;
C.field2 = 'a';
C.field3 = 2;
C.field4 = 'b';
Is there a more efficient way than using "fieldnames" and a for loop?
EDIT: Let's assume that in the case of field name conflicts we give preference to A.
Without collisions, you can do
M = [fieldnames(A)' fieldnames(B)'; struct2cell(A)' struct2cell(B)'];
C=struct(M{:});
And this is reasonably efficient. However, struct errors on duplicate fieldnames, and pre-checking for them using unique kills performance to the point that a loop is better. But here's what it would look like:
M = [fieldnames(A)' fieldnames(B)'; struct2cell(A)' struct2cell(B)'];
[tmp, rows] = unique(M(1,:), 'last');
M=M(:, rows);
C=struct(M{:});
You might be able to make a hybrid solution by assuming no conflicts and using a try/catch around the call to struct to gracefully degrade to the conflict handling case.
Short answer: setstructfields (if you have the Signal Processing Toolbox).
The official solution is posted by Loren Shure on her MathWorks blog, and demonstrated by SCFrench here and in Eitan T's answer to a different question. However, if you have the Signal Processing Toolbox, a simple undocumented function does this already - setstructfields.
help setstructfields
setstructfields Set fields of a structure using another structure
setstructfields(STRUCTIN, NEWFIELDS) Set fields of STRUCTIN using
another structure NEWFIELDS fields. If fields exist in STRUCTIN
but not in NEWFIELDS, they will not be changed.
Internally it uses fieldnames and a for loop, so it is a convenience function with error checking and recursion for fields that are themselves structs.
Example
The "original" struct:
% struct with fields 'color' and 'count'
s = struct('color','orange','count',2)
s =
color: 'orange'
count: 2
A second struct containing a new value for 'count', and a new field, 'shape':
% struct with fields 'count' and 'shape'
s2 = struct('count',4,'shape','round')
s2 =
count: 4
shape: 'round'
Calling setstructfields:
>> s = setstructfields(s,s2)
s =
color: 'orange'
count: 4
shape: 'round'
The field 'count' is updated. The field 'shape' is added. The field 'color' remains unchanged.
NOTE: Since the function is undocumented, it may change or be removed at any time.
I have found a nice solution on File Exchange: catstruct.
Without testing the performance I can say that it did exactly what I wanted.
It can deal with duplicate fields of course.
Here is how it works:
a.f1 = 1;
a.f2 = 2;
b.f2 = 3;
b.f4 = 4;
s = catstruct(a,b)
Will give
s =
f1: 1
f2: 3
f3: 4
I don't think you can handle conflicts well w/o a loop, nor do I think you'd need to avoid one. (although I suppose efficiency could be an issue w/ many many fields...)
I use a function I wrote a few years back called setdefaults.m, which combines one structure with the values of another structure, where one takes precedence over the other in case of conflict.
% SETDEFAULTS sets the default structure values
% SOUT = SETDEFAULTS(S, SDEF) reproduces in S
% all the structure fields, and their values, that exist in
% SDEF that do not exist in S.
% SOUT = SETDEFAULTS(S, SDEF, OVERRIDE) does
% the same function as above, but if OVERRIDE is 1,
% it copies all fields of SDEF to SOUT.
function sout = setdefaults(s,sdef,override)
if (not(exist('override','var')))
override = 0;
end
sout = s;
for f = fieldnames(sdef)'
cf = char(f);
if (override | not(isfield(sout,cf)))
sout = setfield(sout,cf,getfield(sdef,cf));
end
end
Now that I think about it, I'm pretty sure that the "override" input is unnecessary (you can just switch the order of the inputs) though I'm not 100% sure of that... so here's a simpler rewrite (setdefaults2.m):
% SETDEFAULTS2 sets the default structure values
% SOUT = SETDEFAULTS(S, SDEF) reproduces in S
% all the structure fields, and their values, that exist in
% SDEF that do not exist in S.
function sout = setdefaults2(s,sdef)
sout = sdef;
for f = fieldnames(s)'
sout = setfield(sout,f{1},getfield(s,f{1}));
end
and some samples to test it:
>> S1 = struct('a',1,'b',2,'c',3);
>> S2 = struct('b',4,'c',5,'d',6);
>> setdefaults2(S1,S2)
ans =
b: 2
c: 3
d: 6
a: 1
>> setdefaults2(S2,S1)
ans =
a: 1
b: 4
c: 5
d: 6
In C, a struct can have another struct as one of it's members. While this isn't exactly the same as what you're asking, you could end up either with a situation where one struct contains another, or one struct contains two structs, both of which hold parts of the info that you wanted.
psuedocode: i don't remember the actual syntax.
A.field1 = 1;
A.field2 = 'a';
A.field3 = struct B;
to access:
A.field3.field4;
or something of the sort.
Or you could have struct C hold both an A and a B:
C.A = struct A;
C.B = struct B;
with access then something like
C.A.field1;
C.A.field2;
C.B.field3;
C.B.field4;
hope this helps!
EDIT: both of these solutions avoid naming collisions.
Also, I didn't see your matlab tag. By convention, you should want to edit the question to include that piece of info.