Merge structures with subfields

23 views (last 30 days)
Jan
Jan on 10 Jan 2015
Commented: Jan on 13 Jan 2015
Hi guys,
I have multiple structures (around 50, with equal fieldnames). Some fields go up to three levels deep (s.a.b.c). I want to concatenate all the fields. So suppose I have:
s1.time = [240x1 double]
s1.a.b.c = [240x3 double]
s1.a.b.d = [240x3 double]
s2.time = [120x1 double]
s2.a.b.c = [120x3 double]
s2.a.b.d = [120x3 double]
I want to achieve:
s3.time = [360x1double]
s3.a.b.c = [360x3 double]
s3.a.b.d = [360x3 double]
In s3 the time should be increasing while in s1 and s2 the time starts at 0 everytime. I wrote the following which works up till 3 levels but I have the idea this could be done much simpler..
function [ out ] = Concatenate_3rd_level_structs( in , timename)
%UNTITLED Summary of this function goes here
% Detailed explanation goes here
fieldsL1 = fieldnames(in); % Level 1 fields
out = in(1);
for i = 2:length(in)
for j = 1:length(fieldsL1)
if strcmp(fieldsL1{j},timename)
out.(timename) = [out.(timename); in(i).(timename)+out.(timename)(end)+out.(timename)(2)]; % For the time vector create an increasing signal
else
if isstruct(in(i).(fieldsL1{j}))
fieldsL2 = fieldnames(in(i).(fieldsL1{j}));
else
fieldsL2 = [];
end
if ~isempty(fieldsL2) % In case of a level 2 nested structure
for jj = 1:length(fieldsL2)
if isstruct(in(i).(fieldsL1{j}).(fieldsL2{jj}))
fieldsL3 = fieldnames(in(i).(fieldsL1{j}).(fieldsL2{jj}));
else
fieldsL3 = [];
end
if ~isempty(fieldsL3)% In case of a level 3 nested structure
for jjj = 1:length(fieldsL3)
out.(fieldsL1{j}).(fieldsL2{jj}).(fieldsL3{jjj}) = ...
[ out.(fieldsL1{j}).(fieldsL2{jj}).(fieldsL3{jjj}); in(i).(fieldsL1{j}).(fieldsL2{jj}).(fieldsL3{jjj})];
end
else
out.(fieldsL1{j}).(fieldsL2{jj}) = [out.(fieldsL1{j}).(fieldsL2{jj}); in(i).(fieldsL1{j}).(fieldsL2{jj})] ;
end
end
else
out.(fieldsL1{j}) = [out.(fieldsL1{j}); in(i).(fieldsL1{j})] ;
end
end
end
end
end
  1 Comment
Jan
Jan on 12 Jan 2015
Ok maybe I should rephrase my problem a bit, I'm looking for a way to index nested fields in a structure. Suppose I have s.a.b.c
Is there a smart way without for-loopt to index these structures?

Sign in to comment.

Accepted Answer

Guillaume
Guillaume on 12 Jan 2015
Edited: Guillaume on 13 Jan 2015
You'd have to use a loop for sure. As Titus say, I'd also use recursivity. What I would also do inside the recursive bit is operate on cell arrays of scalar structures instead of a structure array
function out = CatStructByTime(in ,timename)
%in: a structure array with at least one field called 'timename'
for field = fieldnames(in)'
field = field{1};
if strcmp(field, timename)
reftime = {in(:).(timename)}';
offsets = num2cell(cumsum([0; cellfun(@(tv) sum(tv([2 end])), reftime(1:end-1))]));
out.(field) = cell2mat(cellfun(@(tv, o) tv+o, reftime, offsets, 'UniformOutput', false));
else
out.(field) = CatStructRecurse(arrayfun(@(s) s.(field), in, 'UniformOutput', false));
end
end
end
function out = CatStructRecurse(sc)
%sc: a cell array of scalar structures
if isstruct(sc{1})
for field = fieldnames(sc{1})'
field = field{1};
out.(field) = CatStructRecurse(cellfun(@(s) s.(field), sc, 'UniformOutput', false));
end
else
out = vertcat(sc{:});
end
end
  3 Comments
Guillaume
Guillaume on 13 Jan 2015
Hum, the code I've attached works with any number of levels of nested structure. Have you tried it?
%generate demo data:
s(1) = struct('time', [0:239]', 'a', struct('b', struct('c', reshape(1:240*3, 240, 3), 'd', -reshape(1:240*3, 240, 3))));
s(2) = struct('time', [0:119]', 'a', struct('b', struct('c', 1000+reshape(1:119*3, 119, 3), 'd', -1000-reshape(1:119*3, 119, 3))));
s(3) = struct('time', [0:59]', 'a', struct('b', struct('c', 2000+reshape(1:59*3, 59, 3), 'd', -2000-reshape(1:59*3, 59, 3))));
s(1).a.very.deeply.nested.field.in.the.structure = [1 2 3];
s(2).a.very.deeply.nested.field.in.the.structure = [4 5 6];
s(3).a.very.deeply.nested.field.in.the.structure = [7 8 9];
news = CatStructByTime(s, 'time');
news =
time: [420x1 double]
a: [1x1 struct]
news.a.very.deeply.nested.field.in.the.structure
ans =
1 2 3
4 5 6
7 8 9
Note, there was a small bug in the code which I've fixed.
Jan
Jan on 13 Jan 2015
Works like a charm! Thanks!

Sign in to comment.

More Answers (1)

Titus Edelhofer
Titus Edelhofer on 12 Jan 2015
Edited: Titus Edelhofer on 12 Jan 2015
Hi Jan,
it probably might be too easy (that's why I don't try here ;-)), but my guess is, that with a recursive call you might be better off.
It could be though that you have to carry the time vector down the levels with you, i.e., instead of recursively calling your function with in.(fieldnames{1}), you will create an intermediate structure
inIntermediate = struct(timename, in.(timename), fieldnames{1}, in.(fieldnames{1}));
which then is used to call the function recursively.
Maybe this helps...
Titus

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!