MATLAB Answers

Matt B
0

'Subscripted assignment between dissimilar structures' thrown on empty struct

Asked by Matt B
on 2 Mar 2013
Latest activity Answered by David Young
on 21 Jan 2014
As shown in the code below, I'm attempting to populate a struct array iteratively. I have a function which returns a 1x1 struct - don't worry all of the fields returned are identical across function calls - and I wish to store these output as entries in a struct array.
The problem I am running into occurs when I attempt to assign the first returned 1x1 struct to the first entry in my struct array. Below is a working dummy example of what happens:
struct_array = struct([]);
first_struct = struct('field1',1,'field2',2);
% This throws an error:
struct_array(1) = first_struct;
Removing the index from struct_array in the last line fixes the error. This makes sense since it's overwriting (not modifying) the struct defined previously. The work around I plan to use for now is as follows:
struct_array = struct([]);
for i =1:length(input)
next_struct = struct_fn(input{i})
if isempty(struct_array)
struct_array = next_struct;
else
struct_array(i) = next_struct;
However this just seems like a silly way to go about it. Is there a better way?
But further, why is this a feature?
I can understand the justification for throwing the error on indexed assignment to a 1x1 struct with no fields (the output of struct()) since the fact that it has non-zero dimensions would seem to imply that the container has been instantiated, with fields given by the empty set. So the error is thrown based on a comparison of the fields of the attempted assignment and the empty set.
However, when the struct array in question is initialized with struct([]) it is a 0x0 struct array with no fields, and so it would seem to imply that the container hasn't been instantiated and the fields are given by null. So indexed assignment should instantiate the struct and its fields.
Clearly though this is not how it works since fieldnames(struct()) and fieldnames(struct([])) both return 0x1 empty cell arrays. In fact, the only discernible difference I can find between struct() and struct([]) is that when given as input to the 'isempty' function they return (sensibly enough) true and false, respectively.
So my final question is, other than that, what's the difference between struct() and struct([])?

  0 Comments

Sign in to comment.

4 Answers

Answer by Matt J
on 3 Mar 2013
Edited by Matt J
on 3 Mar 2013
 Accepted Answer

Like any MATLAB array, it is bad to iteratively concatenate to a struct array because it forces lots of unnecessary memory reallocation. It would be better if you pre-allocate the array by doing
N=length(input);
struct_array(N)=struct('field1",[],'field2',[]);
for i=1:N
struct_array(i)=struct_fn(input(i));
end
Alternatively, instead of pre-allocating before the loop, you could also loop backwards so that all memory allocation is done in the first iteration of the loop.
for i=N:-1:1
struct_array(i)=struct_fn(input(i));
end

  5 Comments

I agree, pre-allocating the array would be the ideal solution, since it would be faster and also avoid the error entirely.
My example was slightly mis-leading for the sake of simplicity, but not every input becomes a valid output. The input are names of mat files in which some data are stored. Output are essentially re-formatted and reduced information from these input to make a particular analysis more manageable. However, the fields I need require that some processing has been done which not every data set has been through.
In short, I don't know how many entries I will need in the struct array until the function returns values for each input.
As for the Index error message, you will only receive this upon access, not assignment. For example, the following works:
a=[]; a(1)=1;
you will only receive this upon access, not assignment. For example, the following works:
OK, well that has clued me in to what is going on. The reason that indexed assignment
struct_array(1) = first_struct;
gives the error you see is because the indexing tells MATLAB that you are assigning into an existing array. Assignment into an existing array will always result in MATLAB attempting to convert the right hand side to the type on left hand side, so that all the array elements can meaningfully co-exist within the same array object. Because first_struct has a different set of fields from struct_array, this conversion is undefined.
This is a general rule across all MATLAB data types. For example,
a=[]; a(1)=1;
works, as you noted, but the following does not, again because the required conversion is ambiguous.
>> a=[]; a(1)={1}
The following error occurred converting from cell to double:
Error using double
Conversion to double from cell is not possible.
In any case, I wonder what the purpose of your original question is if you aren't able to pre-allocate. What advantage were you looking for in defining an initial empty struct at all if it's just going to get overwritten by an iteratively growing struct array?
That's spot on, thanks! As you pointed out, defining the initial empty struct is completely unnecessary, and in fact the reason why the error occurs. I think I assumed (carelessly) that I needed to associate the variable name with a struct object before I tried to assign a struct to it.
In any case, thank you!

Sign in to comment.


Answer by Martin Juhl on 9 Jan 2014

One easy hack that works:
% cell array of structs
list = cell(N,1)
for i = 1:N
list{i} = struct('field1',1,'field2',2);
end
% array of structs
list = [list{:}];

  1 Comment

It works, but it can be costly to call the struct() constructor repeatedly in a loop. Compare,
N=1000;
tic;
list = cell(N,1);
for i = 1:N
list{i} = struct('field1',1,'field2',2);
end
list = [list{:}];
toc
%Elapsed time is 0.011689 seconds.
tic;
list(1:N)=struct('field1',1,'field2',2);
toc
%Elapsed time is 0.000243 seconds.
In the OP's case, I guess it may not matter if the loop contains other heavy functions calls...

Sign in to comment.


Answer by Jim Hokanson on 10 Aug 2013

If you choose not to initialize you can loop backwards however it is generally best to declare the variable as a structure. This should not be done with:
struct([])
but rather with:
struct()
The entire code becomes:
struct_array = struct();
for i=N:-1:1
struct_array(i)=struct_fn(input(i));
end
or if N will ever be 0
if N == 0 struct_array = struct([]) else %Insert code from above end
Doing this ensures that struct_array is in fact a structure and that you don't retain any previous values if this variable was previously used as a structure array. Consider the following:
s = 3;
%------ Go down a bunch of lines later
for i = N:-1:1
s(i) = struct_fn(input(i)); %Oops, this will throw an error
end
or even worse
s(N+30) = some_value;
%---------- Go down a bunch of lines later
for i = N:-1:1
s(i) = struct_fn(input(i)); %Now s has extra elements.
end
99% of the time no initialization is needed, but I think it is better to add struct() prior to the loop

  0 Comments

Sign in to comment.


Answer by David Young
on 21 Jan 2014

For some discussion of the different kinds of empty struct arrays, and also for an easy way to initialize struct arrays that are empty in different ways, you could have a look at my File Exchange submission emptyStruct.

  0 Comments

Sign in to comment.