diff --git a/ShoulderCase/getTabulatedProperties.m b/ShoulderCase/getTabulatedProperties.m index 8249eac..8075b09 100644 --- a/ShoulderCase/getTabulatedProperties.m +++ b/ShoulderCase/getTabulatedProperties.m @@ -1,147 +1,162 @@ function output = getTabulatedProperties(object, varargin) % Return a table with object properties as variables. % Properties are converted to one table variable if they are scalar string, % numeric, or char array. They are converted to multiple variables if they are % 1xn array of string, numeric, or char array. The corresponding variables have % a "_n" suffix. If the property is a numerical 1x3 array the suffixes are % "_x", "_y", "_z". % % Recursion can be applied on other types of properties if the arguments % ("recursive", true) are given. % % To avoid recursion looping on itself specify the parent objects with the % arguments ("parentObjects", object). Recursion automatically add the new % parents object for each deeper level in nested structures. % % Give table's variable names a prefix and a suffix with the arguments % ("prefix", "text_written_before") and ("prefix", "text_written_after") output = []; parameters = inputParser; isScalarString = @(arg) isstring(arg) & isscalar(arg); isBoolean = @(arg) or(isequal(arg, true), isequal(arg, false)); addOptional(parameters, "recursive", false, isBoolean); addOptional(parameters, "parentObjects", {}); addOptional(parameters, "prefix", "", isScalarString); addOptional(parameters, "suffix", "", isScalarString); + addOptional(parameters, "exportArraysVerticalMean", false, isBoolean); + addOptional(parameters, "arrays1x3AreCoordinatesXYZ", false, isBoolean); + addOptional(parameters, "excludedProperties", "", @isstring); parse(parameters, varargin{:}); parameters = parameters.Results; parameters.parentObjects = [parameters.parentObjects, {object}]; properties = string(fields(object))'; for property = properties data = []; - if not(isscalar(object)) + if not(isscalar(object)) | ismember(property, parameters.excludedProperties) continue end % Avoid infinite loop recursion if isParent(object.(property), parameters.parentObjects) continue end % Recursion if not(isstring(object.(property))... | isnumeric(object.(property))... | ischar(object.(property))... | islogical(object.(property)) ) if parameters.recursive output = [output getTabulatedProperties(object.(property),... "recursive", true,... "parentObjects", parameters.parentObjects,... - "prefix", join([parameters.prefix property], "_"))]; + "prefix", join([parameters.prefix property], "_"),... + "suffix", parameters.suffix,... + "exportArraysVerticalMean", parameters.exportArraysVerticalMean,... + "arrays1x3AreCoordinatesXYZ", parameters.arrays1x3AreCoordinatesXYZ,... + "excludedProperties", parameters.excludedProperties)]; end continue end % Format char to string if ischar(object.(property)) object.(property) = string(object.(property)); + % Remove coma from strings that would corrupt the csv file + object.(property) = replace(object.(property), ",", ""); end % Scalar values are straightforward if isscalar(object.(property)) data.(fieldnameToColumnname(property, parameters.prefix, parameters.suffix)) = ... nanIfEmpty(object.(property)); + else + if parameters.exportArraysVerticalMean + object.(property) = mean(object.(property), 1); + end end - % Value is probably a single point coordinates - if isequal(size(object.(property)), [1 3]) & isnumeric(object.(property)) + % Value is a single point coordinates + if isequal(size(object.(property)), [1 3])... + & isnumeric(object.(property))... + & parameters.arrays1x3AreCoordinatesXYZ data.(fieldnameToColumnname(property, parameters.prefix, parameters.suffix)) = true; point = object.(property); data.(fieldnameToColumnname(property, parameters.prefix, "x_" + parameters.suffix)) = ... nanIfEmpty(point(1)); data.(fieldnameToColumnname(property, parameters.prefix, "y_" + parameters.suffix)) = ... nanIfEmpty(point(2)); data.(fieldnameToColumnname(property, parameters.prefix, "z_" + parameters.suffix)) = ... nanIfEmpty(point(3)); end % Value is a one dimensional horizontal array if (size(object.(property), 1) == 1 &... size(object.(property), 2) > 1 &... size(object.(property), 2) ~= 3) data.(fieldnameToColumnname(property, parameters.prefix, parameters.suffix)) = true; array = object.(property); for i = 1:length(array) data.(fieldnameToColumnname(property, parameters.prefix, sprintf("%d_", i) + parameters.suffix)) = ... nanIfEmpty(array(i)); end end if not(isempty(data)) output = [output struct2table(data)]; end end end function output = fieldnameToColumnname(fieldname, prefix, suffix) % Tries to extract words from a string based on the capitalized letters in the % string. Capitalized concatenated words is a standard way of naming variables, % we want to extract a string containing these words modified to lower case % and separated by the "_" character. For example: % FooBARFooBAR -> foo_bar_foo_bar % where the words are "foo" and "bar". columnname = fieldname; % FooBARFooBAR -> FooBARFoo_bar columnname = (regexprep(columnname, "[A-Z]{2,}$", "_${lower($0)}")); % FooBARFoo_bar -> FooBARF#oo_bar columnname = (regexprep(columnname, "[A-Z]{2,}", "$0#")); % FooBARF#oo_bar -> FooBAR_f#oo_bar columnname = (regexprep(columnname, "[A-Z]#", "_${lower($0)}")); % FooBAR_f#oo_bar -> FooBAR_foo_bar columnname = (regexprep(columnname, "#", "")); % FooBAR_foo_bar -> _foo_bar_foo_bar columnname = (regexprep(columnname, "[A-Z]{1,}", "_${lower($0)}")); % _foo_bar_foo_bar -> foo_bar_foo_bar columnname = (regexprep(columnname, "^_", "")); columnname = join([prefix columnname suffix], "_"); columnname = strip(columnname, "_"); output = columnname; end function output = nanIfEmpty(value) if isempty(value) | isequal(value, "") output = nan; else output = value; end end function output = isParent(parsedProperty, parentList) output = false; for i = 1:length(parentList) if isequal(parentList{i}, parsedProperty) output = true; return end end end \ No newline at end of file