6
votes

I have a nested structure array t in the format of t.a.b = value, where and a and b are just random strings. t can have an arbitrary number of a's as field names and each a can have an arbitrary number of b's as field names. I need to make a copy of t called x, but set all x.a.b = 0. In addition I need to make another structure array in the form of y.a = 0 for all a in t. Right now I'm using a nested for loop solution but it is too slow if there are too many a's and b's. Can someone tell me if there's any way to vectorize this nested loop or any other operation in this code as to make this code run faster? Thanks.

names1 = fieldnames(t);
x = t;
y = {};
for i=1:length(names1)
  y.(names1{i}) = 0;
  names2 = fieldnames(x.(names1{i}));
  for j=1:length(names2)
      x.(names1{i}).(names2{j}) = 0;
  end
end 

Sample:

if t is such that
t.hello.world = 0.5
t.hello.mom = 0.2
t.hello.dad = 0.8
t.foo.bar = 0.7
t.foo.moo = 0.23
t.random.word = 0.38

then x should be:
x.hello.world = 0
x.hello.mom = 0
x.hello.dad = 0
x.foo.bar = 0
x.foo.moo = 0
x.random.word = 0

and y should be:
y.hello = 0
y.foo = 0
y.random = 0
2
Could you give some example inputs with corresponding outputs? And give an indication of what you mean by 'many' and 'slow'? - Dennis Jaheruddin
Many can be up to tens of thousands of the combination t.a.b and slow as in hours. - user2017502
Fixed typo in the code, at first sight the only thing that I notice is that you did not pre-allocate y, not sure how much the impact of that would be. Could you run it for a few mins and give the profiler results? - Dennis Jaheruddin
Sorry, which code are you talking about? I don't see any code to run. - user2017502

2 Answers

2
votes

You can get rid of all the loops by doing using structfun

function zeroed = always0(x)
  zeroed = 0;
endfunction

function zeroedStruct = zeroSubfields(x)
   zeroedStruct = structfun(@always0, x, 'UniformOutput', false);
endfunction

y = structfun(@always0, t, 'UniformOutput', false);
x = structfun(@zeroSubfields, t, 'UniformOutput', false); 

If you had arbitrary nesting of fields zeroSubfields could be extended to

function zeroedStruct = zeroSubfields(x)
   if(isstruct(x))
     zeroedStruct = structfun(@zeroSubfields, x, 'UniformOutput', false);
   else
     zeroedStruct = 0;
   endif
endfunction
0
votes

To construct y, you can do something like this:

>> t.hello.world = 0.5;
>> t.hello.mom = 0.2;
>> t.hello.dad = 0.8;
>> t.foo.bar = 0.7;
>> t.foo.moo = 0.23;
>> t.random.word = 0.38;
>> f = fieldnames(t);
>> n = numel(f);
>> fi = cell(1,n*2);
>> fi(1:2:(n*2-1)) = f;
>> fi(2:2:(n*2)) = num2cell(zeros(n,1))
fi = 
    'hello'    [0]    'foo'    [0]    'random'    [0]
>> y = struct(fi{:})
y = 
     hello: 0
       foo: 0
    random: 0

Basically you're just getting the fieldnames, interleaving them with zeros in a cell array, and then directly constructing the structure with a comma-separated list of fieldnames and values, from that cell array.

For x, I'm afraid you would still need to loop over the first level of fieldnames, I think. But you should be able to do something similar to the above within each loop iteration.