Loren on the Art of MATLAB

October 15th, 2009

Concatenating structs

From time to time, I get asked or see queries about how to concatenate two struct arrays to merge the fields. There's even a section in the documentation covering this topic. I thought I'd show it here to help people out.

Contents

Example Data

Suppose I've got some system for which I have collected information on a couple of individuals, including their names and ages.

s1.name = 'fred';
s1.age = 42;
s1(2).name = 'alice';
s1(2).age = 29;

Later, I go back and collect the individual's heights (in cm).

s2.height = 170;
s2(2).height = 160;

It would be great to merge these arrays now.

s1
s2
s1 = 
1x2 struct array with fields:
    name
    age
s2 = 
1x2 struct array with fields:
    height

Let me collect the field names.

fn1 = fieldnames(s1);
fn2 = fieldnames(s2);
fn = [fn1; fn2];

Next, I ensure the fieldnames are unique.

ufn = length(fn) == unique(length(fn))
ufn =
     1

Next let me convert the data in my structs into cell arrays using struct2cell.

c1 = struct2cell(s1)
c2 = struct2cell(s2)
c1(:,:,1) = 
    'fred'
    [  42]
c1(:,:,2) = 
    'alice'
    [   29]
c2(:,:,1) = 
    [170]
c2(:,:,2) = 
    [160]

Next I merge the data from the 2 cell arrays.

c = [c1;c2]
c(:,:,1) = 
    'fred'
    [  42]
    [ 170]
c(:,:,2) = 
    'alice'
    [   29]
    [  160]

And finally, I construct the new struct using cell2struct.

s = cell2struct(c,fn,1)
s = 
1x2 struct array with fields:
    name
    age
    height

I can check the output now.

s(1)
s(2)
ans = 
      name: 'fred'
       age: 42
    height: 170
ans = 
      name: 'alice'
       age: 29
    height: 160

mergeStruct

Here's the function mergeStruct that I created to encapsulate the steps in this process, including some error checks.

dbtype mergeStruct
1     function sout = mergestruct(varargin)
2     %MERGESTRUCT Merge structures with unique fields.
3     
4     %   Copyright 2009 The MathWorks, Inc.
5     
6     % Start with collecting fieldnames, checking implicitly 
7     % that inputs are structures.
8     fn = [];
9     for k = 1:nargin
10        try
11            fn = [fn ; fieldnames(varargin{k})];
12        catch MEstruct
13            throw(MEstruct)
14        end
15    end
16    
17    % Make sure the field names are unique.
18    if length(fn) ~= length(unique(fn))
19        error('mergestruct:FieldsNotUnique',...
20            'Field names must be unique');
21    end
22    
23    % Now concatenate the data from each struct.  Can't use 
24    % structfun since input structs may not be scalar.
25    c = [];
26    for k = 1:nargin
27        try
28            c = [c ; struct2cell(varargin{k})];
29        catch MEdata
30            throw(MEdata);
31        end
32    end
33    
34    % Construct the output.
35    sout = cell2struct(c, fn, 1);

Do You Need to Merge struct data?

Do you ever need to merge data stored in structs when you gather new information? Or are your structs static in nature? If the information you collect is always the same sort of information, then you probably know the form of the structure when you start, even if all the data isn't available at first.

There is code on the File Exchange to concatenate structures; I confess I have not tried it, but it is highly rated. Does this post or the File Exchange contribution help your work? Let me know here.


Get the MATLAB code

Published with MATLAB® 7.9

