time | Calls | line |
---|
| | 1 | function t = subsasgnDot(t,s,b,deleting)
|
| | 2 | %SUBSASGNDOT Subscripted assignment to a table.
|
| | 3 |
|
| | 4 | % Copyright 2012-2019 The MathWorks, Inc.
|
| | 5 |
|
| | 6 | % '.' is assignment to or into a variable. Any sort of subscripting
|
| | 7 | % may follow that, and row labels are inherited from the table.
|
| | 8 |
|
| | 9 | import matlab.internal.datatypes.emptyLike
|
| | 10 | import matlab.internal.datatypes.isCharString
|
| | 11 | import matlab.internal.datatypes.isColon
|
| | 12 | import matlab.internal.datatypes.isScalarInt
|
| | 13 | import matlab.lang.correction.ReplaceIdentifierCorrection
|
| | 14 | import matlab.lang.internal.move % Avoid unsharing of shared-data copy across function call boundary
|
< 0.001 | 8 | 15 | subsType = matlab.internal.tabular.private.tabularDimension.subsType; % "import" for calls to subs2inds
|
| | 16 |
|
< 0.001 | 8 | 17 | if ~isstruct(s), s = substruct('.',s); end
|
| | 18 |
|
| | 19 | % Check for deletion of entire variables, t.Var = [], or of columns/pages of a variable,
|
| | 20 | % t.Var(:,j) = []. deletion deeper that, e.g. t.Var(i).Field(...) = [] or t.Var{i}(...) = [],
|
| | 21 | % is handled by the assignment code path.
|
< 0.001 | 8 | 22 | if nargin < 4
|
| | 23 | % Short-circuit for performance before calling _isEmptySqrBrktLiteral. isequal is
|
| | 24 | % more expensive than isnumeric, but avoids _isEmptySqrBrktLiteral in more cases. For
|
| | 25 | % built-in types, s(2).type=='()' guarantees that length(s)==2, but for a var that is
|
| | 26 | % itself a table, parens need not be the end, so need to check that.
|
< 0.001 | 8 | 27 | deleting = isnumeric(b) && isequal(b,[]) && builtin('_isEmptySqrBrktLiteral',b) ...
|
| | 28 | && (isscalar(s) || ((length(s) == 2) && isequal(s(2).type,'()')));
|
< 0.001 | 8 | 29 | end
|
| | 30 |
|
< 0.001 | 8 | 31 | t_nrows = t.rowDim.length;
|
< 0.001 | 8 | 32 | t_nvars = t.varDim.length;
|
| | 33 |
|
| | 34 | % Translate variable (column) name into an index. Avoid overhead of
|
| | 35 | % t.varDim.subs2inds as much as possible in this simple case.
|
< 0.001 | 8 | 36 | varName = convertStringsToChars(s(1).subs);
|
< 0.001 | 8 | 37 | if isnumeric(varName)
|
| | 38 | % Allow t.(i) where i is an integer
|
| | 39 | varIndex = varName;
|
| | 40 | if ~isScalarInt(varName,1)
|
| | 41 | error(message('MATLAB:table:IllegalVarIndex'));
|
| | 42 | end
|
| | 43 | isNewVar = (varIndex > t_nvars);
|
| | 44 | if isNewVar
|
| | 45 | if deleting
|
| | 46 | error(message('MATLAB:table:VarIndexOutOfRange'));
|
| | 47 | elseif varIndex > t_nvars+1
|
| | 48 | error(message('MATLAB:table:DiscontiguousVars'));
|
| | 49 | else
|
| | 50 | [~,~,~,~,~,updatedVarDim] = t.varDim.subs2inds(varIndex,subsType.assignment);
|
| | 51 | varName = updatedVarDim.labels{varIndex};
|
| | 52 | end
|
| | 53 | end
|
< 0.001 | 8 | 54 | elseif ischar(varName) && (isrow(varName) || isequal(varName,'')) % isCharString(varName)
|
< 0.001 | 8 | 55 | varIndex = find(strcmp(varName,t.varDim.labels));
|
< 0.001 | 8 | 56 | isNewVar = false; % assume for now, update below
|
< 0.001 | 8 | 57 | if isempty(varIndex)
|
| | 58 | % Check against reserved names first as a failsafe against shadowing
|
| | 59 | % .Properties by a dimension name.
|
0.003 | 8 | 60 | if ~deleting && t.varDim.checkReservedNames(varName) % one name, don't need to wrap with any()
|
| | 61 | % Handle assignment to a property under the 'Properties' (virtual)
|
| | 62 | % property, or to the entire 'Properties' property.
|
< 0.001 | 8 | 63 | if strcmp(varName,'Properties')
|
< 0.001 | 8 | 64 | try
|
< 0.001 | 8 | 65 | if isscalar(s)
|
| | 66 | t = setProperties(t,b);
|
< 0.001 | 8 | 67 | else
|
0.022 | 8 | 68 | t = t.setProperty(s(2:end),b);
|
< 0.001 | 8 | 69 | end
|
| | 70 | catch ME
|
| | 71 | if ~isscalar(s) && strcmp(ME.identifier,'MATLAB:table:UnknownProperty')
|
| | 72 | propName = s(2).subs;
|
| | 73 | match = find(strcmpi(propName,t.propertyNames),1);
|
| | 74 | if ~isempty(match) % a property name. but with wrong case
|
| | 75 | match = t.propertyNames{match};
|
| | 76 | throw(MException(message('MATLAB:table:UnknownPropertyCase',propName,match)) ...
|
| | 77 | .addCorrection(ReplaceIdentifierCorrection(propName,match)));
|
| | 78 | else
|
| | 79 | throw(ME);
|
| | 80 | end
|
| | 81 | else
|
| | 82 | throw(ME);
|
| | 83 | end
|
< 0.001 | 8 | 84 | end
|
< 0.001 | 8 | 85 | return
|
| | 86 | elseif isColon(varName)
|
| | 87 | error(message('MATLAB:table:ReservedVarnameColon'))
|
| | 88 | else % t.VariableNames or t.RowNames
|
| | 89 | throw(MException(message('MATLAB:table:InvalidPropertyAssignment',varName)) ...
|
| | 90 | .addCorrection(ReplaceIdentifierCorrection(varName,append('Properties.',varName))));
|
| | 91 | end
|
| | 92 | elseif strcmp(varName,t.metaDim.labels{1})
|
| | 93 | % If it's the row dimension name, assign to the row labels
|
| | 94 | varIndex = 0;
|
| | 95 | % For assignments onto the row labels, accept any vector. For assignments
|
| | 96 | % into, leave the RHS alone.
|
| | 97 | if isscalar(s)
|
| | 98 | if isvector(b), b = b(:); end
|
| | 99 | elseif deleting % && ~isscalar(s)
|
| | 100 | error(message('MATLAB:table:NestedSubscriptingWithDotRowsDeletion',t.metaDim.labels{1}));
|
| | 101 | end
|
| | 102 | elseif strcmp(varName,t.metaDim.labels{2})
|
| | 103 | % If it's the vars dimension name, assign to t{:,:}. Deeper subscripting
|
| | 104 | % is not supported, use explicit braces for that.
|
| | 105 | if ~isscalar(s)
|
| | 106 | error(message('MATLAB:table:NestedSubscriptingWithDotVariables',t.metaDim.labels{2}));
|
| | 107 | end
|
| | 108 | varIndex = -1;
|
| | 109 | elseif deleting
|
| | 110 | error(message('MATLAB:table:UnrecognizedVarNameDeleting',varName));
|
| | 111 | else
|
| | 112 | isNewVar = true;
|
| | 113 | t.varDim.makeValidName(varName,'error'); % error if invalid
|
| | 114 |
|
| | 115 | % If this is a new variable, it will go at the end. Its name is guaranteed
|
| | 116 | % to not conflict; it it did, we'd be assigning to an existing var.
|
| | 117 | varIndex = t_nvars + 1;
|
| | 118 | updatedVarDim = t.varDim.lengthenTo(varIndex,{varName});
|
| | 119 | end
|
| | 120 | end
|
| | 121 | else
|
| | 122 | error(message('MATLAB:table:IllegalVarSubscript'));
|
| | 123 | end
|
| | 124 |
|
| | 125 | % Handle empty assignment intended as deletion of an entire variable or of
|
| | 126 | % columns/pages/etc. of a variable. Deletion of rows in a (single)
|
| | 127 | % variable is caught here and not allowed. Other empty assignment
|
| | 128 | % syntaxes may be assignment to cells or may be deletion of things deeper
|
| | 129 | % in a non-atomic variable, neither is handled here.
|
| | 130 | if deleting
|
| | 131 | % Syntax: t.var = []
|
| | 132 | %
|
| | 133 | % Delete an entire variable.
|
| | 134 | if isscalar(s)
|
| | 135 | if varIndex > 0
|
| | 136 | t.data(varIndex) = [];
|
| | 137 | t.varDim = t.varDim.deleteFrom(varIndex);
|
| | 138 | elseif varIndex == 0
|
| | 139 | t.rowDim = t.rowDim.removeLabels(); % this might error
|
| | 140 | else % varindex == -1
|
| | 141 | varIndex = 1:t.varDim.length;
|
| | 142 | t.data(varIndex) = [];
|
| | 143 | t.varDim = t.varDim.deleteFrom(varIndex);
|
| | 144 | end
|
| | 145 | % Syntax: t.var(:,...) = []
|
| | 146 | % t.var(rowIndices,...) = [] is illegal
|
| | 147 | %
|
| | 148 | % Delete columns/pages/etc. of a variable, with ':' as the first index
|
| | 149 | % in subscript. This may change the dimensionality of the variable,
|
| | 150 | % but won't change the number of rows because we require ':' as the
|
| | 151 | % first index.
|
| | 152 | else % length(s) == 2
|
| | 153 | % All vars in a table must have the same number of rows, so subscripted assignment
|
| | 154 | % deletion on one var isn't allowed to remove rows: no linear indexing, and the
|
| | 155 | % first subscript in 2- or N-D indexing, and all others except one, must be :.
|
| | 156 | if isscalar(s(2).subs) ...
|
| | 157 | || ~isColon(s(2).subs{1}) ...
|
| | 158 | || all(cellfun(@isColon,s(2).subs))
|
| | 159 | error(message('MATLAB:table:EmptyAssignmentToVariableRows'));
|
| | 160 | end
|
| | 161 |
|
| | 162 | var_j = t.data{varIndex}; t.data{varIndex} = []; % DO NOT separate these calls: necessary to avoid shared copy unsharing
|
| | 163 | if isa(var_j,'tabular')
|
| | 164 | % Use dot method to dispatch to overloaded table subscripting
|
| | 165 | var_j = move(var_j).subsasgnParens(s(2),[],false,true);
|
| | 166 | else
|
| | 167 | var_j(s(2).subs{:}) = [];
|
| | 168 | end
|
| | 169 | t.data{varIndex} = var_j;
|
| | 170 | end
|
| | 171 |
|
| | 172 | else
|
| | 173 | updatedRowDim = [];
|
| | 174 |
|
| | 175 | % Syntax: t.var = b
|
| | 176 | %
|
| | 177 | % Replace an entire variable. It must have the right number of rows, unless
|
| | 178 | % the LHS is 0x0.
|
| | 179 | if isscalar(s)
|
| | 180 | if size(b,1) ~= t_nrows && (t_nrows+t_nvars > 0)
|
| | 181 | % If the assignment has the wrong number of rows, check for some
|
| | 182 | % common mistakes to suggest what may have been intended
|
| | 183 | if strcmpi(varName,'Properties') && ((isstruct(b) && isscalar(b)) || isa(b,'matlab.tabular.TabularProperties'))
|
| | 184 | % Things like t.properties = scalarStruct
|
| | 185 | str = getString(message('MATLAB:table:IntendedPropertiesAssignment'));
|
| | 186 | throw(MException(message('MATLAB:table:RowDimensionMismatchSuggest',str)) ...
|
| | 187 | .addCorrection(ReplaceIdentifierCorrection(varName,'Properties')));
|
| | 188 | else
|
| | 189 | match = find(strcmpi(varName,t.propertyNames),1);
|
| | 190 | if ~isempty(match)
|
| | 191 | % Things like t.PropertyName = ...
|
| | 192 | match = t.propertyNames{match};
|
| | 193 | str = getString(message('MATLAB:table:IntendedPropertyAssignment',match));
|
| | 194 | throw(MException(message('MATLAB:table:RowDimensionMismatchSuggest',str)) ...
|
| | 195 | .addCorrection(ReplaceIdentifierCorrection(varName,append('Properties.', match))));
|
| | 196 | end
|
| | 197 | end
|
| | 198 | % Anything else, no suggestion. No point in checking for a case
|
| | 199 | % insensitive match to an existing var, even with the correct case,
|
| | 200 | % this would still be an illegal assignment
|
| | 201 | error(message('MATLAB:table:RowDimensionMismatch'));
|
| | 202 | end
|
| | 203 | var_j = b;
|
| | 204 |
|
| | 205 | % Syntax: t.var(rowIndices,...) = b
|
| | 206 | % t.var{rowIndices,...} = b
|
| | 207 | % t.var{rowIndices,...} = [] (this is assignment, not deletion)
|
| | 208 | % t.var.field = b
|
| | 209 | %
|
| | 210 | % Assign to elements in a variable. Assignment can also be used to
|
| | 211 | % expand the variable's number of rows, or along another dimension.
|
| | 212 | %
|
| | 213 | % Cell indexing, e.g. t.var{rowIndices,...}, or a reference to a
|
| | 214 | % field, e.g. t.var.field, may also be followed by deeper levels of
|
| | 215 | % subscripting. Cannot create a new var implicitly by deeper indexing.
|
| | 216 | else % ~isscalar(s)
|
| | 217 | if isNewVar && (length(s) > 2) && ~isequal(s(2).type,'.')
|
| | 218 | % If the assignment is not to an existing var, check for some common
|
| | 219 | % mistakes to suggest what may have been intended
|
| | 220 | match = matches(t.varDim.labels,varName,'IgnoreCase',true);
|
| | 221 | if any(match)
|
| | 222 | % An existing variable name, but with wrong case
|
| | 223 | match = t.varDim.labels{match};
|
| | 224 | str = getString(message('MATLAB:table:IntendedVarAssignment',match));
|
| | 225 | throw(MException(message('MATLAB:table:InvalidExpansionDotDepthSuggest',str)) ...
|
| | 226 | .addCorrection(ReplaceIdentifierCorrection(varName,match)));
|
| | 227 | elseif matches(varName,t.metaDim.labels{1},'IgnoreCase',true)
|
| | 228 | % The row dimension name, but with wrong case
|
| | 229 | str = getString(message('MATLAB:table:IntendedRowDimAssignment',t.metaDim.labels{1}));
|
| | 230 | throw(MException(message('MATLAB:table:InvalidExpansionDotDepthSuggest',str)) ...
|
| | 231 | .addCorrection(ReplaceIdentifierCorrection(varName,t.metaDim.labels{1})));
|
| | 232 | end
|
| | 233 | % Anything else, no suggestion
|
| | 234 | error(message('MATLAB:table:InvalidExpansionDotDepth'));
|
| | 235 | end
|
| | 236 |
|
| | 237 | if isequal(s(2).type,'.') % dot indexing into variable
|
| | 238 | % If the assignment is not to an existing var, check for some common
|
| | 239 | % mistakes to suggest what may have been intended
|
| | 240 | if isNewVar
|
| | 241 | if strcmpi(varName,'Properties') && isCharString(s(2).subs)
|
| | 242 | % Things like t.properties.name
|
| | 243 | str = getString(message('MATLAB:table:IntendedPropertiesAssignment'));
|
| | 244 | throw(MException(message('MATLAB:table:InvalidExpansionDotSuggest',str)) ...
|
| | 245 | .addCorrection(ReplaceIdentifierCorrection(varName,'Properties')));
|
| | 246 | else
|
| | 247 | match = matches(t.varDim.labels,varName,'IgnoreCase',true);
|
| | 248 | if any(match)
|
| | 249 | % An existing variable name, but with wrong case
|
| | 250 | match = t.varDim.labels{match};
|
| | 251 | str = getString(message('MATLAB:table:IntendedVarAssignment',match));
|
| | 252 | throw(MException(message('MATLAB:table:InvalidExpansionDotSuggest',str)) ...
|
| | 253 | .addCorrection(ReplaceIdentifierCorrection(varName,match)));
|
| | 254 | else
|
| | 255 | % Anything else, no suggestion
|
| | 256 | error(message('MATLAB:table:InvalidExpansionDot'));
|
| | 257 | end
|
| | 258 | end
|
| | 259 | end
|
| | 260 | if varIndex > 0
|
| | 261 | var_j = t.data{varIndex}; t.data{varIndex} = []; % DO NOT separate these calls: necessary to avoid shared copy unsharing
|
| | 262 | elseif varIndex == 0
|
| | 263 | var_j = t.rowDim.labels;
|
| | 264 | else % varIndex == -1
|
| | 265 | assert(false);
|
| | 266 | end
|
| | 267 | else % () or {} subscripting into variable
|
| | 268 | % Initialize a new var, or extract an existing var.
|
| | 269 | if isNewVar
|
| | 270 | % Start the new var out as an Nx0 empty of b's class, with the same
|
| | 271 | % number of rows as the table.
|
| | 272 | if isequal(s(2).type,'{}')
|
| | 273 | % {} subscripting on the new var indicates it should be a cell
|
| | 274 | % with contents being assigned.
|
| | 275 | var_j = cell(t_nrows,0);
|
| | 276 | else
|
| | 277 | var_j = emptyLike([t_nrows,0],'Like',b);
|
| | 278 | end
|
| | 279 |
|
| | 280 | % If the table has no rows, the new var was initialized as 0x0 and
|
| | 281 | % a colon subscript in the first dim would be misinterpreted. Create
|
| | 282 | % explicit row indices instead.
|
| | 283 | if t_nrows == 0 && isColon(s(2).subs{1})
|
| | 284 | if t_nvars == 0
|
| | 285 | % If the table is 0x0, a colon subscript in the first dim should
|
| | 286 | % mean "height of the RHS". t.rowDim.subs2inds would think ':' means
|
| | 287 | % "height of t", so create explicit row indices to let it know how
|
| | 288 | % big : really is.
|
| | 289 | s(2).subs{1} = 1:size(b,1);
|
| | 290 | else
|
| | 291 | % Otherwise, a colon subscript in the first dim should mean "height
|
| | 292 | % of the table", and the RHS must have matching height. var_j is
|
| | 293 | % initialized to have t_nrows rows to match the table, but when t_nrows
|
| | 294 | % is 0, var_j is initialized as 0x0, and var_j's subsasgn would treat
|
| | 295 | % : as "height of the RHS" and not do the proper size checking. Create
|
| | 296 | % explicit row indices to make sure the RHS's height is checked.
|
| | 297 | %
|
| | 298 | s(2).subs{1} = 1:t_nrows;
|
| | 299 | end
|
| | 300 | end
|
| | 301 | % If the table has one or more rows, a colon subscript in the first dim always
|
| | 302 | % means "height of the table", and that subscript can be left alone.
|
| | 303 |
|
| | 304 | % Convert any trailing colon subscripts into explicit indices with length
|
| | 305 | % inherited from the RHS.
|
| | 306 | for k = 2:length(s(2).subs)
|
| | 307 | if isColon(s(2).subs{k})
|
| | 308 | s(2).subs{k} = 1:size(b,k);
|
| | 309 | end
|
| | 310 | end
|
| | 311 | else
|
| | 312 | if varIndex > 0
|
| | 313 | var_j = t.data{varIndex}; t.data{varIndex} = []; % DO NOT separate these calls: necessary to avoid shared copy unsharing
|
| | 314 | elseif varIndex == 0
|
| | 315 | var_j = t.rowDim.labels;
|
| | 316 | else % varIndex == -1
|
| | 317 | assert(false);
|
| | 318 | end
|
| | 319 | end
|
| | 320 |
|
| | 321 | subs1 = s(2).subs{1};
|
| | 322 | haveLabelSubscripts = ~(isnumeric(subs1) || islogical(subs1) || isColon(subs1));
|
| | 323 | if haveLabelSubscripts
|
| | 324 | % The variable inherits row labels from the table, translate to row indices. The
|
| | 325 | % assignment may add rows, get the updated rowDim object with any new row labels.
|
| | 326 | % subs2inds returns the indices as a col vector, which prevents reshaping. This
|
| | 327 | % is fine because the var is constrained inside the table.
|
| | 328 | [s(2).subs{1},~,~,~,~,updatedRowDim] = t.rowDim.subs2inds(subs1,subsType.assignment);
|
| | 329 | % There are some linear indexing cases that should have row semantics, or that
|
| | 330 | % are not even legal. In those cases s(2).subs{1} can't be interpreted as row
|
| | 331 | % labels and so calling t.rowDim.subs2inds returns something completely
|
| | 332 | % meaningless. Those cases will be identified and caught immediately below.
|
| | 333 | else
|
| | 334 | % t.rowDim.subs2inds will leave rowSubscripts alone in these cases, other than
|
| | 335 | % making it a column, so avoid calling it for performance. Leave updateRowDim
|
| | 336 | % empty, only need that in the row labels case.
|
| | 337 | s(2).subs{1} = subs1(:);
|
| | 338 | end
|
| | 339 |
|
| | 340 | if isscalar(s(2).subs) % linear indexing into the LHS
|
| | 341 | % If the LHS is linear indexing, e.g. t.var(indices) = b or t.var{indices} = b,
|
| | 342 | % and new elements will be created, there are cases where we need to force it to
|
| | 343 | % grow as a column vector, because it would try to grow as a row vector.
|
| | 344 | %
|
| | 345 | % If the var is
|
| | 346 | % a scalar or a 0x0
|
| | 347 | % a new var (which is initialized to Nx0, including possibly 1x0)
|
| | 348 | % an Nx0 (N>1) or 0xM (M>1) empty matrix
|
| | 349 | % add a column index so it grows as a column vector.
|
| | 350 | if isscalar(var_j)
|
| | 351 | s(2).subs = [s(2).subs {1}];
|
| | 352 | elseif iscolumn(var_j) % including 0x1
|
| | 353 | % If the var is already a column, linear indexing will have column semantics,
|
| | 354 | % leave the subscript alone. A scalar is a column, but need to force it to
|
| | 355 | % behave like one, so catch those above.
|
| | 356 | elseif all(size(var_j)==0) || isNewVar
|
| | 357 | s(2).subs = [s(2).subs {1}];
|
| | 358 | elseif ismatrix(var_j) && isempty(var_j) && ~isrow(var_j) % Nx0 or 0xM, excluding 1x0
|
| | 359 | % This case is analogous to what would happen in the workspace, except in table
|
| | 360 | % the assignment creates a column instead of a row.
|
| | 361 | s(2).subs = [s(2).subs {1}];
|
| | 362 | var_j = var_j(:); % make it a 0x1 column to be safe
|
| | 363 |
|
| | 364 | % By now var_j must be a row (possibly 1x0), a non-empty matrix, or an N-D array.
|
| | 365 | elseif haveLabelSubscripts
|
| | 366 | % Numeric, logical, and colon subscripts have unambiguous meaning as in linear
|
| | 367 | % indexing regardless of the shape of the var being assigned into. But row
|
| | 368 | % labels have meaning only for column semantics, i.e. only if the var is already
|
| | 369 | % a column (including a 0x1), or if we've added a column index to force the
|
| | 370 | % result of the assignment to _become_ a column. Otherwise, linear indexing with
|
| | 371 | % row labels is an error.
|
| | 372 | error(message('MATLAB:table:InvalidLinearIndexing'));
|
| | 373 | else
|
| | 374 | % If the var is a row, linear indexing has row semantics, let that happen.
|
| | 375 | % If the var is a non-empty matrix, or any N-D array, assignment using linear
|
| | 376 | % indexing is an ambiguous dimension error, let that happen at the actual
|
| | 377 | % assignment.
|
| | 378 | end
|
| | 379 | end
|
| | 380 | end
|
| | 381 |
|
| | 382 | % Now let the variable's subsasgn handle the subscripting in
|
| | 383 | % things like t.name(...) or t.name{...} or t.name.attribute
|
| | 384 |
|
| | 385 | if length(s) == 2
|
| | 386 | % If b is a built-in type, or the same class as var_j, call subsasgn directly
|
| | 387 | % for fastest dispatch to var_j's (possibly overloaded) subscripting. Otherwise,
|
| | 388 | % force dispatch to var_j's subscripting even when b is dominant. In most cases,
|
| | 389 | % calling subsasgn via builtin guarantees dispatch on the first input. However,
|
| | 390 | % if var_j is a table, builtin would dispatch to default, not overloaded,
|
| | 391 | % subscripting, so use dot-method syntax.
|
| | 392 | if isobject(b)
|
| | 393 | if isa(var_j,class(b)) % var_j first is fast when it is built-in
|
| | 394 | var_j = subsasgn(var_j,s(2),b); % dispatches correctly, even to tabular
|
| | 395 | elseif isa(var_j,'tabular')
|
| | 396 | var_j = move(var_j).subsasgn(s(2),b);
|
| | 397 | else
|
| | 398 | var_j = builtin('subsasgn',var_j,s(2),b);
|
| | 399 | end
|
| | 400 | else
|
| | 401 | % If the RHS of the assignment into the table was a literal [], and the LHS
|
| | 402 | % target is t.Var or t.Var(...), that's already been recognized as subscripted
|
| | 403 | % assignment deletion, and handled correctly. A RHS that is a 0x0 double but not
|
| | 404 | % a literal [] should be treated as a genuine assignment, but the built-in
|
| | 405 | % subsasgn called here treats that as deletion when the LHS is a built-in type
|
| | 406 | % subscripted with (). Happily, assignment of any other empty double will have
|
| | 407 | % the desired effect, so turn b into a 0x1. The same must be done for '' (which
|
| | 408 | % "is equal" to []) to prevent it from deleting.
|
| | 409 | if isequal(b,[]) && ~isobject(var_j) && isequal(s(2).type,'()')
|
| | 410 | % One exception: subscripted assignment deletion other than t.Var=[] or
|
| | 411 | % t.Var(...)=[], such as t.Var(i).Field(...)=[] or t.Var{i}(...)=[], ends up
|
| | 412 | % here for delegation to var_j, so don't replace a RHS that _is_ a literal [].
|
| | 413 | if ischar(b) || ~builtin('_isEmptySqrBrktLiteral',b), b = b(:); end
|
| | 414 | end
|
| | 415 | var_j = subsasgn(move(var_j),s(2),b);
|
| | 416 | end
|
| | 417 | else % length(s) > 2
|
| | 418 | % Trick the third and higher levels of subscripting in things like
|
| | 419 | % t.Var{i}(...) etc. into dispatching to the right place even when
|
| | 420 | % t.Var{i}, or something further down the chain, is itself a table.
|
| | 421 | if isequal(b,[]) && isequal(s(end).type,'()')
|
| | 422 | if ischar(b) || ~builtin('_isEmptySqrBrktLiteral',b), b = b(:); end
|
| | 423 | end
|
| | 424 | var_j = matlab.internal.tabular.private.subsasgnRecurser(move(var_j),s(2:end),b);
|
| | 425 | end
|
| | 426 | end
|
| | 427 |
|
| | 428 | % If this is a new variable, make it official.
|
| | 429 | if isNewVar
|
| | 430 | t.varDim = updatedVarDim;
|
| | 431 | end
|
| | 432 |
|
| | 433 | % If an entire var was replaced or created, the new value was required to have
|
| | 434 | % the same number of rows as the table. However, when assigning _into_ a new
|
| | 435 | % var, the assignment might create something shorter than the table, so check
|
| | 436 | % for that and tallen the new var to match the table. Also, assigning into an
|
| | 437 | % existing var that is Nx0 using linear indexing will turn it into a col that
|
| | 438 | % might be shorter, so tallen it to match the table, but don't warn since that's
|
| | 439 | % an implementation artifact. (Assigning into an existing var that is 0xM using
|
| | 440 | % linear indexing will also turn it into a col, but it can never be shorter.)
|
| | 441 | % (Historically, a var could also get shorter by assigning a field to a
|
| | 442 | % non-struct, or by assigning via a direct call to subsasgn into new elements of
|
| | 443 | % _any_ matrix using linear indexing. Neither works that way now. Those would
|
| | 444 | % have gotten fixed here too.)
|
| | 445 | varLen = size(var_j,1);
|
| | 446 | if varLen < t_nrows % t's original number of rows
|
| | 447 | if isNewVar
|
| | 448 | warning(message('MATLAB:table:RowsAddedNewVars'));
|
| | 449 | end
|
| | 450 | var_j = t.lengthenVar(var_j,t_nrows);
|
| | 451 | end
|
| | 452 | if varIndex > 0
|
| | 453 | t.data{varIndex} = var_j;
|
| | 454 | elseif varIndex == 0
|
| | 455 | t.rowDim = t.rowDim.setLabels(var_j);
|
| | 456 | else % varIndex == -1
|
| | 457 | t = t.subsasgnBraces({':' ':'},var_j);
|
| | 458 | end
|
| | 459 |
|
| | 460 | % If the var being assigned to is now taller than the table, add rows to
|
| | 461 | % the rest of the table, including row labels. This might be because the
|
| | 462 | % assignment lengthened an existing var, or because an "into" assignment
|
| | 463 | % created a new var taller than the table. Warn only if we have to lengthen
|
| | 464 | % existing vars that have not been assigned to -- if there's currently only
|
| | 465 | % one var in the table (which might be existing or new), don't warn about
|
| | 466 | % any default values "filled in in the middle".
|
| | 467 | if varLen > t_nrows % t's original number of rows
|
| | 468 | if t.varDim.length > 1 % some existing vars were not assigned to
|
| | 469 | warning(message('MATLAB:table:RowsAddedExistingVars'));
|
| | 470 | end
|
| | 471 | if isempty(updatedRowDim)
|
| | 472 | t.rowDim = t.rowDim.lengthenTo(varLen);
|
| | 473 | else
|
| | 474 | t.rowDim = updatedRowDim;
|
| | 475 | end
|
| | 476 | t = t.lengthenTo(varLen); % updates nrows
|
| | 477 | end
|
| | 478 | end
|