dynare/matlab/lmmcp/catstruct.m

168 lines
5.7 KiB
Matlab

function A = catstruct(varargin)
% CATSTRUCT Concatenate or merge structures with different fieldnames
% X = CATSTRUCT(S1,S2,S3,...) merges the structures S1, S2, S3 ...
% into one new structure X. X contains all fields present in the various
% structures. An example:
%
% A.name = 'Me' ;
% B.income = 99999 ;
% X = catstruct(A,B)
% % -> X.name = 'Me' ;
% % X.income = 99999 ;
%
% If a fieldname is not unique among structures (i.e., a fieldname is
% present in more than one structure), only the value from the last
% structure with this field is used. In this case, the fields are
% alphabetically sorted. A warning is issued as well. An axample:
%
% S1.name = 'Me' ;
% S2.age = 20 ; S3.age = 30 ; S4.age = 40 ;
% S5.honest = false ;
% Y = catstruct(S1,S2,S3,S4,S5) % use value from S4
%
% The inputs can be array of structures. All structures should have the
% same size. An example:
%
% C(1).bb = 1 ; C(2).bb = 2 ;
% D(1).aa = 3 ; D(2).aa = 4 ;
% CD = catstruct(C,D) % CD is a 1x2 structure array with fields bb and aa
%
% The last input can be the string 'sorted'. In this case,
% CATSTRUCT(S1,S2, ..., 'sorted') will sort the fieldnames alphabetically.
% To sort the fieldnames of a structure A, you could use
% CATSTRUCT(A,'sorted') but I recommend ORDERFIELDS for doing that.
%
% When there is nothing to concatenate, the result will be an empty
% struct (0x0 struct array with no fields).
%
% NOTE: To concatenate similar arrays of structs, you can use simple
% concatenation:
% A = dir('*.mat') ; B = dir('*.m') ; C = [A ; B] ;
%
% See also CAT, STRUCT, FIELDNAMES, STRUCT2CELL, ORDERFIELDS
% for Matlab R13 and up
% version 3.0 (mar 2013)
% Originally downloaded from MATLAB central:
% http://www.mathworks.com/matlabcentral/fileexchange/7842-catstruct
% Copyright (C) 2005 Jos van der Geest <jos@jasen.nl>
% Copyright (C) 2013 Christophe Gouel
% Copyright (C) 2016-2021 Dynare Team
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in
% the documentation and/or other materials provided with the distribution
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
% History
% Created in 2005
% Revisions
% 2.0 (sep 2007) removed bug when dealing with fields containing cell
% arrays (Thanks to Rene Willemink)
% 2.1 (sep 2008) added warning and error identifiers
% 2.2 (oct 2008) fixed error when dealing with empty structs (Thanks to
% Lars Barring)
% 3.0 (mar 2013) fixed problem when the inputs were array of structures
% (thanks to Tor Inge Birkenes for pointing this out).
% Rephrased the help section as well.
narginchk(1, Inf);
N = nargin ;
if ~isstruct(varargin{end})
if isequal(varargin{end},'sorted')
sorted = 1 ;
N = N-1 ;
if N<1
error('catstruct: wrong number of input arguments') ;
end
else
error('catstruct:InvalidArgument','Last argument should be a structure, or the string "sorted".') ;
end
else
sorted = 0 ;
end
sz0 = [] ; % used to check that all inputs have the same size
% used to check for a few trivial cases
NonEmptyInputs = false(N,1) ;
NonEmptyInputsN = 0 ;
% used to collect the fieldnames and the inputs
FN = cell(N,1) ;
VAL = cell(N,1) ;
% parse the inputs
for ii=1:N
X = varargin{ii} ;
if ~isstruct(X)
error('catstruct:InvalidArgument',['Argument #' num2str(ii) ' is not a structure.']) ;
end
if ~isempty(X)
% empty structs are ignored
if ii > 1 && ~isempty(sz0)
if ~isequal(size(X), sz0)
error('catstruct:UnequalSizes','All structures should have the same size.') ;
end
else
sz0 = size(X) ;
end
NonEmptyInputsN = NonEmptyInputsN + 1 ;
NonEmptyInputs(ii) = true ;
FN{ii} = fieldnames(X) ;
VAL{ii} = struct2cell(X) ;
end
end
if NonEmptyInputsN == 0
% all structures were empty
A = struct([]) ;
elseif NonEmptyInputsN == 1
% there was only one non-empty structure
A = varargin{NonEmptyInputs} ;
if sorted
A = orderfields(A) ;
end
else
% there is actually something to concatenate
FN = cat(1,FN{:}) ;
VAL = cat(1,VAL{:}) ;
FN = squeeze(FN) ;
VAL = squeeze(VAL) ;
MatlabVersion = version;
[UFN,ind] = unique(FN,'legacy') ;
if numel(UFN) ~= numel(FN)
warning('catstruct:DuplicatesFound','Fieldnames are not unique between structures.') ;
sorted = 1 ;
end
if sorted
VAL = VAL(ind,:) ;
FN = FN(ind,:) ;
end
A = cell2struct(VAL, FN);
A = reshape(A, sz0) ; % reshape into original format
end