github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/pkg/service/combine.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "strings" 6 7 consts "github.com/easysoft/zendata/internal/pkg/const" 8 "github.com/easysoft/zendata/internal/pkg/domain" 9 "github.com/easysoft/zendata/internal/pkg/helper" 10 stringUtils "github.com/easysoft/zendata/pkg/utils/string" 11 "github.com/easysoft/zendata/pkg/utils/vari" 12 ) 13 14 type CombineService struct { 15 ExcelService *ExcelService `inject:""` 16 ExpressionService *ExpressionService `inject:""` 17 LoopService *LoopService `inject:""` 18 OutputService *OutputService `inject:""` 19 PlaceholderService *PlaceholderService `inject:""` 20 } 21 22 func (s *CombineService) CombineChildrenIfNeeded(field *domain.DefField, isOnTopLevel bool) { 23 if len(field.Fields) == 0 { 24 return 25 } 26 27 // 1. get values for child fields 28 if len(field.Values) == 0 { 29 for index, child := range field.Fields { 30 if len(child.Fields) > 0 && len(child.Values) == 0 { // no need to do if already generated 31 s.CombineChildrenIfNeeded(&(field.Fields[index]), false) 32 } 33 34 // for text output only 35 vari.GlobalVars.FieldNameToValuesMap[field.Fields[index].Field] = field.Fields[index].Values 36 vari.GlobalVars.FieldNameToFieldMap[field.Fields[index].Field] = field.Fields[index] 37 } 38 } 39 40 if !field.Join { 41 return 42 } 43 44 // 2. deal with expression 45 arrByField := make([][]interface{}, 0) // 2 dimension arr for child, [ [a,b,c], [1,2,3] ] 46 for i, child := range field.Fields { 47 if child.Value != "" { 48 vari.GlobalVars.FieldNameToValuesMap[child.Field] = s.ExpressionService.GenExpressionValues(child) 49 } 50 51 // select from excel with expr 52 if helper.IsSelectExcelWithExpr(child) { 53 vari.GlobalVars.FieldNameToValuesMap[child.Field] = s.ExcelService.genExcelValuesWithExpr(&child, vari.GlobalVars.FieldNameToValuesMap) 54 } 55 56 arrByField = append(arrByField, vari.GlobalVars.FieldNameToValuesMap[child.Field]) 57 58 // clear child values after combined 59 field.Fields[i].Values = nil 60 } 61 62 // 3. get combined values for parent field 63 isRecursive := vari.GlobalVars.Recursive 64 if stringUtils.InArray(field.Mode, consts.Modes) { // set on field level 65 isRecursive = field.Mode == consts.ModeRecursive || field.Mode == consts.ModeRecursiveShort 66 } 67 68 if len(field.Values) == 0 && field.Fields != nil { 69 field.Values = s.combineChildrenValues(arrByField, isRecursive, isOnTopLevel) 70 } 71 72 s.LoopService.LoopAndFixFieldValues(field, true) 73 } 74 75 func (s *CombineService) combineChildrenValues(arrByField [][]interface{}, isRecursive, isOnTopLevel bool) (ret []interface{}) { 76 arrByRow := s.populateRowsFromTwoDimArr(arrByField, isRecursive, isOnTopLevel) 77 78 for _, arr := range arrByRow { 79 line := s.ConnectValues(arr) 80 ret = append(ret, line) 81 } 82 83 return 84 } 85 86 func (s *CombineService) populateRowsFromTwoDimArr(arrOfArr [][]interface{}, isRecursive, isOnTopLevel bool) ( 87 values [][]interface{}) { 88 count := vari.GlobalVars.Total 89 90 if !isOnTopLevel { 91 if isRecursive { 92 count = s.getRecordCountForRecursive(arrOfArr) 93 } else { 94 count = s.getRecordCountForParallel(arrOfArr) 95 } 96 } 97 98 indexArr := make([]int, 0) 99 if isRecursive { 100 indexArr = s.getModArrForChildrenRecursive(arrOfArr) 101 } 102 103 for i := 0; i < count; i++ { 104 strArr := make([]interface{}, 0) 105 for j := 0; j < len(arrOfArr); j++ { 106 child := arrOfArr[j] 107 if len(child) == 0 { 108 continue 109 } 110 111 var index int 112 if isRecursive { 113 mod := indexArr[j] 114 index = i / mod % len(child) 115 } else { 116 index = i % len(child) 117 } 118 119 val := child[index] 120 strArr = append(strArr, val) 121 } 122 123 values = append(values, strArr) 124 } 125 126 return 127 } 128 129 func (s *CombineService) getRecordCountForParallel(arrOfArr [][]interface{}) int { 130 // get max count of 2nd dim arr 131 count := 1 132 for _, arr := range arrOfArr { 133 if count < len(arr) { 134 count = len(arr) 135 } 136 } 137 138 if count > vari.GlobalVars.Total { 139 count = vari.GlobalVars.Total 140 } 141 142 return count 143 } 144 145 func (s *CombineService) getRecordCountForRecursive(arrOfArr [][]interface{}) int { 146 count := 1 147 for i := 0; i < len(arrOfArr); i++ { 148 arr := arrOfArr[i] 149 count = len(arr) * count 150 } 151 return count 152 } 153 154 func (s *CombineService) getModArrForChildrenRecursive(arrOfArr [][]interface{}) []int { 155 indexArr := make([]int, 0) 156 for range arrOfArr { 157 indexArr = append(indexArr, 0) 158 } 159 160 for i := 0; i < len(arrOfArr); i++ { 161 loop := 1 162 for j := i + 1; j < len(arrOfArr); j++ { 163 loop = loop * len(arrOfArr[j]) 164 } 165 166 indexArr[i] = loop 167 } 168 169 return indexArr 170 } 171 172 func (s *CombineService) ConnectValues(values []interface{}) (ret string) { 173 for i, item := range values { 174 col := fmt.Sprintf("%v", item) 175 176 if i > 0 && vari.GlobalVars.Human { // use a tab 177 ret = strings.TrimRight(ret, "\t") 178 col = strings.TrimLeft(col, "\t") 179 180 ret += "\t" + col 181 } else { 182 ret += col 183 } 184 } 185 186 return 187 }