Updating one field in every element of a Matlab struct array

12,669

Solution 1

Just a note, the deal isn't necessary there:

[arr.val] = newVals{:}; % achieves the same as deal(newVals{:})

The only other way I know how to do this (without the foor loop) is using arrayfun to iterate over each struct in the array:

% make a struct array
arr = [ struct('val',0,'id',1), struct('val',0,'id',2), struct('val',0,'id',3) ]

% some attempts
[arr.val]=arr.val; % fine
[arr.val]=arr.val+3; % NOT fine :(

% works !
arr2 = arrayfun(@(s) setfield(s,'val',s.val+3),arr)

That last command loops over each struct in arr and returns a new one where s.val has been set to s.val=3.

I think this is actually less efficient than your previous two-liner and the for loop though, because it returns a copy of arr as opposed to operating in-place.

(It's a shame Matlab doesn't support layered indexing like [arr.val]=num2cell([arr.val]+3){:}).

Solution 2

I like Carl's and mathematical.coffee's original ideas. I have multiple similar lines to express, so for concision of my mainline code, I went ahead and made the generic subfunction

function varargout = clist(in)
varargout = {in{:}};
end

then I could express each such line in a fairly readable way

[arr.var]  = clist(num2cell([arr.var]+3));  
[arr.var2] = clist(num2cell([arr2.var]/5+33));  

Solution 3

Are all the fields in that struct scalar, or the same size? If so, the idiomatic Matlab way to do this is to rearrange your struct to be a scalar struct with arrays in each of its fields, instead of an array of structs with scalar values in the fields. Then you can do vectorized operations on the fields, like arr.val = arr.val + 3;. See if you can rearrange your data. Doing it this way is much more efficient in both time and memory; that's probably why Matlab doesn't provide convenient syntax for operating over fields of arrays of structs.

Share:
12,669

Related videos on Youtube

Carl
Author by

Carl

Updated on June 03, 2022

Comments

  • Carl
    Carl almost 2 years

    Suppose I have a struct array arr, where each element has a bunch of fields, including one called val. I'd like to increment each element's val field by some constant amount, like so:

    for i = 1:length(arr)
        arr(i).val = arr(i).val + 3;
    end
    

    This obviously works, but I feel there should be a way to do this in just one line of code (and no for loop). The best I've come up with is two lines and requires a temp variable:

    newVals = num2cell([arr.val] + 3);
    [arr.val] = deal(newVals{:});
    

    Any ideas? Thanks.

  • Carl
    Carl about 12 years
    Thanks for the tip on deal. I didn't know about setfield, so that appears to do it in one line, but as you say, this is certainly worse than the for-loop solution. As for indexing like that, I looked into it a while ago; basically, Mathworks claims that supporting anything like that will force compatibility-breaking changes to the parser. Which is a shame, as it bugs me almost every time I write any Matlab code.
  • Berk U.
    Berk U. over 10 years
    Thanks for this answer! In MATLAB 2013b, [arr.val] = newVals{:} works BUT arr.val = newVals{:}. What exactly do the brackets do in this case?
  • Adriaan
    Adriaan over 8 years
    arrayfun is just a wrapper for a for loop in MATLAB, so technically you're still using loops, albeit in disguise.
  • Luca Citi
    Luca Citi about 8 years
    One situation where deal is useful with a struct array is when you want to assign the same value to a field of each element of the struct array. For example if v = 9, you can write [arr.val] = deal(v); to assign 9 to the field val of each element of the struct array arr.
  • user2305193
    user2305193 over 6 years
    [arr.val] = newVals{:}; works, but [arr.val] = newVals(:); doesn't - that is if newVals is a vector and not a cellarray. Can someone explain?!