Mayou
Mayou

Reputation: 8818

Checking two MATLAB structs as equal even if field contents are transposed

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

Answers (1)

NoDataDumpNoContribution
NoDataDumpNoContribution

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

Related Questions