Reputation: 8818
I need to check if two MATLAB structs (all fieldnames and values) are equal. Both of these structs are structs of structs. Occasionally, these structs are equal except for one thing: the field values in one struct are the transpose of the field values in the other.
If I use the function isequal
to check if the two structs are equal, I get a negative result if the field contents are transposed.
Example:
cfg1.x = {'a' 'b'};
cfg1.y.z = {'c' 'd'};
cfg2.x = {'a' 'b'}';
cfg2.y.z = {'c' 'd'}';
isequal(cfg1, cfg2)
>> isequal(cfg1, cfg2)
ans =
logical
0
One solution is before checking for equality, I could loop over the fields of one struct and make sure the sizing aligns with the fields of the other struct by transposing if necessary. However, this doesn't seem very efficient, and I like to avoid loops when I can. Is there a function similar to isequal
that is transpose-invariant?
Upvotes: 1
Views: 1744
Reputation: 10860
A while ago I wrote my own recursive comparison of nested structs (mostly to see where variables differ) and you only need to replace isequal(a,b)
with isequal(a,b) || isequal(a,b')
in one line in the code to get a transpose-invariant isequal behavior. The code throws an error if two variables are not equal and also says where.
function compare(A, B, path)
% Compares two variables, A and B, for equality with a recursive test.
% Throws and error if not equal, otherwise just returns.
assert(nargin >= 2, 'Not enough parameters.');
if nargin == 2
path = '';
end
their_class = class(A);
assert(strcmp(their_class, class(B)), '%s, A and B do not have the same class.', path);
if isnumeric(A) || islogical(A)
% here we also treat NaN as equal since we compare the content of two variables
assert(isequaln(A, B), '%s, Array A and B are not equal.', path);
% replace isequaln(A, B) with isequaln(A, B) || isqualn(A, B') to get transpose-invariance of comparison
else
switch their_class
case 'cell'
compare_cells(A, B, path);
case 'struct'
compare_structs(A, B, path);
case 'char'
assert(strcmp(A, B), '%s, Char array A and B is not equal.', path);
otherwise
error('%s, Comparison of class %s not yet supported.', path, their_class);
end
end
end
function compare_cells(A, B, path)
% Assuming A and B are both cell array, compare them, calling back to the
% main function if needed.
assert(isequal(size(A), size(B)), 'Size of cell arrays not equal.');
for i = 1 : numel(A)
compare(A{i}, B{i}, [path, sprintf('/cell:%d', i)]);
end
end
function compare_structs(A, B, path)
% Assuming A and B are both structs, compare them, calling back to the main
% function if needed.
fields = fieldnames(A);
assert(all(strcmp(unique(fields), unique(fieldnames(B)))), 'Number of names of struct fields not equal.');
for i = 1 : length(fields)
field = fields{i};
a = A.(field);
b = B.(field);
compare(a, b, [path, sprintf('/field:%s', field)]);
end
end
Upvotes: 4