views:

325

answers:

3

I want to combine two structures with differing fields names.

For example, starting with:

A.field1 = 1;
A.field2 = 'a';

B.field3 = 2;
B.field4 = 'b';

I would like to have:

C.field1 = 1;
C.field2 = 'a';
C.field3 = 2;
C.field4 = 'b';

Is there a more efficient way than using "fieldnames" and a for loop?

EDIT: Let's assume that in the case of field name conflicts we give preference to A.

A: 

In C, a struct can have another struct as one of it's members. While this isn't exactly the same as what you're asking, you could end up either with a situation where one struct contains another, or one struct contains two structs, both of which hold parts of the info that you wanted.

psuedocode: i don't remember the actual syntax.

A.field1 = 1;
A.field2 = 'a';
A.field3 = struct B;

to access: A.field3.field4;

or something of the sort.

Or you could have struct C hold both an A and a B:

C.A = struct A;
C.B = struct B;

with access then something like

C.A.field1;
C.A.field2;
C.B.field3;
C.B.field4;

hope this helps!

EDIT: both of these solutions avoid naming collisions.

Also, I didn't see your matlab tag. By convention, you should want to edit the question to include that piece of info.

pbh101
+5  A: 

Without collisions, you can do

M = [fieldnames(A)' fieldnames(B)'; struct2cell(A)' struct2cell(B)'];
C=struct(M{:});

And this is reasonably efficient. However, struct errors on duplicate fieldnames, and pre-checking for them using unique kills performance to the point that a loop is better. But here's what it would look like:

M = [fieldnames(A)' fieldnames(B)'; struct2cell(A)' struct2cell(B)'];

[tmp, rows] = unique(M(1,:), 'last');
M=M(:, rows);

C=struct(M{:});

You might be able to make a hybrid solution by assuming no conflicts and using a try/catch around the call to struct to gracefully degrade to the conflict handling case.

SCFrench
A: 

I don't think you can handle conflicts well w/o a loop, nor do I think you'd need to avoid one. (although I suppose efficiency could be an issue w/ many many fields...)

I use a function I wrote a few years back called setdefaults.m, which combines one structure with the values of another structure, where one takes precedence over the other in case of conflict.

% SETDEFAULTS sets the default structure values 
%    SOUT = SETDEFAULTS(S, SDEF) reproduces in S 
%    all the structure fields, and their values,  that exist in 
%    SDEF that do not exist in S. 
%    SOUT = SETDEFAULTS(S, SDEF, OVERRIDE) does
%    the same function as above, but if OVERRIDE is 1,
%    it copies all fields of SDEF to SOUT.

function sout = setdefaults(s,sdef,override)
if (not(exist('override','var')))
    override = 0;
end

sout = s;
for f = fieldnames(sdef)'
    cf = char(f);
    if (override | not(isfield(sout,cf)))
        sout = setfield(sout,cf,getfield(sdef,cf));
    end
end

Now that I think about it, I'm pretty sure that the "override" input is unnecessary (you can just switch the order of the inputs) though I'm not 100% sure of that... so here's a simpler rewrite (setdefaults2.m):

% SETDEFAULTS2 sets the default structure values 
%    SOUT = SETDEFAULTS(S, SDEF) reproduces in S 
%    all the structure fields, and their values,  that exist in 
%    SDEF that do not exist in S. 

function sout = setdefaults2(s,sdef)
sout = sdef;
for f = fieldnames(s)'
    sout = setfield(sout,f{1},getfield(s,f{1}));
end

and some samples to test it:

>> S1 = struct('a',1,'b',2,'c',3);
>> S2 = struct('b',4,'c',5,'d',6);
>> setdefaults2(S1,S2)

ans = 

    b: 2
    c: 3
    d: 6
    a: 1

>> setdefaults2(S2,S1)

ans = 

    a: 1
    b: 4
    c: 5
    d: 6
Jason S