Reputation: 1767
I have two small structures that I would like to combine but I can't find the right solution. Struct A and B have one column in common (same string values), although it is important that the common values are not necessarily in the same order between the two. For the sake of example, I put them in a different order. I would like to achieve what is in struct C below, where the info from both structures are organised together:
A(1).condition = 'con1';
A(1).y = rand(10, 1);
A(2).condition = 'con2';
A(2).y = rand(10, 1);
B(1).condition = 'con2';
B(1).z = normrnd(0,1);
B(2).condition = 'con1';
B(2).z = normrnd(0,1);
% Desired output:
C = A;
C(1).z = B(2).z;
C(2).z = B(1).z
Upvotes: 2
Views: 114
Reputation: 60444
You need to:
condition
.B(i)
to the corresponding element of array A
. I'm assuming there are more fields in B
than just z
and condition
.Step 1 can be accomplished using ismember
, whose second output argument gives the indices into one array matching elements in the other. We're assuming that each element of B
has a matching element in A
:
[~,I] = ismember({A.condition},{B.condition});
Note that {A.condition}
is a cell array with all the condition
strings. B(I)
is the struct array B
reordered to match A
.
Step 2 can be accomplished as in rahnema1's answer, which we generalize here to copy multiple fields using a loop over the field names, and using dynamic field names:
fields = fieldnames(B);
fields = setdiff(fields,'condition'); % don't copy over this field
for ii = 1:numel(fields)
[A.(fields{ii})] = B(I).(fields{ii});
end
Testing:
A(1).condition = 'con1';
A(1).y = [1,1,1];
A(2).condition = 'con2';
A(2).y = [2,2];
A(3).condition = 'con3';
A(3).y = [3,3,3];
B(1).condition = 'con2';
B(1).z = 2;
B(1).x = 'b';
B(2).condition = 'con3';
B(2).z = 3;
B(2).x = 'c';
B(3).condition = 'con1';
B(3).z = 1;
B(3).x = 'a';
[~,I] = ismember({A.condition},{B.condition});
fields = fieldnames(B);
fields = setdiff(fields,'condition'); % don't copy over this field
for ii = 1:numel(fields)
[A.(fields{ii})] = B(I).(fields{ii});
end
This yields the following struct A
:
>> A(1)
ans =
struct with fields:
condition: 'con1'
y: [1 1 1]
x: 'a'
z: 1
>> A(2)
ans =
struct with fields:
condition: 'con2'
y: [2 2]
x: 'b'
z: 2
>> A(3)
ans =
struct with fields:
condition: 'con3'
y: [3 3 3]
x: 'c'
z: 3
Upvotes: 2
Reputation: 15837
You can assign a comma separated list
to another comma separated list:
C = A;
[C(2:-1:1).z] = B.z;
or
[C.z] = B(2:-1:1).z;
Upvotes: 1
Reputation: 715
Its not elegant, but it should work:
C = A
for idx_A = 1:length(A)
for idx_B = 1:length(B)
if strcmp(A(idx_A).condition, B(idx_B).condition)
C(idx_A).z = B(idx_B).z
end
end
end
If you need frequent merging operations, you could consider using tables. If you want to stick to structs, I think it is generally advisable to use structs of arrays instead of arrays of structs, like so:
A.condition = {'con1', 'con2'};
A.y = [rand(10, 1), rand(10, 1)]
That makes many operations easier.
Upvotes: 0