matlabmatlab-struct

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?


Solution

  • 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