MATLAB Answers

0

Applying functions and assignments on multiple struct fields with only one command

Asked by usr0815 on 9 Sep 2016
Latest activity Answered by usr0815 on 16 Sep 2016
I have this problem very often and want to give just one example. But I want to find - if possible - a more general solution to the problem, not to this specific example. Try the following code:
figure
plot(rand(5,3)) % will plot 3 random lines with three different colors
a = gca;
a.Children(2).Color = rgb2gray(a.Children(2).Color); % will convert the color of the 2nd line (orange with the default color scheme in 2016a) to grayscale
But if I want to apply a function on all fields of Children(:).Color and overwrite the original values in all fields of Children(:).Color with just one command, I will fail, e.g.:
a.Children(:).Color = rgb2gray(a.Children(:).Color); % intention: convert all Colors to Gray and overwrite the original colors with one single assignment...
Error using rgb2gray
Too many input arguments.
The error that occured in this case was obviously already due to the wrong input for rgb2gray on the right side of the assignment (this is a side problem, see later), but even when using (just as example...)
a.Children(:).Color = a.Children(:).Color;
Expected one output from a curly brace or dot indexing expression, but there were 3 results.
there will be an error.
Is there a way how to get such multiple struct assignments solved in a comprehensible way with only one simple command (no for loops, which are so annoying and code space consuming...), which can also be realised when the case is a little bit different... - a general solution, if you want to call it like this? Do you have any other hints for related problems with such (nested?) structs (with complex substructs and so on...)?
Side problem: rgb2gray expected a N x 3 matrix as input. a.Children(:).Color is basically also N x 3 (each Color is 1x3 nested within a length(a.Children) struct), but unfortunately it is contained in a nested struct, so that one has to use a command like this
rgb2gray(reshape([a.Children(:).Color],length(a.Children),3))
to get a N x 3 (length(a.Children) x 3 actually) array from the struct. Is there a simpler command that just converts a nested struct like this (e.g. the Color field of Children(1) to Children(N)) with one simple command to a similar matrix with size(Children) x size(Color)? For example: if 'Children' was a [3x5] struct and 'Color' a [7x6] Matrix, a command that creates a [3x5x7x6] array from it, instead of a confusing [7x(6x3x5)] = [7x90] array, which the square brackets would do (in this example), making it hard to understand how the array must be reshaped if one wants the original structure size back...].
Best regards and thanks,
usr0815

  0 Comments

Sign in to comment.

Products

1 Answer

Answer by usr0815 on 16 Sep 2016
 Accepted Answer

Ultimately, I found it out with the help from a Mathworks® employee. The following code would do the job for the specific color problem and you can derive from that how to do the trick with multi struct assignments in general:
figure
plot(rand(10,5))
legend('Eine', 'Insel', 'mit', 'zwei', 'Bergen');
a = gca;
pause(1); % just that you see that the figure had colors before...
originalColorsCell = {a.Children.Color};
grayColorsCell = cellfun(@(x) {rgb2gray(x)}, originalColorsCell);
[a.Children.Color] = deal(grayColorsCell{:});
So the basic solution is to store the nested struct to a cell with similar structure (I am assuming that there are multiple Children, each having a field called Color). For example Children could be a [8x1-struct] and Color could be a [1x3-double]. Using x = {a.Children.Color} will result in a [1x8-cell], each of these 8 storing a [1x3-double] - so it's basically a conversion from struct to cell if you want so.
*The important (and not so intuitive) command* is the elementwise assignment of the cell contents (needs: {:} ) to the several fields of a.Children.Color (needs square brackets around: [a.Children.Color]):
[a.Children.Color] = deal(x{:});
In fact it even works without deal
[a.Children.Color] = x{:};
(at least for R2014b and higher), as x{:} and [a.Children.Color] should have the exact same number of elements overall. It is easy to understand if you try
u = [a.Children.Color];
v = [x{:}];
as both, u and v will result in a [1x8*3-double] = [1x24-double], which explains why no deal() is needed. But you should probably keep deal in mind for cases where you want to assign a smaller cell to a larger struct... (probably for example if "a" was a [5x1] axis (5 axes) instead of [1x1] and each having [1x8-struct] Children each having a [1x3] Color double).
Best regards,
usr0815

  0 Comments

Sign in to comment.