-
Notifications
You must be signed in to change notification settings - Fork 2
/
mars_struct.m
261 lines (238 loc) · 7.29 KB
/
mars_struct.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
function varargout = mars_struct(action, varargin)
% multifunction function for manipulating structures
%
% To help the exposition a bit:
% 'fill' in a name, means that values empty or missing
% in one structure are fetched from another
%
% 'merge' means simply that missing fields are added, with
% values, from a second structure (but not filled if empty)
%
% Each function needs to deal with the case of empty arguments
%
% FORMAT c = mars_struct('fillafromb', a, b, fieldns, flags)
% fills structure fields empty or missing in a from those present in b
% a, b are structures
% fieldns (optional) is cell array of field names to fill from in b
% c is returned structure
% Is recursive, will fill struct fields from struct fields
% flags may contain 'f', which Force fills a from b (all non empty
% fields in b overwrite those in a)
% flags may also contain 'r', which Restricts fields to write from b, to
% those that are already present in a
%
% FORMAT [c, d] = mars_struct('split', a, b)
% split structure a into two, according to fields in b
% so that c becomes a structure which contains the fields
% in a, that are also present in b, and d contains the fields
% in a that are not present in b. b can be a structure
% or a cell array of fieldnames
%
% FORMAT [d] = mars_struct('strip', a, b)
% strips all fields present in b from those in a,
% returning denuded structure as d. b can be a structure
% or a cell array of fieldnames. 'strip' is just 'split'
% but returning only the second argument
%
% FORMAT c = mars_struct('merge', a, b)
% merges structure a and b (fields present in b added to a)
%
% FORMAT [c,d] = mars_struct('ffillsplit', a, b)
% force fill, followed by split
% All fields from a, that are also present in b, and not empty in b,
% are replaced with the values in b; the result is returned as c
% Any fields present in a, but not present in b, are returned in d
%
% FORMAT c = mars_struct('ffillmerge', a, b)
% force fill followed by merge
% performs 'ffillsplit' on a and b, then merges a and b
% All fields present in a or b are returned in c, but
% any fields present in both, now have the value from b
%
% FORMAT [c d] = mars_struct('splitmerge', a, b)
% performs 'split' on a and b, creating c and d
% then merges c with b.
% d contains fields in a that were not present in b
% c contains fields present in both, or just in b
%
% FORMAT z = mars_struct('isthere', a, b [, c [, d ...])
% returns 1 if field named in b is present in a
% and field value is not empty.
% The call is recursive if more than two arguments are passed
% Thus with structure s = struct('one', struct('two', 3))
% mars_struct('isthere', s, 'one', 'two') returns 1
%
% FORMAT z = mars_struct('getifthere', a, b [, c [, d ...])
% returns value of field named in b from a or [] if absent
% Call is recursive, like 'isthere' above.
%
% FORMAT strs = mars_struct('celldisp', a)
% returns output like disp(a) as a cell array
% Useful for printing text description of structure
%
% $Id: mars_struct.m 253 2005-10-13 15:31:34Z guillaume $
if nargin < 1
error('Action needed');
end
if nargin < 2
error('Must specify structure')
end
if nargin < 3
varargin = {varargin{:} []};
end
[a b] = deal(varargin{1:2});
switch lower(action)
case 'fillafromb'
% Return for empty passed args
if isempty(a), varargout = {b}; return, end
if isempty(b), varargout = {a}; return, end
if nargin < 4, fieldns = []; else fieldns = varargin{3}; end
if isempty(fieldns)
if ~isstruct(b), error('Need struct as 2nd argument'); end
fieldns = fieldnames(b);
end
if nargin < 5, flags = ''; else flags = varargin{4}; end
if isempty(flags), flags = ' ';end
if ischar(fieldns), fieldns=cellstr(fieldns);end
af = fieldnames(a)';
bf = fieldns';
% classify fields 0 = a~b, 1 = a&b, 2=b~a
cf = af;
ftype = ismember(af, bf);
if ~any(flags == 'r')
b_not_a = find(~ismember(bf, af));
cf = {cf{:} bf{b_not_a}};
ftype = [ftype ones(1, length(b_not_a))*2];
end
% cope with arrays of structures
alen = prod(size(a));
blen = prod(size(b));
maxlen = max(alen, blen);
for si=1:maxlen
ctmp = [];
for i=1:length(cf)
fn = cf{i};
switch ftype(i)
case 0 % a~b
fval = getfield(a(si), fn);
case 1 % shared field
bfc = getfield(b(si), fn);
if isempty(getfield(a(si), fn)) | ... % a field is empty
(any(flags == 'f' & ~isempty(bfc)))% or force fill
fval = bfc;
else % field not empty, could be struct -> recurse
fval = getfield(a(si),fn);
if isstruct(fval) & isstruct(bfc)
fval = mars_struct('fillafromb',fval,bfc);
end
end
case 2 % b~a
fval = getfield(b(si), fn);
case 3 % no field information, see below
fval = [];
end
if isempty(ctmp)
ctmp = struct(fn, fval);
else
ctmp = setfield(ctmp, fn, fval);
end
end
c(si) = ctmp;
if si == blen % reached end of bs, rest of b~a fields are empty
ftype = (ftype == 2) * 3;
elseif si == alen % end of a's rest of a~b fields are empty
ftype = (ftype == 0) * 2 + 1;
end
end
varargout = {c};
case 'split'
if isempty(a), varargout = {a,a}; return, end
if isempty(b), varargout = {b,a}; return, end
d = a;
c = [];
if ischar(b), b = {b};end
if isstruct(b), b = fieldnames(b);end
for bf = b(:)'
if isfield(a, bf{1})
c = setfield(c, bf{1}, getfield(a, bf{1}));
d = rmfield(d, bf{1});
end
end
varargout = {c, d};
case 'strip'
[c d] = mars_struct('split', a, b);
varargout = {d};
case 'merge'
if isempty(a), varargout = {b}; return, end
if isempty(b), varargout = {a}; return, end
c = a;
for bf = fieldnames(b)';
if ~isfield(a, bf{1})
c = setfield(c, bf{1}, getfield(b, bf{1}));
end
end
varargout = {c};
case 'ffillsplit'
if isempty(a) | isempty(b)
% Nothing in common, return unchanged
varargout = {a, b}; return
end
c = a; d = b;
cf = fieldnames(c);
for i=1:length(cf)
if isfield(d, cf{i})
dfc = getfield(d,cf{i});
if ~isempty(dfc)
c = setfield(c, cf{i}, dfc);
end
d = rmfield(d, cf{i});
end
end
varargout = {c,d};
case 'ffillmerge'
[a b] = mars_struct('ffillsplit', a, b);
varargout = {mars_struct('merge', a, b)};
case 'splitmerge'
[a c] = mars_struct('split', a, b);
varargout = {mars_struct('merge', a, b) c};
case 'isthere'
if isempty(a), varargout = {0}; return, end
c = mars_struct('getifthere', varargin{:});
varargout = {~isempty(c)};
case 'getifthere'
if isempty(a), varargout = {[]}; return, end
if isempty(b), varargout = {[]}; return, end
for v = 2:nargin-1
b = varargin{v};
if ~isfield(a, b)
varargout = {[]};
return
end
a = getfield(a, b);
end
varargout = {a};
case 'celldisp'
if isempty(a), varargout = {{}}; return, end
af = fieldnames(a);
c = {};
pad_len = size(char(af), 2) + 4;
pad_str = ['%' num2str(pad_len) 's: %s'];
for f = 1:length(af)
d = getfield(a, af{f});
cls = class(d);
sz = size(d);
szstr = sprintf('%dx', size(d));
szstr(end) = [];
switch cls
case 'char'
case {'double', 'float'}
d = ['[' num2str(d) ']'];
otherwise
d = sprintf('[%s %s]', szstr, cls);
end
c{f} = sprintf(pad_str, af{f}, d);
end
varargout = {c};
otherwise
error(['Suspicious action was ' action]);
end % switch