Code covered by the BSD License  

Highlights from
Compare Structures

from Compare Structures by michael arant
Compares two structured variables recursively and notes where the two structures are different.

comp_struct(s1,s2,prt,pse,tol,n1,n2,wbf)
function [match, er1, er2, erc] = comp_struct(s1,s2,prt,pse,tol,n1,n2,wbf)
% check two structures for differances - i.e. see if strucutre s1 == structure s2
% function [fs1, fs2, er] = comp_struct(s1,s2,n1,n2,p,tol)
%
% inputs  8 - 7 optional
% s1      structure one                              class structure
% s2      structure two                              class structure - optional
% prt     print test results (0 / 1 / 2 / 3)         class integer - optional
% pse     pause flag (0 / 1 / 2)                     class integer - optional
% tol     tol default tolerance (real numbers)       class integer - optional
% n1      first structure name (variable name)       class char - optional
% n2      second structure name (variable name)      class char - optional
% wbf     waitbar flag (0 / 1) default is 1          class integer - optional
%
% outputs 4 - 4 optional
% match   matching fields                            class cell - optional
% er1     non-matching feilds for structure one      class cell - optional
% er2     non-matching feilds for structure two      class cell - optional
% erc     common error (both structures listed)      class cell - optional
%
% prt:
%	0 --> no print
%	1 --> print major erros
%	2 --> print all errors
%	3 --> print errors and matches
% pse:
%	1 --> pause for major erros
%	2 --> pause for all errors
%
% example:	[match, er1, er2] = comp_struct(data1,data2,1,1,1e-6,'data1','data2')
% michael arant - may 27, 2013
%
% hint:
% passing just one structure causes the program to copy the structure
% and compare the two.  This is an easy way to list the structure

if nargin < 1; help comp_struct; error('I / O error'); end
if nargin < 2; s2 = s1; prt = 3; end
if nargin < 3; prt = 1; end
if nargin < 4; pse = 0; elseif pse ~= 1 && prt == 0; pse = 0; end
if nargin < 5; tol = 1e-6; end
if nargin < 6; n1 = 's1'; end
if nargin < 7; n2 = 's2'; end
if nargin < 8; wbf = 1; end
if pse > prt, pse = prt; end

% init outputs
match = {}; er1 = {}; er2 = {};


% test to se if both are structures
if isstruct(s1) && isstruct(s2)
	% both structures - get the field names for each structure
	fn1 = fieldnames(s1);
	fn2 = fieldnames(s2);
	% missing fields? get the common fields
	temp1 = ismember(fn1,fn2);
	temp2 = ismember(fn2,fn1);
	% missing fileds in set 1
	for ii = find(~temp2)'
		er1{end+1} = sprintf('%s is missing field %s',n1,fn2{ii});
		if prt; fprintf('%s\n',er1{end}); end; if pse; pause; end
	end
	% missing fields in set 2
	for ii = find(~temp1)'
		er2{end+1} = sprintf('%s is missing field %s',n2,fn1{ii});
		if prt; fprintf('%s\n',er2{end}); end; if pse; pause; end
	end
	% index sizes match?  i.e. do both structures have the same # of indexes?
	inda = numel(s1); indb = numel(s2); inder = inda-indb;
	if inder < 0
		% struct 1 is smaller
		for ii = inda+1:indb
			er1{end+1} = sprintf('%s(%g) is missing',n1,ii);
			if prt; fprintf('%s\n',er1{end}); end; if pse; pause; end
		end
	elseif inder > 0
		% index 2 is smaller
		for ii = indb+1:inda
			er2{end+1} = sprintf('%s(%g) is missing',n2,ii);
			if prt; fprintf('%s\n',er2{end}); end; if pse; pause; end
		end
	end
	% get common fields
	fn = fn1(temp1); fnn = numel(fn); 
	% loop through structure 1 and match to structure 2
	ind = min([inda indb]); cnt = 0; 
	if wbf; wb = waitbar(0,'Comparing ....'); end
	for ii = 1:ind
		% loop each index
		for jj = 1:fnn
			% loop common field names
			if wbf; cnt = cnt + 1; waitbar(cnt/(ind*fnn),wb); drawnow; end
			% add index and field name to the structure name
			n1p = sprintf('%s(%g).%s',n1,ii,fn{jj});
			n2p = sprintf('%s(%g).%s',n2,ii,fn{jj});
			% recurse - run the program again on the sub-set of the structure
			[m e1 e2] = comp_struct(s1(ii).(fn{jj}),s2(ii).(fn{jj}),prt,pse, ...
				tol,n1p,n2p,wbf);
			% add the sub-set (field name) results to the total results
			match = [match m']; er1 = [er1 e1']; er2 = [er2 e2'];
		end
	end
	if wbf;	close(wb); end
else
	% both are non-structures - compare
	% get the varable class and test
	c1 = class(s1); c2 = class(s2);
	if strcmp(c1,c2);
		% both are the same class
		if isequal(s1,s2)
			% results are equal
			match{end+1} = sprintf('%s and %s match',n1,n2);
			if prt == 3; fprintf('%s\n',match{end}); end
		else
			% same class but not equal
			% calculate error if type is single or double
			% test for function type match if function handle
			switch c1
				case {'single', 'double'}, er = abs(s1 - s2);
				case {'function_handle'},
					s1f = functions(s1); s2f = functions(s2);
					if strcmp(s1f.function,s2f.function)
						% same function with different values - record deviation and exit
						er = 0;
						er1{end+1} = sprintf('%s and %s are both %s but have different values', ...
							n1,n2,char(s1));
						er2{end+1} = er1{end};
						if prt > 1; fprintf('%s\n',er1{end}); end;
						if pse > 1; pause; end
					else
						er = 1;
					end
				otherwise, er = 1;
			end
			% test error - error will be 0 (no error) or 1 (error) for all
			% classes except double and single.  double and single are the 
			% actual error which is tested against the tolerance
			% this was done for cases where structures are run on different 
			% platforms and numerical precision errors are observed
			if er > tol
				% sets do not match
				er1{end+1} = sprintf('%s and %s do not match',n1,n2);
				er2{end+1} = sprintf('%s and %s do not match',n1,n2);
				if prt > 1; fprintf('%s\n',er1{end}); end;
				if pse > 1; pause; end
			else
				% sets are a tolerance match
				match{end+1} = sprintf('%s and %s are tolerance match',n1,n2);
				if prt > 2; fprintf('%s\n',match{end}); end
			end
		end
	else
		% fields are different classes
		er1{end+1} = sprintf('%s is class %s, %s is class %s',n1,c1,n2,c2);
		er2{end+1} = sprintf('%s is class %s, %s is class %s',n1,c1,n2,c2);
		if prt; fprintf('%s\n',er1{end}); end
		if pse; pause; end
	end
end

% transpose outputs
match = match'; er1 = er1'; er2 = er2';

% common error list?
if nargout == 4
	% combine errors into single list
	erc = {};
	% compare errors
	temp1 = ismember(er1,er2); temp2 = ismember(er2,er1);
	% matching errors
	idx1 = find(temp1);
	% unique errors
	inx1 = find(~temp1); inx2 = find(~temp2);
	% record unique errors
	if ~isempty(inx1); erc = [erc; er1(inx1)]; end 
	if ~isempty(inx2); erc = [erc; er2(inx2)]; end 
	% recod matching errors
	if ~isempty(idx1); erc = [erc; er1(idx1)]; end 
end

Contact us