2

Suppose I have the following struct in Matlab (read from a JSON file):

>>fs.
fs.dichte              fs.hoehe               fs.ts2                 
fs.temperatur          fs.ts3                 fs.viskositaet
fs.ts1                 fs.ts4

Each one of the fs.ts* components contains another struct. In this particular case, the index of ts goes from 1 to 4, but in another case it could as well be 2 or 7. You get the idea, right? I want the program to be flexible enough to handle any possible input. So my question comes down to: How to query the maximum index of ts? In an ideal world, this would work:

who fs.ts*

But unfortunately, it just returns nothing. Any ideas?

(Btw, I'm using Octave and don't have Matlab available for testing; however, there should really be a solution to this which works in both environments.)

Suever
  • 64,497
  • 14
  • 82
  • 101
winkmal
  • 622
  • 1
  • 6
  • 16
  • 1
    Not an answer to your exact question but sounds like instead of `tsN` fields, you should have a single `ts` field with a list. Tip: every time you see a number in a variable or field name, think whether you shouldn't be using a vector/array/list instead. – carandraug Aug 29 '16 at 12:58
  • Great idea; I've been using JSON only for a short time, so I didn't think of that solution. So I replaced my four `tsN` by an array of objects called `ts`. It is correctly parsed by `loadjson`. But how can I access the substructures? `fs.ts(1)` works and outputs the structure, which contains more variables, e.g. `laenge` and `hoehe`. But `fs.ts(1).laenge` gives: `error: cell cannot be indexed with .` – winkmal Aug 29 '16 at 13:23
  • 1
    You need to use curly brackets when indexing a element from a cell array. If you use parentheses, then you get a back a cell array slice. So use `fs.ts{N}` which returns element N of the cell array, instead of `fs.ts(N)` which returns a cell array with the element N. – carandraug Aug 29 '16 at 13:29
  • Right, this way it works. Why didn't I think of that myself? :-s What's confusing me though is the fact that, although `fieldnames(fs.ts{1})` renders the correct result, autocompletion doesn't work here, so if I type `fs.ts{1}.` at the interactive prompt and press TAB, the variables are not listed (the output gives `./ ../ .nargin.`). Is this not yet implemented in Octave GUI, or a general lack of the language? Does Matlab act the same way? – winkmal Aug 29 '16 at 13:58
  • Octave relies on readline for autocompletion. What you suggest is a possible improvement to Octaves readline configuration. Please suggest that in the [Octave bug tracker](https://savannah.gnu.org/bugs/?func=additem&group=octave). – carandraug Aug 29 '16 at 14:16

2 Answers2

3

You can use fieldnames to get all field names of the struct, then use regexp to extract the ones that start with ts and extract the number. Then you can compare the numbers to find the largest.

fields = fieldnames(fs);

number = str2double(regexp(fields, '(?<=^ts)\d+$', 'once', 'match'));
numbers = number(~isnan(number));

[~, ind] = max(number);
max_field = fields{ind};
max_value = fs.(max_field);
Suever
  • 64,497
  • 14
  • 82
  • 101
  • Thanks! This definitely helped me out. I slightly modified your approach, since in the next step, I'm looping over `tsN`: `numberOfParts = str2double(regexp(fields, '(?<=^ts)\d+$', 'once', 'match'))';` `numberOfParts = numberOfParts(~isnan(numberOfParts(1, :)))` (from the comments of [this answer](http://stackoverflow.com/a/5202703). Then I can do `for i = numberOfParts` and so on... – winkmal Aug 29 '16 at 13:39
  • @rotton I incorporated this into the answer. Consider marking this as the solution if it worked for you. – Suever Aug 29 '16 at 13:43
2

Not an answer to your exact question but sounds like instead of tsN fields, you should have a single ts field with a list.

Tip: every time you see a number in a variable or field name, think whether you shouldn't be using a vector/array/list instead.

This is true for all languages but more so for Octave since everything is arrays. Even if you have three field named ts1, ts2, and ts3 with scalars values, what you really have is three fields whose values are an array of size 1x1.

In Octave you can have two things. Either the value of ts is a cell array, each element of the cell array a scalar struct; or is a struct array. Use a cell array of structs when each struct has different keys, use a struct array when all structs have the same keys.

Struct array

octave> fs.ts = struct ("foo", {1, 2, 3, 4}, "bar", {"a", "b", "c", "d"});
octave> fs.ts # all keys/fields in the ts struct array
ans =

  1x4 struct array containing the fields:

    foo
    bar

octave> fs.ts.foo # all foo values in the ts struct array
ans =  1
ans =  2
ans =  3
ans =  4

octave> numel (fs.ts) # number of structs in the ts struct array
ans =  4

octave> fs.ts(1) # struct 1 in the ts struct array
ans =

  scalar structure containing the fields:

    foo =  1
    bar = a

octave> fs.ts(1).foo # foo value of the struct 1
ans =  1

Cell array of scalar structs

However, I'm not sure if JSON supports anything like struct arrays, you will probably need to have a list of structs. In that case, you will end up with a cell array of struct scalars.

octave> fs.ts = {struct("foo", 1, "bar", "a"), struct("foo", 2, "bar", "b"), struct("foo", 3, "bar", "c"), struct("foo", 4, "bar", "d"),};
octave> fs.ts # note, you actually have multiple structs
ans = 
{
  [1,1] =

    scalar structure containing the fields:

      foo =  1
      bar = a

  [1,2] =

    scalar structure containing the fields:

      foo =  2
      bar = b

  [1,3] =

    scalar structure containing the fields:

      foo =  3
      bar = c

  [1,4] =

    scalar structure containing the fields:

      foo =  4
      bar = d

}
octave-gui:28> fs.ts{1} # get struct 1
ans =

  scalar structure containing the fields:

    foo =  1
    bar = a

octave-gui:29> fs.ts{1}.foo # value foo from struct 1
ans =  1
carandraug
  • 12,938
  • 1
  • 26
  • 38
  • Well, although @Suever 's solution solves my specific problem, your answer/comments made me rethink my data structure. So this leads me to several other questions. Should I open a new one then? I have a sample JSON file I could share publicly. Since SX does not let me upload files, I'd need to use pastebin or something similar... – winkmal Aug 29 '16 at 14:12
  • You should create a new question since it sounds like your new issues are very different from this. You shouldn't need to upload files or to use pastebin. You should create a [minimal working example](https://stackoverflow.com/help/mcve) instead. – carandraug Aug 29 '16 at 14:20