-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrecursiveNestedStruct2Table.m
More file actions
110 lines (101 loc) · 5.29 KB
/
Copy pathrecursiveNestedStruct2Table.m
File metadata and controls
110 lines (101 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
function tParent = recursiveNestedStruct2Table(level,nameOfLevel)
%Takes a nested structure (level) and returns a flat table in 'long' form.
if numel(level)>1 %we have a struct array
%level = fillBlankStructArrayRowWithTopRowValues(level);
tParent = struct2table(rotateAndFillStruct(level(1))); %so deal with the first one
tParent.(nameOfLevel) = ones(height(tParent),1);
tParent = [tParent(:,end) tParent(:,1:end-1)];
for s=2:numel(level)
tUpperTemp = struct2table(rotateAndFillStruct(level(s))); % then every other one
tUpperTemp.(nameOfLevel) = s*ones(height(tParent),1);
tUpperTemp = [tUpperTemp(:,end) tUpperTemp(:,1:end-1)];
tUpperTemp = matchVariableNames(tUpperTemp,tParent);
tParent = [tParent ; tUpperTemp]; %and concatinate them all together
end
else %assumes we have a one level struct
tParent = struct2table(rotateAndFillStruct(level));
end
fNames = fieldnames(tParent);
fNames = fNames(~strcmp(fNames,'Properties') & ~strcmp(fNames,'Variables') & ~strcmp(fNames,'DimensionNames') & ~strcmp(fNames,'Row'));
if ~iscell(fNames)
fNames = {fNames};
end
[tParent,structCols] = sortNonStructColsToLeft(tParent);
if isempty(structCols)
return
end
tRow = cell(height(tParent),1);
for row = 1:height(tParent) %step through each row
tChildren = cell(size(structCols));
for col = structCols %Step through cols that contain structs.
tChildren{col==structCols} = recursiveNestedStruct2Table(tParent{row,col},tParent.Properties.VariableNames{col}); %recursive call
tChildren{col==structCols}.(tParent.Properties.VariableNames{col}) = row*ones(height(tChildren{col==structCols}),1);
tChildren{col==structCols} = [tChildren{col==structCols}(:,end) tChildren{col==structCols}(:,1:end-1)];
tChildren{col==structCols} = prependToAllTableVariableNames(tChildren{col==structCols},tParent.Properties.VariableNames{col});
end
maxHeight = max(cellfun(@height,tChildren));
tChildren = cellfun(@(x) struct2table(fillNans(myTable2Struct(x),maxHeight)),tChildren,'UniformOutput',false);
nonStructs = repmat(tParent(row,1:structCols(1)-1),maxHeight,1);
tRow{row} = [nonStructs tChildren{:}];
end
tParent = tRow{1};
for rIdx=2:length(tRow)
tParent = matchVariableNames(tParent,tRow{rIdx});
tRow{rIdx} = matchVariableNames(tRow{rIdx},tParent);
tParent = [tParent ; tRow{rIdx}];
end
function A = rotateAndFillStruct(A)
%Fill coloumns with nans or {nans} until all cols have matching
%number of elements
A = structRowsToCols(A);
heights = structfun(@(x) size(x,1),A);
A = fillNans(A,max(heights));
end
function A = fillNans(A,maxHeight)
%adds Nans table or struct *A* so that all feilds are *maxHeight*
fNames_ = fieldnames(A);
fNames_ = fNames_(~strcmp(fNames_,'Properties') & ~strcmp(fNames_,'Variables') & ~strcmp(fNames_,'DimensionNames') & ~strcmp(fNames_,'Row'));
for r = 1:length(fNames_)
if isempty(A.(fNames_{r}))
A = rmfield(A,fNames_{r});
elseif isstruct(A.(fNames_{r})(1))
A.(fNames_{r}) = [A.(fNames_{r}) ; repmat(struct(A.(fNames_{r})),maxHeight-length(A.(fNames_{r})),1)];
elseif iscell(A.(fNames_{r})(1))
A.(fNames_{r}) = [A.(fNames_{r}) ; repmat(num2cell(nan(size(A.(fNames_{r})(1,:)))),maxHeight-length(A.(fNames_{r})),1)];
elseif ischar(A.(fNames_{r}))
A.(fNames_{r}) = [{A.(fNames_{r})} ; repmat({''},maxHeight-1,1)];
else
A.(fNames_{r}) = [A.(fNames_{r}) ; nan(maxHeight-length(A.(fNames_{r})),1)];
end
end
end
function t = prependToAllTableVariableNames(t,str2prepend)
for vName = 1:length(t.Properties.VariableNames)
t.Properties.VariableNames{vName} = [str2prepend '_' t.Properties.VariableNames{vName}];
end
end
function [t,structCols] = sortNonStructColsToLeft(t)
%All feilds containing structs are pushed to the right of the table *t*, all non-structs are pushed to the left
hasStruct = varfun(@(x) isstruct(x), t(1,:));
[structCols,sortIdx] = sort(hasStruct{1,:});
t = t(:,sortIdx);
structCols =find(structCols);
end
function sA = fillBlankStructArrayRowWithTopRowValues(sA)
vNames = fieldnames(sA);
for sRow=2:numel(sA)
for v = 1:length(vNames)
if isempty(sA(sRow).(vNames{v})) || isnan(sA(sRow).(vNames{v})) || (iscell(sA(sRow).(vNames{v})) && isnan(sA(sRow).(vNames{v}){1}))
sA(sRow).(vNames{v}) = sA(sRow-1).(vNames{v});
end
end
end
end
function s = myTable2Struct(t)
fNames_ = fieldnames(t);
fNames_ = fNames_(~strcmp(fNames_,'Properties') & ~strcmp(fNames_,'Variables') & ~strcmp(fNames_,'DimensionNames') & ~strcmp(fNames_,'Row'));
for i=1:length(fNames_)
s.(fNames_{i}) = t.(fNames_{i});
end
end
end