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 properties (private) numericKeys = false; 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)) this.numericKeys = true; 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, []); if (numel (this.keySet) != numel (this.valueSet)) error ("The number of keys and values must be the same."); endif c = unique (cellfun (@class, this.keySet, "UniformOutput", false)); if (numel (c) == 1) this.KeyType = char (c); if (! strcmp (this.KeyType, "char")) this.numericKeys = true; n = unique (cellfun (@numel, this.keySet)); if (! (numel (n) == 1 && n == 1)) error ("Unsupported key specified."); endif endif else error ("The specified keys must all be of the same data type."); endif this = sortKeys (this); c = unique( cellfun (@class, this.valueSet, "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 if (numel (s) > 1) sref = subsref (sref, s(2:end)); endif endfunction function this = subsasgn (this, s, val) if (numel (s) > 1) error ("Only one level of indexing is supported by a containers.Map."); endif 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 (val) && !strcmp (this.ValueType, "char")) error ("Specified value type does not match the type expected for this container."); endif val = feval (this.ValueType, val); endif i = findKey (this, key); if (any (i)) this.valueSet{i} = val; else this = insertKey (this, key, val); end case '{}' error ("Only '()' indexing is supported for assigning values into a containers.Map."); endswitch endfunction function newobj = horzcat (varargin) error ("Horizontal concatenation is not supported by a containers.Map."); endfunction function newobj = vertcat (varargin) % When concatenating maps, the data type of all values must be consistent with the ValueType % of the leftmost map. keySet = cell (1, 0); for i=1:numel (varargin) keySet = [keySet varargin{i}.keySet]; endfor valueSet = cell (1, 0); for i=1:numel (varargin) valueSet = [valueSet varargin{i}.valueSet]; endfor newobj = containers.Map (keySet, valueSet); 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 function this = insertKey (this, key, val) this.keySet{end+1} = key; this.valueSet{end+1} = val; this = sortKeys (this); endfunction function this = sortKeys (this) if (this.numericKeys) [~, I] = unique(cell2mat(this.keySet)); this.keySet = this.keySet(I); this.valueSet = this.valueSet(I); else [this.keySet, I] = unique(this.keySet); this.valueSet = this.valueSet(I); endif endfunction endmethods endclassdef