Reputation: 618
%example
clear all
a1 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a2 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a3 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a4 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
Pretend that the structures represent a time series where a1 represents the first 5 day (for example), a2 represents day 5-10 and so on... I'm trying to combine every fieldname in the structures so that I have one continuous series (instead of having them split into different structures. For example...
data1 = [a1.data1;a2.data1;a3.data1;a4.data1];
and then do the same for data2 and data3
What would be the best way to do this?
Upvotes: 2
Views: 285
Reputation: 4118
@Andrey is right in saying that "The best way is to define the structures as array of structures beforehand". His answer is complete. But I can't help giving the following more compact code for dealing with the case where one "insists on using N structs". It assumes their names all start with foo, and end in a number, eg. foo1, foo2, foo21, and it will result in the data fields being stacked alphabetically with respect to the foo names.
% first make some data
foo1 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
foo2 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
foo3 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
foo4 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
Now we get a list of the variables in the workspace that start with foo and end in some number, and put them into a cell, and then we eval the comma delimited concatenation of this cell, setting foo to be this value.
varstarter = 'foo';
varlist = who('-regexp', ['^' varstarter '[0-9]+']);
try
eval([varstarter '=[' sprintf('%s,',varlist{:}) '];']);
catch
warning('Could not concatenate variables starting with "%s".',varstarter);
end
This lets us use @Andrey's answer directly:
data1 = cat(1, foo.data1);
data2 = cat(1, foo.data2);
data3 = cat(1, foo.data3);
Edit (addendum): You can automate this last step a bit more by looping through the field names of foo and assigning variables of the same name to the concatination. If you want different names, the code's not hard to modify. Also maybe add a check to make sure the thing actually has fields.
if ~isstruct(foo)
warning('Variable "%s" is of an unexpected type.',varstarter);
else
varfields = fieldnames(foo);
for k=1:length(varfields)
eval([varfields{k} '=cat(1, ' varstarter '.' varfields{k} ');']);
end
end
In the spirit of avoiding explicit loops because it's fun, here is an equivalent code for the bit inside of the else statement:
varfields = repmat(fieldnames(foo)',2,1);
eval(sprintf(['%s=cat(1,' varstarter '.%s);'],varfields{:}));
Upvotes: 2
Reputation: 20915
The best way is to define the structures as array of structures beforehand:
a(1) = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a(2) = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a(3) = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a(4) = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
Which allows you to get the data quite easily:
cat(1,a.data1)
But if you insist on using N structs, then try this:
function so3
a1 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a2 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a3 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
a4 = struct('data1',rand(12,2),'data2',rand(12,2),'data3',rand(12,3));
s{1} = struct2cell(a1);
s{2} = struct2cell(a2);
s{3} = struct2cell(a3);
s{4} = struct2cell(a4);
N = numel(fieldnames(a1));
data = cell([1 N]);
for i=1:N
data{i} = cell2mat(cellfun(@(x){x{i}'},s));
end
end
Upvotes: 7