1classdef IndexedTable < handle
2 % INDEXEDTABLE Generic enhanced table wrapper with flexible
object-based filtering
4 % This
class wraps any MATLAB table to enable filtering using Station, Node,
5 % JobClass, and/or Chain objects,
while maintaining full backward compatibility
6 % with standard table operations.
8 % Supports all NetworkSolver result tables:
9 % - AvgTable (Station + JobClass)
10 % - AvgNodeTable (Node + JobClass)
11 % - AvgChainTable (Station + Chain)
12 % - AvgNodeChainTable (Node + Chain)
13 % - AvgSysTable (Chain only)
14 % - Any custom table with Station/Node/JobClass/Chain columns
17 % % Create from any MATLAB table
18 % t_native = solver.avgTable();
19 % t = IndexedTable(t_native);
21 % % Standard table operations (works as normal)
22 % rows = t.data(1:5, :)
23 % value = t.data.QLen(1)
25 % % Object-based filtering (four equivalent syntaxes)
26 % rows = t(station_obj) % Direct indexing
27 % rows = t.filterBy(station_obj) % filterBy method
28 % rows = t.get(station_obj) % get alias (shorthand)
29 % rows = t.tget(station_obj) % tget alias
31 % % Filter by class object
33 % rows = t.filterBy(class_obj)
34 % rows = t.get(class_obj)
35 % rows = t.tget(class_obj)
37 % % Filter by both station/node and class/chain (any order)
38 % rows = t(station_obj, class_obj)
39 % rows = t.filterBy(station_obj, class_obj)
40 % rows = t.get(station_obj, class_obj)
41 % rows = t.tget(station_obj, class_obj)
44 % solver = SolverJMT(model);
45 % solver.runAnalyzer();
46 % avgTable = IndexedTable(solver.avgTable());
47 % metrics = avgTable(queue,
jobclass);
49 % Example (AvgNodeTable):
50 % nodeTable = IndexedTable(solver.avgNodeTable());
51 % metrics = nodeTable(sink,
jobclass);
53 % Example (AvgChainTable):
54 % chainTable = IndexedTable(solver.avgChainTable());
55 % metrics = chainTable(queue, chain);
57 % Example (AvgSysTable):
58 % sysTable = IndexedTable(solver.avgSysTable());
59 % metrics = sysTable(chain);
62 data % Underlying MATLAB table
66 function obj = IndexedTable(table_data)
67 % INDEXEDTABLE(table_data) Create wrapper around any MATLAB table
70 % table_data: A MATLAB table
object with
object-filterable columns
72 if ~istable(table_data)
73 error(
'IndexedTable requires a MATLAB table as input');
75 obj.data = table_data;
78 function varargout = subsref(obj, s)
79 % SUBSREF Support both standard indexing and
object-based filtering
81 % Intelligently detects
object types and filters appropriate columns
90 % Check
if this is object-based indexing
93 % Check
if first argument
is a Station/Node/JobClass/Chain
object
94 isObjArg = isa(indices{1},
'Station') || isa(indices{1},
'Node') || ...
95 isa(indices{1},
'JobClass') || isa(indices{1},
'Chain');
98 % Object-based indexing - use filterBy
99 if length(indices) == 1
100 result = obj.filterBy(indices{1});
101 elseif length(indices) == 2
102 result = obj.filterBy(indices{1}, indices{2});
104 error(
'Too many arguments for object-based indexing');
107 % Standard numeric/logical indexing - pass to underlying table
108 varargout{1} = subsref(obj.data, s);
112 % Handle chained indexing
114 varargout{1} = subsref(result, s(2:end));
118 varargout{1} = result;
121 % Handle method calls and
property access
122 if ~isempty(s(1).subs)
123 methodName = s(1).subs;
124 % Check
if this is a call to our alias methods
125 if strcmp(methodName,
'get') && length(s) > 1 && strcmp(s(2).type,
'()')
126 result = obj.get(s(2).subs{:});
128 varargout{1} = subsref(result, s(3:end));
131 varargout{1} = result;
133 elseif strcmp(methodName,
'tget') && length(s) > 1 && strcmp(s(2).type,
'()')
134 result = obj.tget(s(2).subs{:});
136 varargout{1} = subsref(result, s(3:end));
139 varargout{1} = result;
141 elseif strcmp(methodName,
'filterBy') && length(s) > 1 && strcmp(s(2).type,
'()')
142 result = obj.filterBy(s(2).subs{:});
144 varargout{1} = subsref(result, s(3:end));
147 varargout{1} = result;
150 % Check
if this is a table column name - delegate to underlying data
151 if ismember(methodName, obj.data.Properties.VariableNames)
152 varargout{1} = subsref(obj.data, s);
156 % For other properties/methods, delegate to builtin subsref
158 varargout{1} = builtin(
'subsref', obj, s);
160 if strcmp(ME.identifier,
'MATLAB:maxlhs')
161 builtin('subsref', obj, s);
169 % Cell indexing - delegate to underlying table
170 varargout{1} = subsref(obj.data, s);
173 error(
'Invalid indexing type');
177 function result = filterBy(obj, varargin)
178 % FILTERBY Filter table by Station/Node and/or JobClass/Chain objects
180 % Intelligently filters based on table structure and
object types
183 % rows = t.filterBy(station_obj)
184 % rows = t.filterBy(class_obj)
185 % rows = t.filterBy(station_obj, class_obj)
188 % varargin: 1 or 2 arguments - Station/Node/JobClass/Chain objects
191 % result: Filtered MATLAB table
196 if isa(arg1,
'Station') || isa(arg1,
'Node')
197 result = obj.filterByFirstDimension(arg1);
198 elseif isa(arg1, 'JobClass') || isa(arg1, 'Chain')
199 result = obj.filterBySecondDimension(arg1);
201 error('Invalid argument type. Expected Station, Node, JobClass, or Chain.');
211 % Determine order: could be (station,
class) or (class, station)
212 if (isa(arg1, 'Station') || isa(arg1, 'Node')) && ...
213 (isa(arg2, 'JobClass') || isa(arg2, 'Chain'))
214 % First arg
is station/node, second
is class/chain
215 result = obj.filterByFirstDimensionInternal(result, arg1);
216 result = obj.filterBySecondDimensionInternal(result, arg2);
218 elseif (isa(arg1, 'JobClass') || isa(arg1, 'Chain')) && ...
219 (isa(arg2, 'Station') || isa(arg2, 'Node'))
220 % First arg
is class/chain, second
is station/node
221 result = obj.filterByFirstDimensionInternal(result, arg2);
222 result = obj.filterBySecondDimensionInternal(result, arg1);
225 error('Expected one Station/Node and one JobClass/Chain
object.');
229 error('filterBy expects 1 or 2 arguments');
233 function result = tget(obj, varargin)
234 % TGET Alias for filterBy - backward compatibility
235 result = obj.filterBy(varargin{:});
238 function result = get(obj, varargin)
239 % GET Alias
for filterBy - convenient shorthand
240 result = obj.filterBy(varargin{:});
243 function n = numel(obj, varargin)
244 % NUMEL Support numel() function
245 % When called with extra arguments (during indexing like obj(a,b)),
246 % return 1 to tell MATLAB we expect a single output from subsref.
247 % When called directly as numel(obj), return actual element count.
255 function n = numArgumentsFromSubscript(obj, s, indexingContext)
256 % NUMARGUMENTSFROMSUBSCRIPT Specify number of outputs for indexing
257 % This tells MATLAB to always expect exactly 1 output from subsref
258 % operations, which
is appropriate for table data access.
262 function n = size(obj, varargin)
263 % SIZE Support size() function
264 n = size(obj.data, varargin{:});
267 function n = height(obj)
268 % HEIGHT Get number of rows
269 n = height(obj.data);
272 function n = width(obj)
273 % WIDTH Get number of columns
278 % DISP Display the table
282 function summary(obj)
283 % SUMMARY Display table summary
287 function t = head(obj, n)
288 % HEAD Get first n rows
292 t = head(obj.data, n);
295 function t = tail(obj, n)
296 % TAIL Get last n rows
300 t = tail(obj.data, n);
303 function t = getTable(obj)
304 % GETTABLE Return the native MATLAB table
308 function result = varfun(func, obj, varargin)
309 % VARFUN Apply function to table variables
310 % Delegates to the underlying MATLAB table
311 result = varfun(func, obj.data, varargin{:});
314 function result = table2array(obj)
315 % TABLE2ARRAY Convert IndexedTable to array
316 % Delegates to the underlying MATLAB table
317 result = table2array(obj.data);
321 methods (Access =
private)
322 function result = filterByFirstDimension(obj, first_dim_obj)
323 % Filter table by first dimension
object (Station or Node)
324 result = obj.filterByFirstDimensionInternal(obj.data, first_dim_obj);
327 function result = filterByFirstDimensionInternal(~, tbl, first_dim_obj)
328 % Internal method for filtering by first dimension
329 colNames = tbl.Properties.VariableNames;
331 % Try Station first, then Node
332 if ismember('Station', colNames)
334 if iscategorical(col)
335 result = tbl(col == first_dim_obj.name, :);
337 result = tbl(strcmp(col, first_dim_obj.name), :);
339 elseif ismember('Node', colNames)
341 if iscategorical(col)
342 result = tbl(col == first_dim_obj.name, :);
344 result = tbl(strcmp(col, first_dim_obj.name), :);
351 function result = filterBySecondDimension(obj, second_dim_obj)
352 % Filter table by second dimension
object (JobClass or Chain)
353 result = obj.filterBySecondDimensionInternal(obj.data, second_dim_obj);
356 function result = filterBySecondDimensionInternal(~, tbl, second_dim_obj)
357 % Internal method for filtering by second dimension
358 colNames = tbl.Properties.VariableNames;
360 % Try JobClass first, then Chain
361 if ismember('JobClass', colNames)
363 if iscategorical(col)
364 result = tbl(col == second_dim_obj.name, :);
366 result = tbl(strcmp(col, second_dim_obj.name), :);
368 elseif ismember('Chain', colNames)
370 if iscategorical(col)
371 result = tbl(col == second_dim_obj.name, :);
373 result = tbl(strcmp(col, second_dim_obj.name), :);