What are some efficient ways to combine two structures in MATLAB?

前端 未结 5 1700
我在风中等你
我在风中等你 2020-11-29 11:01

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 = \         


        
相关标签:
5条回答
  • 2020-11-29 11:32

    Short answer: setstructfields (if you have the Signal Processing Toolbox).


    The official solution is posted by Loren Shure on her MathWorks blog, and demonstrated by SCFrench here and in Eitan T's answer to a different question. However, if you have the Signal Processing Toolbox, a simple undocumented function does this already - setstructfields.

    help setstructfields

     setstructfields Set fields of a structure using another structure
        setstructfields(STRUCTIN, NEWFIELDS) Set fields of STRUCTIN using
        another structure NEWFIELDS fields.  If fields exist in STRUCTIN
        but not in NEWFIELDS, they will not be changed.
    

    Internally it uses fieldnames and a for loop, so it is a convenience function with error checking and recursion for fields that are themselves structs.

    Example

    The "original" struct:

    % struct with fields 'color' and 'count'
    s = struct('color','orange','count',2)
    
    s = 
        color: 'orange'
        count: 2
    

    A second struct containing a new value for 'count', and a new field, 'shape':

    % struct with fields 'count' and 'shape'
    s2 = struct('count',4,'shape','round')
    
    s2 = 
        count: 4
        shape: 'round'
    

    Calling setstructfields:

    >> s = setstructfields(s,s2)
    s = 
        color: 'orange'
        count: 4
        shape: 'round'
    

    The field 'count' is updated. The field 'shape' is added. The field 'color' remains unchanged.

    NOTE: Since the function is undocumented, it may change or be removed at any time.

    0 讨论(0)
  • 2020-11-29 11:34

    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
    
    0 讨论(0)
  • 2020-11-29 11:42

    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.

    0 讨论(0)
  • 2020-11-29 11:45

    I have found a nice solution on File Exchange: catstruct.

    Without testing the performance I can say that it did exactly what I wanted. It can deal with duplicate fields of course.

    Here is how it works:

    a.f1 = 1;
    a.f2 = 2;
    b.f2 = 3;
    b.f4 = 4;
    
    s = catstruct(a,b)
    

    Will give

    s = 
    
        f1: 1
        f2: 3
        f3: 4
    
    0 讨论(0)
  • 2020-11-29 11:50

    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.

    0 讨论(0)
提交回复
热议问题