classdef MapN < handle
% A multidimensional map container
%
% A MapN object contains key lists and values. A value is some unit of
% data that you want stored in the MapN object, and a key list is a
% unique reference to that data.
%
% MapN is modelled on containers.Map, but whereas a containers.Map
% object is indexed by a single scalar or string key, a MapN object is
% indexed by an ordered set of such keys, expressed as a
% comma-separated list of arguments.
%
% MapN is a handle class.
%
% Description - basic outline
% ---------------------------
%
% A MapN object is constructed like this:
%
% M = MapN();
%
% Values are stored using M(key1, key2, ...) = value, for example:
%
% M(1, 'a') = 'a string value';
% M(1, 'b') = 287.2;
% M(2) = [1 2 3; 4 5 6];
% M(2, 'x', pi) = {'a' 'cell' 'array'};
%
% and values are retrieved using M(key1, key2, ...), for example
%
% v = M(1, 'b');
% u = M(2);
%
% Key lists and key types
% -----------------------
%
% At least one key must be given in a key list. (The empty key list
% could logically be allowed, were it not that expressions of the form
% M() = v generate syntax errors.)
%
% Apart from this, a key list may be of any length. Key lists of
% different lengths are independent, so M('a') and M('a', 'b') may both
% be given values, and will not interact.
%
% Each key must be of a type supported by containers.Map. This includes
% numerical scalars and strings. A given key list may contain a mixture
% of different key types; however, type consistency between different
% key lists is required.
%
% A simple rule is that there will be no error if the same key type is
% used consistently at each position in the key list. In the example
% above, position 1 uses a double, position 2 uses a string, and
% position 3 uses a double. An attempt to use a string as a key in
% position 1 is inconsistent and throws an exception:
%
% M('a', 'c') = 'v'; % error!
%
% The type of the key at a given position is established by the first
% use of a key at that position. (There is no equivalent to the
% 'KeyType' parameter of containers.Map.)
%
% In fact, the restriction on key types is more liberal than the simple
% rule implies. An exact statement is that if two key lists have the
% same length and are identical in the first N positions, then the two
% keys at position N+1 must have the same type.
%
% Value types
% -----------
%
% By default, values may be of any type and value types may be mixed
% freely in a MapN object.
%
% There may be an efficiency advantage in restricting the value types
% if a large number of strings, or scalar values of the same type, are
% to be stored. The restriction is put in place in the constructor
% thus:
%
% Muni = MapN('uniformvalues', true);
%
% If this is done, stored values must be scalars or strings, and they
% cannot be arbitrarily mixed. The rule is that if two key lists are
% identical apart from the final key in each, then the two
% corresponding values must such that they can both be assigned to a
% containers.Map object which has had 'uniformvalues' set to true. The
% type is determined by the first value stored. (There is no equivalent
% to the 'ValueType' parameter of containers.Map.) Numerical values may
% be automatically converted to the type of previously stored values.
%
% The method uniformValues returns the setting of the 'uniformvalues'
% parameter.
%
% Invalid key lists and default values
% ------------------------------------
%
% It is possible to test whether a key list has been stored using
% isKey. For example
%
% b = isKey(M, 2, 'x');
%
% assigns true to b if M(2, 'x') holds a value, and false otherwise.
% isKey returns false if a key has an invalid type (as for
% containers.Map).
%
% By default, an exception will be thrown if an attempt is made to
% access a MapN object using a key list which has not been stored. For
% example:
%
% M = MapN();
% M(2, 'a') = 123;
% v = M(2, 'a'); % OK
% u = M(2, 'b'); % error!
%
% This may be changed by using the 'default' parameter in the
% constructor:
%
% Mdef = MapN('default', 'zzz');
%
% If a default is set, then the default value, which may have any type,
% is returned for any key list for which no value has been stored:
%
% u = Mdef(2, 'b'); % u gets value 'zzz', no error
%
% The types of keys in the key list must still be consistent with the
% types of keys in the key lists that have been stored.
%
% The default value may be set or changed for an existing MapN object
% using
%
% setDefault(M, 'defaultvalue');
%
% and the default may be removed (so access with invalid keys will
% cause errors) using:
%
% noDefault(M);
%
% The method hasDefault(M) tells whether M has a default, and if it
% has, getDefault(M) returns the current default value.
%
% Updating and removing entries
% -----------------------------
%
% The value for a given key list is updated using the usual assignment;
% the previous value is overwritten.
%
% M(pi, 'x') = 1; % 1 is current value
% M(pi, 'x') = 2; % 2 replaces 1 as the value for this key list
%
% A key list and its corresponding value may be deleted using the
% remove method, for example:
%
% remove(M, pi, 'x');
% u = M(pi, 'x'); % u gets default value, or there's an error
%
% If an attempt is made to remove a key which is not stored, an
% exception is thrown. (This is different to containers.Map, which
% issues a warning. The advantage of an exception is that it can be
% caught.)
%
% Multiple key lists and values
% -----------------------------
%
% For the methods in this section, a key list is represented as a cell
% array of keys. A set of key lists is a cell array of cell arrays.
%
% A set of key lists and a set of values can be supplied to the
% constructor to set up some initial associations. For example
%
% M = MapN({{1 'a'} {1 'b'} {2} {2 'x' pi}}, ...
% {'a string' 287.2 [1 2 3; 4 5 6] {'a' 'cell' 'array'}});
%
% is equivalent to
%
% M = MapN();
% M(1, 'a') = 'a string';
% M(1, 'b') = 287.2;
% M(2) = [1 2 3; 4 5 6];
% M(2, 'x', pi) = {'a' 'cell' 'array'};
%
% Multiple values may be retrieved with the values method. For example:
%
% vals = values(M, {{2}, {1, 'a'}});
%
% assigns a cell array to vals such that vals{1} is [1 2 3; 4 5 6] and
% vals{2} is 'a string'.
%
% If the second argument is omitted, values returns all the values:
%
% vals = values(M); % vals is a 1x4 cell array
%
% Multiple values can be stored with storeValues; for example, the
% first two assignments above are equivalent to:
%
% storeValues(M, {{1 'a'} {1 'b'}}, {'a string' 287.2});
%
% All the key lists stored in an object may be got with:
%
% klists = keys(M);
%
% The order of the result is consistent with the order of values, so
% values(M) is the same as values(M, keys(M)).
%
% The isKey function can be used with a set of key lists, and returns a
% logical array with true in the positions of the valid key lists:
%
% b = isKey(M, {{1 'a'} {'spqr' 17 pi} {2}});
%
% sets b to logical([1 0 1]).
%
% Other methods
% -------------
%
% The number of key lists stored may be obtained with the length
% method. (The Count property available in containers.Map is not
% supported; use length(M) instead.)
%
% The size method returns the number of key lists stored as the first
% dimension, and 1 for all other dimensions.
%
% Vertical concatenation is supported (as for containers.Map). If M1,
% M2 and M3 are MapN objects, and we execute, for example
%
% Mcomb = [M1; M2; M3];
%
% then Mcomb is a single MapN object which stores all the key lists
% from M1, M2 and M3 and their associated values. If a key list is
% duplicated, then the last value associated with it by M1, M2 or M3,
% in that order, is stored in Mcomb. If any of M1, M2 or M3 has a
% default value, then Mcomb has a default whose value is the last one
% encountered in M1, M2 or M3.
%
% Mcomb is constructed with an 'uniformvalues' parameter equal to that
% of M1. If M1 has 'uniformvalues' set to true, and any of the input
% maps contain values that have inconsistent types, an error will
% result.
%
% Horizontal concatenation is not supported.
%
% Copying a MapN object
% ---------------------
%
% Because this is a handle class, Mnew = M does not make a true copy,
% so updates to M will affect Mnew, and vice versa. To make an
% independent copy of M, you can use:
%
% Mnew = copy(M);
%
% Method call syntax
% ------------------
%
% Methods of MapN must be called using the syntax func(MapNobj, ...),
% not MapNobj.func(...).
%
% Methods and properties
% ----------------------
%
% MapN methods:
% MapN - constructor for MapN objects
% subsref - implements value = Mobj(keylist)
% subsasgn - implements M(keylist) = value
% isKey - determine whether given key lists are stored
% keys - get all the stored keys
% values - get stored values
% storeValues - store multiple keys and values
% remove - remove stored values
% hasDefault - determine whether a MapN has a default value
% getDefault - get the default value
% noDefault - unset any default value
% setDefault - set the default value
% uniformValues - get the value of the 'uniformvalues' property
% length - get the number of key lists stored
% size - get the number of key lists stored
% isempty - determine whether any key lists are stored
% vertcat - implement [Mobj1; Mobj2]
% horzcat - error on [Mobj1, Mobj2]
% copy - copy a MapN object
%
% This class has no publicly accessible properties.
%
% See also: containers.Map
% Copyright David Young 2011
properties (Access = private)
% Root map implemented as a cell array rather than a Map
% as indexed by no. of arguments, and m() = v is forbidden by
% syntax restriction
rootMap = {};
% This is a private property accessed via a method, rather than a
% publicly accessible property because of the complexity of
% providing correct obj.name indexing when subsref/subsasgn are
% overriden.
Ct = 0;
doDefault; % boolean, whether to supply a default value
default; % value of default
univals; % 'uniformvalues' parameter for new Maps
end
methods
function M = MapN(varargin)
% constructs MapN objects
%
% M = MapN() constructs an empty MapN object.
%
% M = MapN(KEYLISTS, VALUES) constructs a MapN object which
% contains the specified key lists and values. KEYLISTS is a
% cell array; each element of this is a cell array containing
% the keys making up a key list. VALUES is a cell array
% containing the values. KEYLISTS and VALUES must have the same
% number of elements and VALUES{k} is associated with
% KEYLISTS{k} in M.
%
% M = MapN(..., 'default', d) sets the default value returned
% by M to d.
%
% M = MapN(..., 'uniformvalues', tf), where tf is a logical
% scalar, sets the 'uniformvalues' properties of the underlying
% containers.Map objects. This restricts variation of the types
% of the values stored in M.
%
% Additional values are added to M using
%
% M(key1, key2, ...) = val;
%
% and values are retrieved from M using
%
% val = M(key1, key2, ...);
%
% See the class information for details and examples.
%
% See also: MapN
ip = inputParser;
ip.addOptional('keylist', {}, @iscell);
ip.addOptional('vallist', {}, @iscell);
ip.addParamValue('default', []);
ip.addParamValue('uniformvalues', false, ...
@(x) islogical(x) && isscalar(x));
ip.parse(varargin{:});
M.doDefault = ~ismember('default', ip.UsingDefaults);
M.default = ip.Results.default;
M.univals = ip.Results.uniformvalues;
if ~isempty(ip.Results.keylist)
storeValues(M, ip.Results.keylist, ip.Results.vallist);
end
end
function v = subsref(M, S)
% returns value associated with key list
%
% Implements the syntax
%
% value = MapNobj(key1, key2, ...)
%
% See also: MapN, MapN/subsasgn, MapN/values
if ~isscalar(S) || ~strcmp(S.type, '()')
error('MapN:Subsref:LimitedIndexing', ...
'Only ''()'' indexing is supported by a MapN');
end
try
v = subsrefAction(M, S.subs);
catch me
% default is handled in subsrefError for efficiency
v = subsrefError(M, me, S.subs);
end
end
function M = subsasgn(M, S, v)
% sets value associated with key list
%
% Implements the syntax
%
% MapNobj(key, key2, ...) = value
%
% See also: MapN, MapN/subsref, MapN/storeValues
if ~isscalar(S) || ~strcmp(S.type, '()')
error('MapN:Subsasgn:LimitedIndexing', ...
'Only ''()'' indexing is supported by a MapN');
end
try
M = subsasgnAction(M, S.subs, v);
catch me
subsasgnError(M, me, S.subs);
end
end
function remove(M, varargin)
% deletes keylist/value pairs
%
% remove(MAPNOBJ, KEY1, KEY2, ...) removes the key list and its
% associated value from MAPNOBJ.
%
% remove(MAPNOBJ, KEYLISTS) removes multiple key lists
% and values. KEYLISTS is a cell array of cell arrays, each of
% which contains a list of keys.
%
% See also: MapN
ss = varargin;
nargs = length(ss);
if nargs == 1 && iscell(ss{1})
c = ss{1};
for ii = 1:numel(c)
remove(M, c{ii}{:});
end
else
try
m = M.rootMap{nargs};
sx(m, 1);
if isempty(m)
M.rootMap{nargs} = [];
end
M.Ct = M.Ct - 1;
catch me
removeError(M, me, ss);
end
end
function sx(m, ind)
if ind == nargs
m(ss{ind}); % prompt error if not a valid key
remove(m, ss{ind});
else
mm = m(ss{ind});
sx(mm, ind+1);
if isempty(mm)
remove(m, ss{ind});
end
end
end
end
function b = isKey(M, varargin)
% tests whether keys are stored
%
% B = isKey(MAPNOBJ, KEY1, KEY2, ...) returns a logical scalar
% value indicating whether the keylist is stored in the MapN
% object.
%
% B = (MAPNOBJ, KEYLISTS) tests multiple key lists. KEYLISTS is
% a cell array of cell arrays, each of which contains a list of
% keys. B is a logical array such that B(k) is true if
% KEYLISTS{k} is stored in MAPNOBJ.
%
% See also: MapN
nargs = length(varargin);
ss = varargin;
if nargs == 0
error('MapN:IsKey:emptyKeys', ...
'At least one key must be supplied.');
end
if nargs == 1 && iscell(ss{1})
c = ss{1};
b = false(size(c));
for ii = 1:numel(c)
b(ii) = isKey(M, c{ii}{:});
end
else
if nargs > length(M.rootMap) || isempty(M.rootMap{nargs})
b = false;
else
b = si(M.rootMap{nargs}, 1);
end
end
function b = si(m, ind)
if ind == nargs
b = isKey(m,ss{ind});
elseif isKey(m, ss{ind})
b = si(m(ss{ind}), ind+1);
else
b = false;
end
end
end
function c = keys(M)
% returns all the stored key lists
%
% KEYLISTS = keys(MAPNOBJ) returns a cell array containing all
% the key lists stored in MAPNOBJ. KEYLISTS is a cell array of
% cell arrays, each of which contains a list of keys.
%
% See also: MapN, MapN/values
c = {};
for nargs = 1:length(M.rootMap)
if ~isempty(M.rootMap{nargs})
c = [c sk(M.rootMap{nargs}, nargs-1)]; %#ok<AGROW>
end
end
function c = sk(m, argsleft)
ks = keys(m);
if argsleft
c = {};
for ii = 1:length(ks)
newks = sk(m(ks{ii}), argsleft-1);
newks = cellfun(@(newk) [ks(ii) newk], newks, ...
'UniformOutput', false);
c = [c newks]; %#ok<AGROW>
end
else
c = cellfun(@(k) {k}, ks, 'UniformOutput', false);
end
end
end
function c = values(M, keysin)
% returns stored values
%
% VALS = values(MAPNOBJ) returns all the stored values. VALS is
% a cell array of values, stored in the same order as the
% corresponding key lists in the result of keys(MAPNOBJ).
%
% VALS = values(MAPNOBJ, KEYLISTS) returns the values
% corresponding to the given key lists. KEYLISTS is a cell
% array of cell arrays, each of which contains a list of keys.
%
% See also: MapN, MapN/keys, MapN/subsref
if nargin == 2
c = cell(size(keysin));
for kk = 1:numel(keysin)
try
c{kk} = subsrefAction(M, keysin{kk});
catch me
c{kk} = subsrefError(M, me, keysin{kk});
end
end
else
c = {};
for nargs = 1:length(M.rootMap)
if ~isempty(M.rootMap{nargs})
c = [c sv(M.rootMap{nargs}, nargs-1)]; %#ok<AGROW>
end
end
end
function c = sv(m, argsleft)
if argsleft
c = {};
ks = keys(m);
for ii = 1:length(ks)
c = [c sv(m(ks{ii}), argsleft-1)]; %#ok<AGROW>
end
else
c = values(m);
end
end
end
function storeValues(M, keysin, valsin)
% stores multiple values
%
% storeValues(MAPNOBJ, KEYLISTS, VALUES) stores the given key
% lists and their corresponding values in MAPNOBJ. KEYLISTS is
% a cell array of cell arrays, each of which contains a list of
% keys. VALUES is a cell array of the corresponding values.
%
% See also: MapN, MapN/subsasgn
if ~isequal(numel(keysin), numel(valsin))
error('MapN:StoreValues:argLength', ...
'Keys and Values arguments must be same length');
end
for ii = 1:numel(keysin)
try
M = subsasgnAction(M, keysin{ii}, valsin{ii});
catch me
subsasgnError(M, me, keysin{ii});
end
end
end
function b = hasDefault(M)
% tests for a default value
%
% B = hasDefault(MAPNOBJ) returns true if MAPNOBJ(BADKEYLIST)
% returns a default value when BADKEYLIST is not stored, and
% false if MAPNOBJ(BADKEYLIST) throws an exception.
%
% See also: MapN, MapN/getDefault, MapN/setDefault,
% MapN/noDefault
b = M.doDefault;
end
function d = getDefault(M)
% returns the default value
%
% V = getDefault(MAPNOBJ) returns the default value, provided
% that hasDefault(MAPNOBJ) returns true.
%
% See also: MapN, MapN/hasDefault, MapN/setDefault,
% MapN/noDefault
d = M.default;
end
function setDefault(M, d)
% sets the default value
%
% setDefault(MAPNOBJ, V) sets the default value to D. This also
% sets the value returned by hasDefault(MAPNOBJ) to true.
%
% See also: MapN, MapN/getDefault, MapN/hasDefault,
% MapN/noDefault
M.doDefault = true;
M.default = d;
end
function noDefault(M)
% unsets the default value
%
% noDefault(MAPNOBJ) removes any default value from MAPNOBJ,
% causing it to throw an exception if an attempt is made to
% index it with a key list that has not been stored. This also
% sets the value returned by hasDefault(MAPNOBJ) to false.
%
% See also: MapN, MapN/getDefault, MapN/hasDefault,
% MapN/setDefault
M.doDefault = false;
end
function b = uniformValues(M)
% returns the value of the 'uniformvalues' parameter
%
% B = uniformValues(MAPNOBJ) returns true only if
% 'uniformvalues' was set to true in the constructor when
% MAPNOBJ was created.
%
% See also: MapN
b = M.univals;
end
function l = length(M)
% returns the number of stored keylist/value pairs
%
% L = length(MAPNOBJ) returns the number of keylist/value pairs
% stored in MAPNOBJ.
l = M.Ct;
end
function b = isempty(M)
% tests if any data is stored
%
% B = isempty(M) returns true only if no keylist/value pairs
% are currently stored in MAPNOBJ.
%
% See also: MapN
b = M.Ct == 0;
end
function varargout = size(M, dim)
% returns a version of the size
%
% S = size(MAPNOBJ) returns the number of keylist/value pairs
% stored in MAPNOBJ. Other syntaxes, such as [S1, S2] =
% size(MAPNOBJ) and S = size(MAPNOBJ, DIM) return the number of
% pairs for the first dimension and 1 for the second and
% subsequent dimensions.
%
% Although MapN objects are multidimensional, it is not
% possible to compute a meaningful size for each dimension
% because the keys are not restricted to a small set of
% integers. The size method therefore behaves as if MapN
% objects were 1-dimensional, with a length equal to the number
% of stored associations.
%
% See also: MapN, MapN/length
if nargin == 2
if isequal(dim, 1)
varargout{1} = M.Ct;
else % containers.Map doesn't check dim is valid either!
varargout{1} = 1;
end
else
if nargout < 2
varargout{1} = [M.Ct 1];
else
varargout{1} = M.Ct;
varargout(2:nargout) = num2cell(ones(1, nargout-1));
end
end
end
function Mout = horzcat(varargin) %#ok<STOUT>
% throws an exception
%
% Horizontal concatenation is not supported.
%
% See also: MapN
error('MapN:HorzcatNotSupported', ...
'Horizontal concatenation is not supported by a MapN');
end
function Mout = vertcat(M, varargin)
% combines MapN objects into a single object
%
% MAPNOBJ_OUT = [MAPNOBJ1; MAPNOBJ2; ...] combines MAPNOBJ1
% etc. into a single object which contains all the keys and a
% value for each key.
%
% See also: MapN, MapN/copy
Mout = copy(M);
for ii = 1:length(varargin)
M = varargin{ii};
storeValues(Mout, keys(M), values(M));
if M.doDefault
Mout.doDefault = true;
Mout.default = M.default;
end
end
end
function Mout = copy(M)
% makes a copy of a MapN object
%
% MAPNOBJ_OUT = copy(MAPNOBJ_IN) makes a copy of the input
% object, such that updates to MAPNOBJ_OUT do not affect
% MAPNOBJ_IN, and vice versa.
%
% Because MapN is a handle class, writing MAPNOBJ_OUT =
% MAPNOBJ_IN does not make an independent object, only a new
% handle to it, and updates to one affect the other.
%
% See also: MapN
Mout = MapN(keys(M), values(M), 'uniformvalues', M.univals);
Mout.doDefault = M.doDefault;
Mout.default = M.default;
end
end
methods (Access = private)
function v = subsrefAction(M, ss)
% Implements indexed access by traversing a tree of Maps. Each
% key in a key list is used as an index into a Map whose values
% are the Maps for the next position in the list, except for
% the last Map whose values are to be returned. At the root of
% the tree is a cell array indexed by the number of keys in the
% key list.
%
% The default is handled at a higher level by catching an
% exception. This is inelegant, but is forced on us because
% containers.Map does not provide a default mechanism at all,
% and we would need to call containers.Map.isKey before every
% access to avoid exceptions. Our strategy prioritises
% efficient access for valid key lists.
nargs = length(ss);
v = sr(M.rootMap{nargs}, 1);
function v = sr(m, ind)
if ind == nargs
v = m(ss{ind});
else
v = sr(m(ss{ind}), ind+1);
end
end
end
function M = subsasgnAction(M, ss, v)
% Implements indexed update by building a tree of Maps. New
% Maps are constructed and added to the tree as necessary. See
% subsrefAction.
nargs = length(ss);
rmap = M.rootMap;
if length(rmap) >= nargs && ~isempty(rmap{nargs})
sa(rmap{nargs}, 1);
else
M.rootMap{nargs} = sa([], 1);
end
function m = sa(m, ind)
import containers.Map
key = ss{ind};
if isempty(m) % need to make a new Map
% Need to check that an array has not been passed, in order
% to pass the key to the Map constructor
if ~(isscalar(key) || ischar(key))
error('MapN:Subsasgn:arrayKey', ...
'Key must be a scalar or a string');
end
if ind == nargs
m = Map(key, v, 'uniformvalues', M.univals);
M.Ct = M.Ct + 1;
else
m = Map(key, sa([], ind+1));
end
else % update existing Map
if ind == nargs
hadKey = isKey(m, key);
m(key) = v;
if ~hadKey
M.Ct = M.Ct + 1;
end
else
if isKey(m, key)
sa(m(key), ind+1);
else
m(key) = sa([], ind+1);
end
end
end
end
end
end
methods (Access = private)
function v = subsrefError(M, me, ss)
% handles exceptions on access. Includes default mechanism -
% see comments below and in subsrefAction.
if isempty(ss) % must come first, as identifier same as next case
throwAsCaller(MException('MapN:Subsref:emptyKeys', ...
'At least one key must be supplied.'));
elseif ismember(me.identifier, ...
{'MATLAB:badsubscript', 'MATLAB:Containers:Map:NoKey'})
% Return default if one is set. Unpleasant to do it via
% exception handling, but since containers.Map does not
% allow a default, the alternative is to call isKey before
% every Map access, costing time for normal access.
if M.doDefault
v = M.default;
else
throwAsCaller(MException('MapN:Subsref:NoKey', ...
'The specified key list is not present in this container.'));
end
elseif strcmp(me.identifier, 'MATLAB:Containers:TypeMismatch')
throwAsCaller(MException('MapN:Subsref:KeyTypeMismatch', ...
'A key has a different type to that expected'));
else
rethrow(me);
end
end
function subsasgnError(~, me, ss)
% handles exceptions on updates
if isempty(ss) % should be trapped by syntax checking
throwAsCaller(MException('MapN:Subsasgn:emptyKeys', ...
'At least one key must be supplied.'));
elseif strcmp(me.identifier, 'MATLAB:Containers:TypeMismatch')
% Map has same id but different messages for key and val
% mismatch! To distinguish need to look at the message.
if isempty(strfind(me.message, 'key'))
throwAsCaller(MException('MapN:Subsasgn:ValueTypeMismatch', ...
'A value has a different type to that expected'));
else
throwAsCaller(MException('MapN:Subsasgn:KeyTypeMismatch', ...
'A key has a different type to that expected'));
end
elseif ~isempty(strfind(me.identifier, 'MapN:Subsasgn')) || ...
strcmp(me.identifier, 'MATLAB:Containers:Map:UniformNonScalar')
throwAsCaller(me);
else
rethrow(me);
end
end
function removeError(~, me, ss)
% handles exceptions on key removals
if isempty(ss) % must come first
throwAsCaller(MException('MapN:Remove:emptyKeys', ...
'At least one key must be supplied.'));
elseif ismember(me.identifier, ...
{'MATLAB:badsubscript', 'MATLAB:Containers:Map:NoKey'})
throwAsCaller(MException('MapN:Remove:NoKey', ...
'The specified key list is not present in this container.'));
elseif strcmp(me.identifier, 'MATLAB:Containers:TypeMismatch')
throwAsCaller(MException('MapN:Remove:TypeMismatch', ...
'A key has a different type to that expected at its position'));
else
rethrow(me);
end
end
end
end