classdef Map < handle properties (getAccess = public, SetAccess = private) KeyType = "char"; ValueType = "any"; endproperties properties (Dependent, SetAccess = protected) Count endproperties properties (Access = private) keySet = cell (1, 0); valueSet = cell (1, 0); endproperties methods (Access = public) function this = Map (varargin) if (nargin == 0) elseif ((nargin == 2) || ((nargin == 4) && strcmpi(varargin{3}, "UniformValues"))) key = varargin{1}; value = varargin{2}; if (~iscell (key)) if (isnumeric (key)) key = num2cell(key); else key = { key }; endif endif if (~iscell (value)) if (isnumeric (value)) value = num2cell(value); else value = { value }; endif endif this.keySet = reshape (key, 1, []); this.valueSet = reshape (value, 1, []); c = unique( cellfun (@class, key, "UniformOutput", false)); if (numel (c) == 1) this.KeyType = char (c); else this.KeyType = "any"; endif c = unique( cellfun (@class, value, "UniformOutput", false)); if (numel (c) == 1) this.ValueType = char (c); else this.ValueType = "any"; endif if (nargin == 4) if (varargin{4}) if (strcmp (this.ValueType, "any")) error ("When 'UniformValues' is true, all values should belong to the same class."); endif else this.ValueType = "any"; endif endif elseif (nargin == 4) for i=[1 3] switch lower (varargin{i}) case "keytype" this.KeyType = varargin{i+1}; case "valuetype" this.ValueType = varargin{i+1}; otherwise error ("Missing parameter name 'KeyType' or 'ValueType'."); endswitch endfor if (~ismember (this.KeyType, {"char","double", "single", "int32", "uint32", "int64", "uint64"})) error ("Unsupported KeyType."); endif if (~ismember (this.ValueType, {"char","double", "single", "int32", "uint32", "int64", "uint64", "logical", "any"})) error ("Unsupported ValueType."); endif else error("Incorrect number of inputs specified."); endif endfunction function keySet = keys (this) keySet = this.keySet; endfunction function valueSet = values (this, keySet) if (nargin == 1) valueSet = this.valueSet; else if (~iscell (keySet)) if (isnumeric (keySet)) keySet = num2cell(keySet); else keySet = { keySet }; endif endif valueSet = cell (1, numel (keySet)); for i=1:numel (valueSet) valueSet{i} = subsref (this, substruct ('()', keySet(i))); endfor endif endfunction function tf = isKey (this, keySet) if (~iscell (keySet)) if (isnumeric (keySet)) keySet = num2cell(keySet); else keySet = { keySet }; endif endif tf = false(1, numel (keySet)); for i=1:numel(tf) tf(i) = any (findKey (this, keySet{i})); endfor endfunction function remove (this, keySet) if (~iscell (keySet)) if (isnumeric (keySet)) keySet = num2cell(keySet); else keySet = { keySet }; endif endif for i=1:numel(keySet) j = findKey (this, keySet{i}); if (any (j)) this.keySet(j) = []; this.valueSet(j) = []; else warning ("The specified key is not present in this container."); endif endfor endfunction function varargout = size (this, n) if (nargin == 1) if (nargout <= 1) varargout = { [this.Count 1] }; else varargout{1} = this.Count; [varargout{2:nargout}] = deal (1); endif else if (n == 1) varargout = { this.Count }; else varargout = { 1 }; endif endif endfunction function mapLength = length (this) mapLength = this.Count; endfunction function tf = isempty (this, keySet) tf = this.Count == 0; endfunction function count = get.Count (this) count = uint64 (numel (this.keySet)); endfunction function sref = subsref (this, s) switch s(1).type case "." switch s(1).subs case "keys" sref = keys (this); case "values" sref = values (this) case "size" sref = size (this); case "length" sref = length (this); case "isempty" sref = isempty (this); case "Count" sref = this.Count; case "KeyType" sref = this.KeyType; case "ValueType" sref = this.ValueType; otherwise error (["The name '%s' is not an accessible property for " ... "an instance of class '%s'."], s(1).subs, class (this)); endswitch case "()" i = findKey (this, s(1).subs{1}); if (any (i)) sref = this.valueSet{i}; else error ("The specified key is not present in this container."); endif otherwise error ("Only '()' indexing is supported by a containers.Map."); endswitch endfunction function this = subsasgn (this, s, v) switch s(1).type case "." error ("Properties of an instance of class '%s' are read-only.", class (this)); case "()" key = s(1).subs{1}; if (~isa (key, this.KeyType)) error ("Specified key type does not match the type expected for this container."); endif if (~strcmp (this.ValueType, "any")) if (ischar (v) && ~strcmp (this.ValueType, "char")) error ("Specified value type does not match the type expected for this container."); endif v = feval (this.ValueType, v); endif i = findKey (this, key); if (any (i)) this.valueSet{i} = v; else this.keySet{end+1} = key; this.valueSet{end+1} = v; end case '{}' error ("Only '()' indexing is supported for assigning values into a containers.Map."); endswitch endfunction function display (this) printf ("containers.Map object with properties:\n\n"); printf ([" Count : %d\n" ... " KeyType : %s\n" ... " ValueType : %s\n\n"], this.Count, this.KeyType, this.ValueType); endfunction endmethods methods (Access = private) function i = findKey(this, key) i = cellfun (@(x) isequal (x, key), this.keySet); endfunction endmethods endclassdef