diff --git a/inst/calendarDuration.m b/inst/calendarDuration.m index 6ea5062b..14f814eb 100644 --- a/inst/calendarDuration.m +++ b/inst/calendarDuration.m @@ -834,6 +834,16 @@ function disp (this) function this = subsasgnParensPlanar (this, s, rhs) #SUBSASGNPARENSPLANAR ()-assignment for planar object + if (isnumeric (rhs) && isequal (size (rhs), [0 0])) + # Special `x(ix) = []` deletion form + this.Sign(s.subs{:}) = []; + this.Years(s.subs{:}) = []; + this.Months(s.subs{:}) = []; + this.Days(s.subs{:}) = []; + this.Time(s.subs{:}) = []; + this.IsNaN(s.subs{:}) = []; + return + endif if (! isa (rhs, 'calendarDuration')) rhs = calendarDuration (rhs); endif diff --git a/inst/categorical.m b/inst/categorical.m index ef34f619..f51e087d 100644 --- a/inst/categorical.m +++ b/inst/categorical.m @@ -1264,6 +1264,12 @@ function summary (this) function this = subsasgnParensPlanar (this, s, rhs) #SUBSASGNPARENSPLANAR ()-assignment for planar object + if (isnumeric (rhs) && isequal (size (rhs), [0 0])) + # Special `x(ix) = []` deletion form + this.code(s.subs{:}) = []; + this.tfMissing(s.subs{:}) = []; + return + endif if (! isa (rhs, 'categorical')) # TODO: This conversion is probably wrong. It probably needs to be done # with respect to this's existing cats list diff --git a/inst/datetime.m b/inst/datetime.m index 5f219252..7abe00c7 100644 --- a/inst/datetime.m +++ b/inst/datetime.m @@ -1487,6 +1487,12 @@ function disp (this) function out = subsasgnParensPlanar (this, s, rhs) #SUBSASGNPARENSPLANAR ()-assignment for planar object + if (isnumeric (rhs) && isequal (size (rhs), [0 0])) + # Special `x(ix) = []` deletion form + out = this; + out.dnums(s.subs{:}) = []; + return + endif if (! isa (rhs, 'datetime')) rhs = datetime (rhs); endif diff --git a/inst/duration.m b/inst/duration.m index de6c8ecf..3019bc4f 100644 --- a/inst/duration.m +++ b/inst/duration.m @@ -915,6 +915,13 @@ function disp (this) function out = subsasgnParensPlanar (this, s, rhs) #SUBSASGNPARENSPLANAR ()-assignment for planar object + if (isnumeric (rhs) && isequal (size (rhs), [0 0])) + # Special `x(ix) = []` deletion form + out = this; + out.days = []; + out.days(s.subs{:}) = []; + return + endif if (! isa (rhs, 'duration')) rhs = duration (rhs); endif diff --git a/inst/string.m b/inst/string.m index e9b9236c..b0756370 100644 --- a/inst/string.m +++ b/inst/string.m @@ -1188,6 +1188,12 @@ function disp (this) function this = subsasgnParensPlanar (this, s, rhs) #SUBSASGNPARENSPLANAR ()-assignment for planar object + if (isnumeric (rhs) && isequal (size (rhs), [0 0])) + # Special `x(ix) = []` deletion form + this.strs(s.subs{:}) = []; + this.tfMissing(s.subs{:}) = []; + return + endif if (! isa (rhs, 'string')) rhs = string (rhs); endif diff --git a/inst/table.m b/inst/table.m index a10296e3..0b5fc9e5 100644 --- a/inst/table.m +++ b/inst/table.m @@ -987,15 +987,27 @@ function prettyprint (this) endif out = this; - switch (s(1).type) + switch (s.type) case '()' - error ('table.subsasgn: Assignment using ()-indexing is not supported for table'); + if (isnumeric (rhs) && isequal (size (rhs), [0 0])) + # Special `x(ix) = []` deletion form + if (numel (s.subs) != 2) + error ('table.subsasgn: table subscripting must use exactly 2 subscripts; got %d subscripts', ... + numel (s.subs)) + endif + if (! isequal (s.subs{2}, ":")) + error ('table.subsasgn: in `tbl(ixR,ixC) = []`, ixC must be ":"') + endif + out = subsetrows_impl(this, s.subs{1}, true); + else + error ('table.subsasgn: Assignment using ()-indexing is not yet implemented for table'); + end case '{}' if (numel (s.subs) != 2) error ('table.subsasgn: {}-indexing of table requires exactly two arguments'); endif [ixRow, ixVar] = resolveRowVarRefs (this, s.subs{1}, s.subs{2}); - if(! isscalar (ixVar)) + if (! isscalar (ixVar)) error ('table.subsasgn: {}-indexing must reference a single variable; got %d', ... numel (ixVar)); endif @@ -1009,6 +1021,8 @@ function prettyprint (this) endif if (isequal (s.subs, 'Properties')) # Special case for this.Properties access + # Will need to be implemented using chained assignment, and probably handled + # up above in a special case near the top. error ('table.subsasgn: .Properties access is not implemented yet'); else out = setvar (this, s.subs, rhs); @@ -3314,22 +3328,7 @@ function prettyprint (this) ## ## @end deftypefn function out = subsetrows (this, ixRows) - out = this; - if (! isnumeric (ixRows) && !islogical (ixRows)) - # TODO: Hmm. Maybe we ought not to do this check, but just defer to the - # individual variable values' indexing logic, so SUBSREF/SUBSINDX overrides - # are respected. Would produce worse error messages, but be more "right" - # type-wise. - error ('table.subsetrows: ixRows must be numeric or logical; got a %s', ... - class (ixRows)); - endif - s = struct ('type', '()', 'subs', {{ixRows,':'}}); - for i = 1:width (this) - out.VariableValues{i} = subsref (out.VariableValues{i}, s); - endfor - if (! isempty (this.RowNames)) - out.RowNames = out.RowNames(ixRows); - endif + out = subsetrows_impl (this, ixRows); endfunction ## -*- texinfo -*- @@ -3480,6 +3479,50 @@ function mustBeAllColVectorVars (this) methods (Access = private) + function out = subsetrows_impl (this, ixRows, doInvertIx) + # Internal implementation for subsetrows and restrict, supporting index inversion + if (nargin < 3 || isempty (doInvertIx)); doInvertIx = false; end + mustBeScalarLogical (doInvertIx); + + ixRowsIn = ixRows; + if (doInvertIx) + if (islogical (ixRowsIn)) + ixRows = ! ixRowsIn; + elseif (isnumeric (ixRowsIn)) + nRows = height (this); + tfBad = ixRows > nRows; + if any (tfBad) + badIxs = ixRows(tfBad); + error ('table: invalid row index: index (%d) exceeds number of rows (%d)', ... + badIxs(1), nRows) + endif + ixRows = 1:height (this); + ixRows(ixRowsIn) = []; + else + error ('table: invalid type for ixRows: must be logical or numeric; got %s', class (ixRows)) + endif + endif + + out = this; + if (! isnumeric (ixRows) && !islogical (ixRows)) + # TODO: Hmm. Maybe we ought not to do this check, but just defer to the + # individual variable values' indexing logic, so SUBSREF/SUBSINDX overrides + # are respected. Would produce worse error messages, but be more "right" + # type-wise. + error ('table.subsetrows: ixRows must be numeric or logical; got a %s', ... + class (ixRows)); + endif + s = struct ('type', '()', 'subs', {{ixRows,':'}}); + varVals = this.VariableValues; + for i = 1:width (this) + varVals{i} = subsref (varVals{i}, s); + endfor + out.VariableValues = varVals; + if (! isempty (this.RowNames)) + out.RowNames = out.RowNames(ixRows); + endif + endfunction + function out = proxykeysForOneTable (this) varProxyKeys = cell (size (this.VariableNames)); for iVar = 1:numel (this.VariableNames);