Search code examples
matlabstructmatrix-indexing

Logical index of structure with various dimensioned fields


Lets say I have a structure like this:

S.index = 1:10;
S.testMatrix = zeros(3,3,10);
for x = 1:10
    S.testMatrix(:,:,x) = magic(3) + x;
end
S.other = reshape(0:39, 4, 10);

It contains a 1x10 vector, a 3x3x10 multi-paged array and a 4x10 matrix. Now say I want to select only the entries corresponding to the indices between 2 and 8. mask = S.index > 2 & S.index < 8;

I tried structfun(@(x) x(mask), S, 'UniformOutput', 0); first which correctly worked for only the vector, which makes perfect sense. So then I figured all I needed to do was expand my mask. So I did this.

test = structfun(@(x) x(repmat(mask, size(x, ndims(x) - 1), 1)), S, 'UniformOutput',0);

The expanded mask was correct for the matrix but not the multi-paged array. And the 2D matrix was flattened to a vector.

If I was going to index these elements individually I would do something like this:

S2.index = S.index(mask);
S2.other = S.other(:,mask);
S2.testMatrix = S.testMatrix(:,:,mask);

My use case is for hundreds of structures each with 20+ fields. How do I script the indexing? The exact problem occurs is limited to a structure with 1xN vectors, 3xN and 4xN matrices and 3x3xN arrays. The mask is constructed based on one of the vectors representing time. The field names are constant for each structure so I could brute force the thing and type in the commands and run it as a function, but I'm looking for an intelligent way to index it.

Update: Here is something that looks promising.

fn = fieldnames(S);
for x = 1:length(fn)
    extraDim = repmat({':'}, 1, ndims(S.(fn{x})) - 1);
    S2.(fn{x}) = S.(fn{x})(extraDim{:}, mask);
end

Solution

  • You can exploit the fact that the string ':' can be used as an index instead of :, and build a comma-separated list of that string repeated the appropriate number of times for each field:

    s = {':',':'}; % auxilary cell array to generate the comma-separated list
    S2 = structfun(@(f) f(s{1:ndims(f)-1}, mask), S, 'UniformOutput', false);