0

Everytime I try editing arrays of structs by field, I discover I really need to take a few weeks and try to really learn Matlab. Right now, I have an array of structs, where each struct has fields along the line of:

x.fruit, x.color, x.season, x.source, x.flibbertigibbet

each of these fields is a string. I also have an cell array of strings:

y = {'apple', 'banana', 'palm of granite'}

I would like to remove all struct where x.fruit is in y (e.g. x.fruit == 'apple'), but can't seem to find a way to do this other than by looping through y.

I was hoping for something along the lines of:

bad_idx = [x(:).fruit in y];
x(bad_idx) = [];

Is this doable? Is there someway to use cellfun to do this?

Suever
  • 64,497
  • 14
  • 82
  • 101
user1245262
  • 6,968
  • 8
  • 50
  • 77

1 Answers1

1

If each element of x only contains a string for the fruit field, you can easily do this the following way.

toremove = ismember({x.fruit}, 'apple')
x(toremove) = [];

Or more briefly

x = x(~ismember({x.fruit}, 'apple'));

The {x.fruit} syntax combines all of the values of fruit for each struct into a cell array. You can then use ismember on the cell array of strings to compare each one to 'apple'. This will yield a logical array the size of x that can be used to index into x.

You could also use something like strcmp instead of ismember above.

x = x(~strcmp({x.fruit}, 'apple'));

Update

If each x(k).fruit contains a cell array, then you can use an approach similar to the above approach combined with cellfun.

x(1).fruit = {'apple', 'orange'};
x(2).fruit = {'banana'};
x(3).fruit = {'grape', 'orange'};

x = x(~cellfun(@(fruits)ismember('apple', fruits), {x.fruit}));

%// 1 x 2 struct array with fields: 
%//     fruit

If you want to check for multiple types of fruit to remove at once, you could do something like.

%// Remove if EITHER 'apple' or 'banana'
tocheck = {'apple', 'banana'};
x = x(~cellfun(@(fruits)any(ismember({'apple', 'banana'}, fruits)), {x.fruit}));

%// Remove if BOTH 'apple' and 'banana' in one
x = x(~cellfun(@(fruits)all(ismember({'apple', 'banana'}, fruits)), {x.fruit}));
Suever
  • 64,497
  • 14
  • 82
  • 101
  • These methods work fine for a single fruit, but I have a list of them. Do I need to loop through the fruits contained in my cell array (i.e. y), removing each one from x, or is there a more 'Matlab-y' way to do this with an implicit loop? – user1245262 May 17 '16 at 16:55
  • @user1245262 Updated to include that use case. – Suever May 17 '16 at 16:57