First I specify A
as a struct, and two other structs: B
having the same order of elements, and C
having a different order of elements.
A.x = 11;
A.y = 11;
B.x = 21;
B.y = 22;
C.y = 31; %// Note that I am specifying
C.x = 32; %// y first and x second
A = B; %// Works fine
A = C; %// Works fine
Assigning A
to B
and to C
works, which is the behavior I expect from structs - order of elements should not matter.
Now I specify A
as a struct array instead of a struct and try to assign one of its elements to B
and C
respectively:
clear;
A(1).x = 11;
A(1).y = 12;
B.x = 21;
B.y = 22;
C.y = 31; %// Note that I am specifying
C.x = 32; %// y first and x second
A(1) = B; %// Works fine
A(1) = C; %// Error!
Suddenly MATLAB is complaining with the error:
subscripted assignment between dissimilar structures
Does anyone have any idea why this happens and how to fix it in an elegant way?
This is likely happening because the built-in subsasgn
call for the struct
probably just compares the output of fieldnames
(which is dependent upon field order) for the source and destination structs and does not perform any sorting prior to the comparison (likely because of the performance hit of sorting two cell arrays for every assignment). If there is a difference (as in the case you've shown) then an error is thrown and the assignment is aborted.
The easiest way to get around this is to use orderfields
on the source struct and specify that you want the ordering to match the destination struct's using the second input argument.
A = struct('x', 11, 'y', 12);
B = struct('y', 21, 'x', 22);
%// Ensure that the fields of B are in the same order as they are in A
A(1) = orderfields(B, A);
In my personal opinion, I think that subsasgn
should do this itself for struct
inputs because the operation is relatively fast (as there is no sorting and the underlying data of the struct
isn't copied) but allows for more flexible struct
assignment.
If, on the other hand you aren't doing direct assignment, but simply want to append two structs
, the order of the fields does not matter and the order of the fields is inherited from the first struct
that is encountered.
%// Uses the ordering of the fields in A
C = cat(1, A, B);
%// Uses the ordering of the fields in B
D = cat(1, B, A);
Update
I just noticed that you showed in your original post that the following worked because the order didn't matter.
A = B;
This works because this assignment is not dependent upon the datatype of A
. In this scenario, MATLAB removes the data that A
was referencing before the assignment and then re-assigns A
it to the point to B
. We could even make A
a cell array and perform the above assignment with no issues.
A = cell(2);
B = struct('y', 21, 'x', 22);
%// No errors here!
A = B;
This assignment does not make a call to subsasgn
(which deals only with subscript assignment) and therefore would not be expected to exhibit the issue you encountered.
One way that I have solved this in the past is to create a "null" version of the structure, similar to creating a constructor for an object.
%% define null struct
null_struct.x = 0;
null_struct.y = 0;
%% Now, initialize all structs with it
A=null_struct;
B=null_struct;
C=null_struct;
%% You can even initialize large arrays
Z(1:1000, 1:1000) = null_struct;
Then, you can populate the structures in any order you like. You can even pass the "empty" structures into a function, and allow the function to populate the values, and the function doesn't have to be careful about the order the values are assigned.
A(1).x = 11;
A(1).y = 12;
B.x = 21;
B.y = 22;
C.y = 31; % Note that I'm specifying
C.x = 32; % y first and x second
A(1) = B; % Works fine
A(1) = C; % Also works fine!
Initializing your data structures is very good programming practice, and for large struct arrays, it actually saves a lot of time to do the initialization up front. Even if you have to initialize more elements than needed, and truncate the array at the end, it is generally worth it.
EDIT: Explanation for your error: The reason that MATLAB throws an error in your original example is that internally (in the C-code backend), MATLAB stores the field names in an ordered array of character strings, and maps the names to corresponding field indices. When you perform an assignment like
A = C;
MATLAB first checks that the two lists of fieldnames match, which requires that the lists be identical, including in the same order. If they are, then it maps the field values in order from the rhs to the lhs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With