Skip to content

Commit

Permalink
Allow using empty array when indexing on DataStub object (#605)
Browse files Browse the repository at this point in the history
* Coarse fix for #595

* Add test

* Update test to test different data types

* Make local function getEmptyRepresentation more explicit

* Remove unused code from test
  • Loading branch information
ehennestad authored Feb 11, 2025
1 parent c3eedb7 commit 065dbf8
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
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})
% 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)
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)
end
end

0 comments on commit 065dbf8

Please sign in to comment.