Indexing over all values in nested struct - matlab

I have a nested struct which contains values and is defined as:
mystruct.level1.a = 1;
mystruct.level1.b = 2;
mystruct.level2.a = 8;
mystruct.level2.b = 9;
I want to perform operations on the elements in level1 and level2. What I want to do is access the values in level1 and level2, put them in a vector, without referencing the nested field names.
E.g. I'd like to do something like:
level1_vector = [mystruct.level1]
Which I would like to output:
level1_vector = [1 2]
How can I do that?

Use the combination of two functions below:
cell2mat(struct2cell(mystruct.level1))

There's a structfun to do just that. It will return another struct, with the same names. So for your case:
con_struct = structfun (#(x) [x.a x.b], mystruct, "UniformOutput", false);
Now, con_struct will have the same fields as mystruct, but instead of a struct, each of them is an array with the values you wanted. You can feed each of arrays again to whatever function you want
structfun (#foo, con_struct)

Related

Maximum value of fields of nested structure

I have following structure:
S.s1.val = 1;
S.s2.val= 5;
S.s3.val= 4;
...
S.s10.value = 3;
How can I find max value of all val fields without using loops. And what is general solution to apply functions to all nested structure fields?
There is no general solution, but one way to think of is structfun to collect the data you want to process to an array.
maxval = max( structfun(#(x) x.val, S) )
Internally structfun works serially like a loop, so if you're really into speed, don't use structs (or cell arrays).

Matlab: iterate over multiple structs

I have 5 Matlab structs. I would like to iterate over them. My current solution is the following:
all_structs = [struct1,struct2,struct3,struct4,struct5];
for single_struct = all_structs
% do stuff to each struct here
end
However, each of the structs above has a matrix with a lot of data (including some other properties). Also, whatever I change in the single_struct is not passed back to the original struct.
Question: How do I fix that? Does Matlab copy all that data again when I create the vector all_structs? Or is the data from each of the structs (struct1,...,struct5) passed by reference? Is there a better way to iterate over multiple structs?
Thanks for helping!
struct will not be passed by reference. You will need to loop over the elements in all_structs using an index and then access and modify using that index. If you need something to be treated as reference you will need to define a class for it and make the class inherit from handle. Suggested reading
for i = 1:numel(all_structs)
% do stuff to each struct here
all_structs(i).data = ones(10,5); % your code here
end
I would suggest also reading on arrayfun, though it is useful if you want to do an operation and get results. From your description it sounds like you want to modify the structs.
In case you want to modify content of original structs, without making a copy, you can use a cell array of structs names.
Then iterate the names, and use eval to modify the content.
Using eval is inefficient, so don't make it a habit...
See the following code sample:
%Create sample structs (each struct has a data element).
struct1.data = 1;
struct2.data = 2;
struct3.data = 3;
%Create a cell array containing structs names as strings.
struct_names = {'struct1', 'struct2', 'struct3'};
%Iterate all structs names
%Modify data elements of each struct using eval.
for i = 1:length(struct_names)
sname = struct_names{i}; %Get struct name from cell array.
%Evaluate a string like: 'struct1.data = struct1.data + 1;'
eval([sname, '.data = ', sname, '.data + 1;']);
end

Concatenating (sub)fields of structs in a cell array

I have a Matlab object, that is a cell array containting structs that have almost identical structures and I want to programmatically get a (sub)field of the structs of all cell array elements.
For example, we take test
test = {struct('a',struct('sub',1)), struct('a',struct('sub',2),'b',1)};
This will create a cell array with the following structure:
cell-element 1: a --> sub --> 1
cell-element 2: a --> sub --> 2
\-> b --> 1
It can be seen that the elements of test don't have the exact same structure, but similar. How can I get all values of the a.sub fields of the elements of the cell. I can obtain them in this specific problem with
acc=zeros(1,numel(test));
for ii=1:numel(test)
acc(ii) = test{ii}.a.sub;
end
but I can't quite get this method to work in a more general context (ie. having different fields).
You may want to use the function getfield:
%//Data to play with
test = {struct('a',struct('sub',1)), struct('a',struct('sub',2),'b',1)};
%//I'm interested in these nested fields
nested_fields = {'a', 'sub'};
%//Scan the cell array to retrieve the data array
acca = cellfun(#(x) getfield(x, nested_fields{:}), test);
In case your data cannot guarantee that all the elements are the same type and size, then you need to output a cell array instead:
%//Scan the cell array to retrieve the data cell array
accc = cellfun(#(x) getfield(x, nested_fields{:}), test, 'UniformOutput', false);
Later Edit
If one wants to use different sets of nested fields for each cell element then:
%//nested_fields list should have the same size as test
nested_fields = {{'a','sub'}, {'b'}};
accm = cellfun(#(x,y) getfield(x,y{:}), test, nested_fields, 'UniformOutput', false);
Edit: No need for recursion, as shown by #CST-link:s answer; the native getfield function can neatly unfold a cell array of fields as its second argument, e.g. getfield(foo{i}, fields{:}) instead of the call to the recursive function in my old answer below. I'll leave the recursive solution below, however, as it could have some value in the context of the question.
You can build you own recursive version of getField, taking a cell array of fields.
function value = getFieldRec(S,fields)
if numel(fields) == 1
value = getfield(S, fields{1});
else
S = getfield(S,fields{1})
fields{1} = [];
fields = fields(~cellfun('isempty',fields));
value = getFieldRec(S,fields);
end
end
Example usage:
foo = {struct('a',struct('sub',1)), ...
struct('a',struct('sub',2),'b',3), ...
struct('c',struct('bar',7),'u',5)};
accessFields = {'a.sub', 'b', 'c.bar'};
values = zeros(1,numel(foo));
for i = 1:numel(foo)
fields = strsplit(accessFields{i},'.');
values(i) = getFieldRec(foo{i},fields);
end
With the following result
values =
1 3 7
I have found a way to do this using eval:
function out = catCellStructSubField(cellStruct, fieldName)
out = zeros(1,numel(cellStruct));
for ii = 1:numel(cellStruct)
out(ii) = eval(['cellStruct{ii}.' fieldName]);
end
Where it can be used on my test example like this:
catCellStructSubField(test, 'a.sub')
Dynamic field names (cellStruct{ii}.(fieldName)) does not work because I'm accessing sub-fields.
I know eval is often a bad idea. I'm curious for different solutions.

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)

Simultaneously assign values to multiple structure fields

I have a matlab structure that follows the following pattern:
S.field1.data1
...
.field1.dataN
...
.fieldM.data1
...
.fieldM.dataN
I would like to assign values to one data field (say, data3) from all fields simultaneously. That would be semantically similar to:
S.*.data3 = value
Where the wildcard "*" represents all fields (field1,...,fieldM) in the structure. Is this something that can be done without a loop in matlab?
Since field1 .. fieldM are structure arrays with identical fields, why not make a struct array for "field"? Then you can easily set all "data" members to a specific value using deal.
field(1).data1 = 1;
field(1).data2 = 2;
field(2).data1 = 3;
field(2).data2 = 4;
[field.data1] = deal(5);
disp([field.data1]);
A loop-based solution can be flexible and easily readable:
names = strtrim(cellstr( num2str((1:5)','field%d') )); %'# field1,field2,...
values = num2cell(1:5); %# any values you want
S = struct();
for i=1:numel(names)
S.(names{i}).data3 = values{i};
end
In simple cases, you could do that by converting your struct into a cell array using struct2cell(). As you have a nested structure, I don't think that will work here.
On the other side, is there any reason why your data is structured like this. Your description gives the impression that a simple MxN array or cell array would be more suitable.