3

Here’s my initial structure array:

A(1).B.C = 'a';
A(2).B.C = 'b';
A(3).B.C = 'a';
A(4).B.C = 'a';

I want to change the values of field C based on values of Values and indexes of IndexingArray:

Values = {'a', 'b'};
IndexingArray = [1 1 0 1];

So, my new structure array will be:

A(1).B.C = 'b';
A(2).B.C = 'b';
A(3).B.C = 'a';
A(4).B.C = 'b';

Is there a way to do it without using a for loop?

gnovice
  • 125,304
  • 15
  • 256
  • 359
  • this is not even remotely a signal processing question. I think you meant to ask this on StackOverflow, and ask your phase-related StackOverflow question here! – Marcus Müller Mar 23 '17 at 22:16
  • 1
    I can't think a way of doing this without a for loop.... Is there any reason why a for loop does not work for you? This are not matrices, thus you won't find much improvement by avoiding a for loop (if possible). – Ander Biguri Mar 28 '17 at 12:08

2 Answers2

3

You can do this without loops using deal and comma separated list syntax, but it might look a little harder to read:

% Initialize A:
A(1).B.C = 'a';
A(2).B.C = 'b';
A(3).B.C = 'a';
A(4).B.C = 'a';
Values = {'a', 'b'};
IndexingArray = [1 1 0 1];

temp = [A.B];           % Structure array containing B substructures
[temp.C] = deal(Values{IndexingArray+1});  % Set field C of each structure element
temp = num2cell(temp);  % Convert to a cell array of 1-by-1 structures
[A.B] = deal(temp{:});  % Update B substructures

And in newer versions of MATLAB, you can omit deal altogether:

temp = [A.B];
[temp.C] = Values{IndexingArray+1};
temp = num2cell(temp);
[A.B] = temp{:};

This should allow you to update the B and C fields without affecting any other fields that might exist in your more complicated structure.

gnovice
  • 125,304
  • 15
  • 256
  • 359
1

Instead of changing the value, it is simpler to rebuild the structure array.

  • Get value by IndexingArray:

    val = Values(IndexingArray+1);
    
  • Build array of structures using cell2struct, and convert to cell array using num2cell:

    T = num2cell(cell2struct(val, {'C'}, 1));
    
  • Convert T result to array of structures using cell2struct:

    A = cell2struct(T', {'B'}, 1);
    

Here is the code sample to create A:

Values = {'a', 'b'};
IndexingArray = [1 1 0 1];

val = Values(IndexingArray+1);

T = num2cell(cell2struct(val, {'C'}, 1));

A = cell2struct(T', {'B'}, 1);

Building A in a single line of code:

A = cell2struct((num2cell(cell2struct(Values(IndexingArray+1), {'C'}, 1)))', {'B'}, 1);

Result (for testing):

>> A(1).B.C

ans =

b

>> A(2).B.C

ans =

b

>> A(3).B.C

ans =

a

>> A(4).B.C

ans =

b

Solution using arrayfun:

val = Values(IndexingArray+1);

A = arrayfun(@(x) struct('B', struct('C', val{x})), 1:4)

Update specific elements of A:

In case you need to update specific elements, instead overwriting A, you can apply arrayfun selectively, to the indexes, you know you need to update.

Example:

Assume A length is 6 elements, and you need to update the first 4, you can use the following code:

A(1:4) = arrayfun(@(x) struct('B', struct('C', val{x})), 1:4);

Assume you know you need to update only A(1) and A(4), you can use the following example:

A(1).B.C = 'a';
A(2).B.C = 'b';
A(3).B.C = 'a';
A(4).B.C = 'a';
A(5).B.C = 'c';
A(6).B.C = 'd';

Values = {'a', 'b'};
IndexingArray = [1 1 0 1];

val = Values(IndexingArray+1);

%List of indices of A to update
indices = [1, 4];

A(indices) = arrayfun(@(x) struct('B', struct('C', val{x})), indices);
Rotem
  • 30,366
  • 4
  • 32
  • 65
  • I tested your last solution, but it overwrites the whole struct A. Isn't there a way to update a specific field, rather than overwriting the struct A? – Rightia Rollmann Mar 26 '17 at 19:53
  • I updated my answer, please let me know if this is what you have meant. – Rotem Mar 27 '17 at 07:38
  • If struct A has more than one field (e.g., B and D), your solution doesn't work because I only want to update field B, but it overwrites the whole. I only want to selectively update fields B and C and not any other fields that struct A or struct B possess – Rightia Rollmann Mar 27 '17 at 19:28
  • I think there is no way to do it without a `for` loop (there could be a way using `eval`). Do you like me do delete my answer (giving others better chance answering)? – Rotem Mar 27 '17 at 19:51
  • Before I delete my answer, can you please revise your answer: Add all the additional constrains you wrote in comments to your post. Mention struct `A` has more than one field (e.g., `B` and `D`). Mention you want to update a specific field. Please give a code sample that reflects all these requirements. Consider posting a solution that uses a for loop to your question... – Rotem Mar 27 '17 at 21:08
  • As far as I know `arrayfun` is a for loop, inside. I doubt this can be done without a for loop! – Ander Biguri Mar 28 '17 at 12:08