1function A = catstruct(varargin)
2% CATSTRUCT   Concatenate or merge structures with different fieldnames
3%   X = CATSTRUCT(S1,S2,S3,...) merges the structures S1, S2, S3 ...
4%   into one new structure X. X contains all fields present in the various
5%   structures. An example:
6%
7%     A.name = 'Me' ;
8%     B.income = 99999 ;
9%     X = catstruct(A,B)
10%     % -> X.name = 'Me' ;
11%     %    X.income = 99999 ;
12%
13%   If a fieldname is not unique among structures (i.e., a fieldname is
14%   present in more than one structure), only the value from the last
15%   structure with this field is used. In this case, the fields are
16%   alphabetically sorted. A warning is issued as well. An axample:
17%
18%     S1.name = 'Me' ;
19%     S2.age  = 20 ; S3.age  = 30 ; S4.age  = 40 ;
20%     S5.honest = false ;
21%     Y = catstruct(S1,S2,S3,S4,S5) % use value from S4
22%
23%   The inputs can be array of structures. All structures should have the
24%   same size. An example:
25%
26%     C(1).bb = 1 ; C(2).bb = 2 ;
27%     D(1).aa = 3 ; D(2).aa = 4 ;
28%     CD = catstruct(C,D) % CD is a 1x2 structure array with fields bb and aa
29%
30%   The last input can be the string 'sorted'. In this case,
31%   CATSTRUCT(S1,S2, ..., 'sorted') will sort the fieldnames alphabetically.
32%   To sort the fieldnames of a structure A, you could use
33%   CATSTRUCT(A,'sorted') but I recommend ORDERFIELDS for doing that.
34%
35%   When there is nothing to concatenate, the result will be an empty
36%   struct (0x0 struct array with no fields).
37%
38%   NOTE: To concatenate similar arrays of structs, you can use simple
39%   concatenation:
40%     A = dir('*.mat') ; B = dir('*.m') ; C = [A ; B] ;
41%
42%   See also CAT, STRUCT, FIELDNAMES, STRUCT2CELL, ORDERFIELDS
43
44% for Matlab R13 and up
45% version 3.0 (mar 2013)
46% Originally downloaded from MATLAB central:
47% http://www.mathworks.com/matlabcentral/fileexchange/7842-catstruct
48
49% Copyright (C) 2005 Jos van der Geest <jos@jasen.nl>
50% Copyright (C) 2013 Christophe Gouel
51% Copyright (C) 2016-2020 Dynare Team
52%
53% Redistribution and use in source and binary forms, with or without
54% modification, are permitted provided that the following conditions are
55% met:
56%
57%     * Redistributions of source code must retain the above copyright
58%       notice, this list of conditions and the following disclaimer.
59%     * Redistributions in binary form must reproduce the above copyright
60%       notice, this list of conditions and the following disclaimer in
61%       the documentation and/or other materials provided with the distribution
62%
63% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
64% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
65% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
66% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
67% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
68% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
69% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
70% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
71% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
72% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
73% POSSIBILITY OF SUCH DAMAGE.
74
75% History
76% Created in 2005
77% Revisions
78%   2.0 (sep 2007) removed bug when dealing with fields containing cell
79%                  arrays (Thanks to Rene Willemink)
80%   2.1 (sep 2008) added warning and error identifiers
81%   2.2 (oct 2008) fixed error when dealing with empty structs (Thanks to
82%                  Lars Barring)
83%   3.0 (mar 2013) fixed problem when the inputs were array of structures
84%                  (thanks to Tor Inge Birkenes for pointing this out).
85%                  Rephrased the help section as well.
86
87narginchk(1, Inf);
88N = nargin ;
89
90if ~isstruct(varargin{end})
91    if isequal(varargin{end},'sorted')
92        sorted = 1 ;
93        N = N-1 ;
94        if N<1
95            error('catstruct: wrong number of input arguments') ;
96        end
97    else
98        error('catstruct:InvalidArgument','Last argument should be a structure, or the string "sorted".') ;
99    end
100else
101    sorted = 0 ;
102end
103
104sz0 = [] ; % used to check that all inputs have the same size
105
106% used to check for a few trivial cases
107NonEmptyInputs = false(N,1) ;
108NonEmptyInputsN = 0 ;
109
110% used to collect the fieldnames and the inputs
111FN = cell(N,1) ;
112VAL = cell(N,1) ;
113
114% parse the inputs
115for ii=1:N
116    X = varargin{ii} ;
117    if ~isstruct(X)
118        error('catstruct:InvalidArgument',['Argument #' num2str(ii) ' is not a structure.']) ;
119    end
120
121    if ~isempty(X)
122        % empty structs are ignored
123        if ii > 1 && ~isempty(sz0)
124            if ~isequal(size(X), sz0)
125                error('catstruct:UnequalSizes','All structures should have the same size.') ;
126            end
127        else
128            sz0 = size(X) ;
129        end
130        NonEmptyInputsN = NonEmptyInputsN + 1 ;
131        NonEmptyInputs(ii) = true ;
132        FN{ii} = fieldnames(X) ;
133        VAL{ii} = struct2cell(X) ;
134    end
135end
136
137if NonEmptyInputsN == 0
138    % all structures were empty
139    A = struct([]) ;
140elseif NonEmptyInputsN == 1
141    % there was only one non-empty structure
142    A = varargin{NonEmptyInputs} ;
143    if sorted
144        A = orderfields(A) ;
145    end
146else
147    % there is actually something to concatenate
148    FN = cat(1,FN{:}) ;
149    VAL = cat(1,VAL{:}) ;
150    FN = squeeze(FN) ;
151    VAL = squeeze(VAL) ;
152    MatlabVersion = version;
153    if (isoctave && octave_ver_less_than('6')) || (~isoctave && str2double(MatlabVersion(end-5:end-2))<2013) % Equivalent to, but faster than if verLessThan('matlab','8.1')
154        [UFN,ind] = unique(FN) ;
155    else
156        [UFN,ind] = unique(FN,'legacy') ;
157    end
158
159    if numel(UFN) ~= numel(FN)
160        warning('catstruct:DuplicatesFound','Fieldnames are not unique between structures.') ;
161        sorted = 1 ;
162    end
163
164    if sorted
165        VAL = VAL(ind,:) ;
166        FN = FN(ind,:) ;
167    end
168
169    A = cell2struct(VAL, FN);
170    A = reshape(A, sz0) ; % reshape into original format
171end
172