1########################################################################
2##
3## Copyright (C) 2007-2021 The Octave Project Developers
4##
5## See the file COPYRIGHT.md in the top-level directory of this
6## distribution or <https://octave.org/copyright/>.
7##
8## This file is part of Octave.
9##
10## Octave is free software: you can redistribute it and/or modify it
11## under the terms of the GNU General Public License as published by
12## the Free Software Foundation, either version 3 of the License, or
13## (at your option) any later version.
14##
15## Octave is distributed in the hope that it will be useful, but
16## WITHOUT ANY WARRANTY; without even the implied warranty of
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18## GNU General Public License for more details.
19##
20## You should have received a copy of the GNU General Public License
21## along with Octave; see the file COPYING.  If not, see
22## <https://www.gnu.org/licenses/>.
23##
24########################################################################
25
26## -*- texinfo -*-
27## @deftypefn  {} {} structfun (@var{func}, @var{S})
28## @deftypefnx {} {[@var{A}, @dots{}] =} structfun (@dots{})
29## @deftypefnx {} {} structfun (@dots{}, "ErrorHandler", @var{errfunc})
30## @deftypefnx {} {} structfun (@dots{}, "UniformOutput", @var{val})
31##
32## Evaluate the function named @var{name} on the fields of the structure
33## @var{S}.  The fields of @var{S} are passed to the function @var{func}
34## individually.
35##
36## @code{structfun} accepts an arbitrary function @var{func} in the form of an
37## inline function, function handle, or the name of a function (in a character
38## string).  In the case of a character string argument, the function must
39## accept a single argument named @var{x}, and it must return a string value.
40## If the function returns more than one argument, they are returned as
41## separate output variables.
42##
43## If the parameter @qcode{"UniformOutput"} is set to true (the default), then
44## the function must return a single element which will be concatenated into
45## the return value.  If @qcode{"UniformOutput"} is false, the outputs are
46## placed into a structure with the same fieldnames as the input structure.
47##
48## @example
49## @group
50## s.name1 = "John Smith";
51## s.name2 = "Jill Jones";
52## structfun (@@(x) regexp (x, '(\w+)$', "matches")@{1@}, s,
53##            "UniformOutput", false)
54##   @result{} scalar structure containing the fields:
55##        name1 = Smith
56##        name2 = Jones
57## @end group
58## @end example
59##
60## Given the parameter @qcode{"ErrorHandler"}, @var{errfunc} defines a function
61## to call in case @var{func} generates an error.  The form of the function is
62##
63## @example
64## function [@dots{}] = errfunc (@var{se}, @dots{})
65## @end example
66##
67## @noindent
68## where there is an additional input argument to @var{errfunc} relative to
69## @var{func}, given by @nospell{@var{se}}.  This is a structure with the
70## elements @qcode{"identifier"}, @qcode{"message"} and @qcode{"index"},
71## giving respectively the error identifier, the error message, and the index
72## into the input arguments of the element that caused the error.  For an
73## example on how to use an error handler, @pxref{XREFcellfun,,cellfun}.
74##
75## @seealso{cellfun, arrayfun, spfun}
76## @end deftypefn
77
78function varargout = structfun (func, S, varargin)
79
80  if (nargin < 2)
81    print_usage ();
82  endif
83
84  nargs = length (varargin);
85
86  recognized_opts = {"UniformOutput", "ErrorHandler"};
87  uo_str = recognized_opts{1};
88
89  uniform_output = true;
90
91  while (nargs >= 2)
92    opt_match = strcmpi (varargin{nargs-1}, recognized_opts);
93    if (opt_match(1))
94      uniform_output = varargin{nargs};
95    endif
96    if (any (opt_match))
97      nargs -= 2;
98    else
99      break;
100    endif
101  endwhile
102
103  if (nargs > 0)
104    error ("structfun: invalid options");
105  endif
106
107  varargout = cell (max ([nargout, 1]), 1);
108  [varargout{:}] = cellfun (func, struct2cell (S), varargin{:});
109
110  if (! uniform_output)
111    varargout = cellfun ("cell2struct", varargout, {fieldnames(S)}, {1}, ...
112                         uo_str, false);
113  endif
114
115endfunction
116
117
118%!test
119%! s.name1 = "John Smith";
120%! s.name2 = "Jill Jones";
121%! l.name1 = "Smith";
122%! l.name2 = "Jones";
123%! o = structfun (@(x) regexp (x, '(\w+)$', "matches"){1}, s,
124%!                "UniformOutput", false);
125%! assert (o, l);
126
127%!function [a, b] = __twoouts (x)
128%!  a = x + x;
129%!  b = x * x;
130%!endfunction
131
132%!test
133%! s = struct ("a", {1, 2, 3}, "b", {4, 5, 6});
134%! c(1:2, 1, 1) = [2; 8];
135%! c(1:2, 1, 2) = [4; 10];
136%! c(1:2, 1, 3) = [6; 12];
137%! d(1:2, 1, 1) = [1; 16];
138%! d(1:2, 1, 2) = [4; 25];
139%! d(1:2, 1, 3) = [9; 36];
140%! [aa, bb] = structfun (@__twoouts, s);
141%! assert (aa, c);
142%! assert (bb, d);
143
144%!test
145%! s = struct ("a", {1, 2, 3}, "b", {4, 5, 6});
146%! c = struct ("a", {2, 4, 6}, "b", {8, 10, 12});
147%! d = struct ("a", {1, 4, 9}, "b", {16, 25, 36});
148%! [aa, bb] = structfun (@__twoouts, s, "UniformOutput", false);
149%! assert (aa, c);
150%! assert (bb, d);
151