-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmod_exc_DataTables.bas
executable file
·298 lines (238 loc) · 8.66 KB
/
mod_exc_DataTables.bas
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
Attribute VB_Name = "mod_exc_DataTables"
Option Explicit
' error handling tag ********************
Const cStrModuleName As String = "mod_exc_DataTables"
' ********************
'
' Practical subfunctions for manipulating data tables easily
'
' 160721.AMG get all data rows from sheet without header(s)
' 150706.AMG better handling of blanks
' 150622.AMG normalise table with multiple entries in one column
' 150611.AMG new match options to trim trailing & leading spaces
' 150326.AMG added Table Match functions previously in Consol module
' 150312.AMG created with table creation
' REFERENCES
' ==========
'
' This module uses the following references (paths and GUIDs may vary)
' (only those required by it's dependent modules)
' DEPENDENCIES
' ============
'
' This module requires the following vba-lib dependencies:
' mod_exc_WbkShtRngName
' IMPROVEMENTS
' ============
'
' * work out where SplitCellsWithKey and TransTblNormaliseMultiEntries are used to test the following
' * simplify TransTblNormaliseMultiEntries to use mod_off_ExportListToExcel.bas
'
' PREPARATION
' ===========
'
' no special prep required
'
' Define match types
Public Enum enumDataTableMatchType
MatchCaseSens
MatchCaseInsens
MatchCaseSensTrim
MatchCaseInsensTrim
End Enum
'
' *** TABLE CREATION *****************************
'
Public Function FillTableDownByCopyingBlanksFromAbove()
Dim rng, rRow, rCel As Range
Set rng = ActiveSheet.UsedRange
For Each rRow In rng.Rows
For Each rCel In rRow.Cells
With rCel
If (.Value = "") And (.row > 1) Then
.Value = rng.Cells(.row - 1, .Column).Value
End If
End With
Next
Next
End Function
Public Function FillColumnDownByCopyingBlanksFromAbove()
Dim rCel As Range
For Each rCel In ActiveSheet.UsedRange.Columns(ActiveCell.Column).Cells
With rCel
If (.Value = "") And (.row > 1) Then
.Value = ActiveSheet.Cells(.row - 1, .Column).Value
End If
End With
Next
End Function
'
' *** TABLE READ *****************************
'
' Get the Range of the table Data rows from the selected sheet
' but ignoring any header rows if present
Public Function rngGetTableDataFromSheet( _
Optional ByRef shtFromWorksheet As Excel.Worksheet _
, Optional ByVal lngNumHeaders As Long = 0 _
) As Excel.Range
Dim rng As Excel.Range
Dim sht As Excel.Worksheet
If sht Is Nothing Then
Set sht = Excel.Application.ActiveSheet
End If
' credit - http://www.pcreview.co.uk/threads/vba-select-used-range-minus-the-top-header.3517661/
If lngNumHeaders = 0 Then
Set rng = sht.UsedRange
Else
With sht.UsedRange
Set rng = .Cells(1 + lngNumHeaders, 1).Resize(.Rows.Count - lngNumHeaders, .Columns.Count)
End With
End If
' alternatively use INTERSECT - http://www.mrexcel.com/forum/excel-questions/619875-exclude-rows-usedrange.html
Set rngGetTableDataFromSheet = rng
End Function
'
' *** TABLE SEARCH / MATCH *****************************
'
' find the first absolute row in a Table (OTPIONALLY between a given range of rows)
' where the value in a certain column matches the VALUE passed)
' or zero if no match is found
Public Function intMatchGetRow _
(ByVal strMatch As String _
, ByVal enumMatchType As enumDataTableMatchType _
, ByRef sht As Worksheet _
, ByVal intCol As Integer _
, ByVal intFirstRow As Integer _
, ByVal intLastRow As Integer _
, Optional ByVal strIgnore As String = "" _
)
' intMyRow = intMatchGetRow _
' (strMatch:="" _
' , enumMatchType:=enumMatchType _
' , sht:=shtMine _
' , intCol:=0 _
' , intFirstRow:=0 _
' , intLastRow:=0 _
' , strIgnore:="" _
' )
' IMPROVEMENTS:
' see CAPS in description above
' more value types
' if intLastRow becomes OPTIONAL, then do we just continue until first blank or last row of used range?
Dim intTryRow As Integer
Dim strLookFor, strCheckValue As String
intMatchGetRow = 0
strLookFor = strMatchPrepareValue(strMatch, enumMatchType, strIgnore)
If strLookFor <> "" Then
For intTryRow = intFirstRow To intLastRow
strCheckValue = strMatchPrepareValue(sht.Cells(intTryRow, intCol), enumMatchType, strIgnore)
If bMatchCheckValues(strCheckValue, strLookFor, enumMatchType) Then
' return the value and break out
intMatchGetRow = intTryRow
intTryRow = intLastRow
End If
Next
End If
End Function ' intMatchGetRow
Public Function strMatchPrepareValue _
(ByVal strUnprepared As String _
, ByVal enumMatchType As enumDataTableMatchType _
, Optional ByVal strIgnore As String = "" _
) As String
Dim strKeyToMatch As String
Dim strToReplace As String
Select Case enumMatchType
Case enumDataTableMatchType.MatchCaseInsens:
strKeyToMatch = UCase(strUnprepared)
strToReplace = UCase(strIgnore)
Case enumDataTableMatchType.MatchCaseInsensTrim:
strKeyToMatch = LTrim(RTrim(UCase(strUnprepared)))
strToReplace = UCase(strIgnore)
Case enumDataTableMatchType.MatchCaseSens:
strKeyToMatch = strUnprepared
strToReplace = strIgnore
Case enumDataTableMatchType.MatchCaseSensTrim:
strKeyToMatch = LTrim(RTrim(strUnprepared))
strToReplace = strIgnore
End Select
' ONLY USE IGNORE DEPENDING ON MATCH TYPE FLAG ??
If strToReplace <> "" Then
strKeyToMatch = Replace(strKeyToMatch, strToReplace, "")
End If
strMatchPrepareValue = strKeyToMatch
End Function
Public Function strTrimPrepareValue _
(ByVal strUnprepared As String _
, ByVal enumMatchType As enumDataTableMatchType _
) As String
' moved from mod_exc_ConsolInteligence
Dim strKeyToMatch As String
Select Case enumMatchType
Case enumDataTableMatchType.MatchCaseInsensTrim _
Or enumDataTableMatchType.MatchCaseSensTrim:
strKeyToMatch = LTrim(RTrim(strUnprepared))
End Select
strTrimPrepareValue = strKeyToMatch
End Function
Public Function bMatchCheckValues _
(varFirst As Variant _
, varSecond As Variant _
, enumMatchType As enumDataTableMatchType _
) As Boolean
' default return value
bMatchCheckValues = False
' assuming values already prepared
If varFirst = varSecond Then
bMatchCheckValues = True
End If
End Function
'
' *** TABLE TRANSFORMATION ***
'
Sub SplitCellsWithKey()
Dim WorkRng As Range
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", "Split Cells with Key", WorkRng.Address, Type:=8)
' REWORK TO USE THIS FUNCTION
' Call TransTblNormaliseMultiEntries("NormSvrs", WorkRng.Address)
'based partly on http://www.extendoffice.com/documents/excel/2211-excel-split-cell-by-carriage-return.html
'Update 20141024
Dim rng As Range
On Error Resume Next
For Each rng In WorkRng
Dim lLFs As Long
lLFs = VBA.Len(rng) - VBA.Len(VBA.Replace(rng, vbLf, ""))
If lLFs > 0 Then
rng.Offset(1, 0).Resize(lLFs).Insert shift:=xlShiftDown
rng.Resize(lLFs + 1).Value = Application.WorksheetFunction.Transpose(VBA.Split(rng, vbLf))
rng.Offset(1, -1).Resize(lLFs).Insert shift:=xlShiftDown
End If
Next
End Sub
Function TransTblNormaliseMultiEntries _
(strRange As String _
, Optional strNewSheetName As String = "SheetNorm" _
, Optional strDelim As String = vbLf _
)
' defaults to LineFeed delimeter, used in multi-line cells (ALT-ENTER)
Dim shtOutput As Excel.Worksheet
Set shtOutput = getSheetOrCreateIfNotFound(Excel.ActiveWorkbook, strNewSheetName)
Dim rngRow As Range
Dim rngSourceTable As Range
Dim iOutRow As Integer
iOutRow = 1
For Each rngRow In rngSourceTable
Dim iCountDelims As Integer
'some credit - http://www.extendoffice.com/documents/excel/2211-excel-split-cell-by-carriage-return.html
On Error Resume Next
iCountDelims = VBA.Len(rngRow) - VBA.Len(VBA.Replace(rngRow, vbLf, ""))
If iCountDelims > 0 Then
rngRow.Offset(1, 0).Resize(iCountDelims).Insert shift:=xlShiftDown
rngRow.Resize(iCountDelims + 1).Value = Application.WorksheetFunction.Transpose(VBA.Split(rngRow, vbLf))
rngRow.Offset(1, -1).Resize(iCountDelims).Insert shift:=xlShiftDown
shtOutput.Cells(1, 1).Value = "SourceID"
shtOutput.Cells(1, 2).Value = "Path"
iOutRow = iOutRow + 1
End If
Next
End Function