Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow using empty array when indexing on DataStub object #605

Merged
merged 10 commits into from
Feb 11, 2025
40 changes: 40 additions & 0 deletions +tests/+unit/dataStubTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,43 @@ function testObjectCopy(testCase)
tests.util.verifyContainerEqual(testCase, nwbNew, nwb);
nwbExport(nwbNew, 'new.nwb');
end

function testLoadWithEmptyIndices(testCase)
nwb = NwbFile(...
'identifier', 'DATASTUB',...
'session_description', 'test datastub object copy',...
'session_start_time', datetime());

% Add different datatypes to a table, and try to read them in later
% using empty indexing on a DataStub representation
tableToExport = table( ...
{'test'}, ... % Cell
0, ... % Double
false, ... % Logical
struct('x', 1, 'y', 1, 'z', 1) ... % Struct (compound)
);
dynamicTable = util.table2nwb(tableToExport);
nwb.acquisition.set('Test', dynamicTable);

nwbExport(nwb, 'testLoadWithEmptyIndices.nwb')

nwbIn = nwbRead('testLoadWithEmptyIndices.nwb', 'ignorecache');

importedTable = nwbIn.acquisition.get('Test');
varNames = transpose( string(importedTable.colnames) );

for iVarName = varNames
iDataStub = importedTable.vectordata.get(iVarName).data;

testCase.assertClass(iDataStub, 'types.untyped.DataStub')
value = iDataStub([]);
testCase.assertEmpty(value)

if isstruct(tableToExport.(iVarName))
expectedClass = 'table';
else
expectedClass = class(tableToExport.(iVarName));
end
testCase.assertClass(value, expectedClass)
end
end
29 changes: 28 additions & 1 deletion +types/+untyped/@DataStub/load_mat_style.m
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@
, iDimension, dimensionSize);
end

if isscalar(userSelection) && ~ischar(userSelection{1})
if isscalar(userSelection) && isempty(userSelection{1})
ehennestad marked this conversation as resolved.
Show resolved Hide resolved
% If userselection (indices) is empty, get the first element of this
% DataStub and try to return an empty representation of that type.
data = obj.load_mat_style(1);
data = getEmptyRepresentation(data);
return

elseif isscalar(userSelection) && ~ischar(userSelection{1})
% linear index into the fast dimension.
orderedSelection = unique(userSelection{1});

Expand Down Expand Up @@ -200,3 +207,23 @@
indexKeyIndex((indexKeyIndexNextIndex+1):end) = 1;
end
end

function emptyInstance = getEmptyRepresentation(nonEmptyInstance)
ehennestad marked this conversation as resolved.
Show resolved Hide resolved
try
emptyInstance = nonEmptyInstance;
if istable(nonEmptyInstance)
% To make an empty table instance, we need to use row/column colon
% indices to clear all the table's data. We want to keep the
% original table's metadata, like variable names etc, so we clear
% the table data instead of creating a new empty table with
% table.empty
emptyInstance(:, :) = [];
else
% All other types should support linear indexing.
emptyInstance(:) = [];
end
catch ME
error('Failed to retrieve empty type for value of class "%s". Reason:\n%s', ...
class(nonEmptyInstance), ME.message)

Check warning on line 227 in +types/+untyped/@DataStub/load_mat_style.m

View check run for this annotation

Codecov / codecov/patch

+types/+untyped/@DataStub/load_mat_style.m#L225-L227

Added lines #L225 - L227 were not covered by tests
end
end