5 Responses to “Concatenating structs”

  1. Ben replied on :

    My team has used just this idea for some time now. For a while, we had our own function named merge_structs (note the underscore :-)) but we replaced our usage of it with an undocumented function that (I think) does the same thing, called setstructfields. Doing a quick find on my system, I see that the file is in this path in my MATLAB installation: $matlabroot/toolbox/signal/sigtools/setstructfields.m Any chance this could be moved into “supported and documented” side of things?

  2. Loren replied on :

    Ben-

    I recommend you use the link on the right to make the suggestion to Technical Support.

    –Loren

  3. Pete Scotson replied on :

    Loren,

    Yes, I do need to merge structures. I have tried and customised the functions on file exchange but I usually want to merge structures in a very specific way with custom error handling etc.

    Mostly I use structures as “contents addressable” containers for data. Such a format makes finding the record easy using dynamic field names in code, and works well for keyboard work (with tab completion).

    tmp.age       = 42;
    tmp.height    = 170;
    mystruc.fred  = tmp;
    
    tmp.age       = 29;
    tmp.height    = 160;
    mystruc.alice = tmp;
    
    ...
    
    name='alice';
    mystruc.(name).age
    

    I rarely want to do statistical analysis across the records because I usually want to do computations on the data within the individual record. Structfun can be used to get the items such as the ages into an array.

    Regards,
    Pete

  4. A different Ben replied on :

    Hi Loren,

    I’ve got a slight variation on the merger you’ve presented - I want to use a struct like a linked list for merger.

    In your example, the heights in s2 are in the same order as the entries of s1. In my problem, I may not know the order of the second structure, and want to use one value as an index to add to the first structure. That is, given a structure that is like this:

    s1.name = 'fred';
    s1.age = 42;
    s1(2).name = 'alice';
    s1(2).age = 29;
    

    and a second structure like this:

    s2.name = 'alice';
    s2.height = 160;
    s2(2).name = 'fred';
    s2(2).height = 170;
    

    how can I use the name to find the appropriate height to add from s2 to the correct substructure of s1? I ‘d like to end up with a structure like this:

    s1.name = 'fred';
    s1.age = 42;
    s1.height = 170;
    s1(2).name = 'alice';
    s1(2).age = 29;
    s1(2).height = 160;
    

    Is there an easy way to do such a thing?

  5. Loren replied on :

    Hello A Different Ben!

    There is nothing ready to go for you, but there are some tools that should make your life easier. ismember is the main one that occurs to me now.

    Here I sketch my thought process in how to do this. It might not be the most efficient, but it has all the right steps, I think.

    Either know which fields are in common, e.g., .name, or write a little code to find the overlaps. For the overlap ones, you need to get the values for both the first and second structs, and you can then use ismember with multiple outputs to figure out which locations in struct2 match the list in struct1. You can then reorder the struct2 values according to the ismember match indices. You can then delete the matching field (e.g., name) from struct. And finally, merge struct2 into struct1.

    Good luck!

    –Loren

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Loren Shure works on design of the MATLAB language at The MathWorks. She writes here about once a week on MATLAB programming and related topics.

  • Jun: I totally can not believe it, Loren. You are really helpful. Thank you so much, MATLAB master!
  • Loren: Wow folks- Always lots of interest when there’s a quickie to try out! I will only make 2 general...
  • Loren: Jun- ismember is your friend here: >> [aa,ind] = ismember(Array2,Arra y1) aa = 1 1 1 1 1 1 1 ind = 1 2 1 4 4 3...
  • Dan: I like the first way better than the second way. Combining the arrays into one and running any is nice, although...
  • James Myatt: How about I = (a == 0 | b == 0); a(I) = []; b(I) = [];
  • Tunc: Hello Loren, love your blog because of such inspiring and challenging comments to such ’small’...
  • Pekka Kumpulainen: Here is my tradeoff. I usually want to keep the original variables as they are most probably...
  • Iain: Followup: Of course, to allow NaNs (counting them as non-zero): mask = (a~=0) & (b~=0); The mask says “a...
  • Matt Fig: I would usually go with something like this: y = a&b; x = a(y); y = b(y); But I was surprised to find...
  • kk: c=all([a;b]) a(c) a(b)

These postings are the author's and don't necessarily represent the opinions of The MathWorks.