forked from 6234456/Excel-VBA-Dicts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Dicts.bas
2051 lines (1584 loc) · 59.9 KB
/
Dicts.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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'@desc Util Class Dicts
'@author Qiou Yang
'@license MIT
'@dependency Lists, HashSets
'@lastUpdate 07.07.2020
' replace with HashSets
' add feed with shorten
'
'@TODO add comments
' unify the Exception-Code
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' declaration compulsory
Option Explicit
'___________private variables_____________
'implement TreeMaps Object
Private pKeys As HashSets
Private pVals As Collection
Const pUpdate As Boolean = True ' to update if duplicated
' has column label
Private pIsLabeled As Boolean
' column label as Dicts, label -> index
Private pLabeledArray As Dicts
' target workbook
Private pWb As Workbook
Private pList As Lists
Private pResKey ' res for the callback map/reduce/filter
Private pResVal
' enum for the parameters in filter/reduce/map
Enum ProcessWith
key = 0
value = 1
RangedValue = 2
End Enum
' aggregate method for the function ranged
Enum AggregateMethod
AggMap = 0
AggReduce = 1
Aggfilter = 2
End Enum
Private Sub Class_Initialize()
Set pWb = ThisWorkbook
Set pList = New Lists
Set pKeys = New HashSets
Set pVals = New Collection
End Sub
Private Sub Class_Terminate()
Set pWb = Nothing
Set pLabeledArray = Nothing
Set pList = Nothing
Set pKeys = Nothing
Set pVals = Nothing
End Sub
' get/set target workbook
Public Property Get wb() As Workbook
Set wb = pWb
End Property
Public Property Let wb(ByRef wkb As Workbook)
Set pWb = wkb
End Property
' get the underlying Dicitionary-Object
Public Property Get dict() As Dicts
Set dict = Me
End Property
Public Property Let callbackKey(res)
If IsObject(res) Then
Set pResKey = res
Else
pResKey = res
End If
End Property
Public Property Get callbackKey()
If IsObject(pResKey) Then
Set callbackKey = pResKey
Else
callbackKey = pResKey
End If
End Property
Public Property Let callbackVal(res)
If IsObject(res) Then
Set pResVal = res
Else
pResVal = res
End If
End Property
Public Property Get callbackVal()
If IsObject(pResVal) Then
Set callbackVal = pResVal
Else
callbackVal = pResVal
End If
End Property
Public Function add(k, v) As Dicts
Dim tmp
tmp = pKeys.ceiling(k, False)
If IsNull(tmp) Then
pKeys.add k, pUpdate
pVals.add v
Else
pKeys.add k, pUpdate
pVals.add v, before:=tmp + 1
pVals.Remove tmp + 2
End If
Set add = Me
End Function
Public Property Let Item(key As Variant, value As Variant)
add key, value
End Property
Public Property Get Item(key As Variant) As Variant
Dim tmp As Variant
tmp = pKeys.ceiling(key, False)
If IsNull(tmp) Then
Item = Null
Else
If IsObject(pVals.Item(tmp + 1)) Then
Set Item = pVals.Item(tmp + 1)
Else
Item = pVals.Item(tmp + 1)
End If
End If
End Property
Public Function exists(key As Variant) As Boolean
exists = pKeys.contains(key)
End Function
Public Function RemoveAll()
pKeys.clear
Set pVals = New Collection
End Function
Public Function Remove(e)
Dim tmp
tmp = pKeys.ceiling(e)
If Not IsNull(tmp) Then
pVals.Remove tmp + 1
pKeys.Remove e
End If
End Function
Public Function clear()
RemoveAll
End Function
' get/set column labels
Public Property Get label() As Dicts
If pIsLabeled Then
Set label = pLabeledArray
Else
Set label = Nothing
End If
End Property
Public Property Let label(ByVal rng As Variant)
setLabel rng
End Property
Public Function hasLabel() As Boolean
hasLabel = pIsLabeled
End Function
Public Function copyLabel(ByRef src As Dicts, ByRef targ As Dicts)
If src.hasLabel Then
targ.label = src.label
Else
targ.label = Nothing
End If
End Function
'''''''''''
'@desc: set the column/row labels to the underlying Dicts
'@return: this Dicts
'@param: rng either as Array, Dicts or as Range
'''''''''''
Public Function setLabel(ByVal rng As Variant) As Dicts
If isInstanceOf(rng, "Nothing") Then
pIsLabeled = False
Set pLabeledArray = Nothing
Else
Dim c
Dim cnt As Long
cnt = 0
Dim d As New Dicts
If isInstanceOf(rng, "Range") Then
For Each c In rng.Cells
d.Item(Trim(CStr(c.value))) = cnt
cnt = cnt + 1
Next c
Me.setLabel d
Else
If isDict(rng) Then
Set pLabeledArray = rng
ElseIf IsArray(rng) Then
Dim k
For k = 0 To UBound(rng) - LBound(rng)
d.Item(rng(k)) = k
Next k
Me.setLabel d
End If
End If
pIsLabeled = True
End If
Set setLabel = Me
End Function
'@desc get element by key and label
'@return the target element
Public Function getByLabel(ByRef k As Variant, ByRef label As String) As Variant
If Not pIsLabeled Then
Err.Raise 99760, , "LabelAbsentException: please specify the label first"
End If
If Not Me.exists(k) Then
Err.Raise 89760, , "ElementNotFoundException: the key does not exist"
End If
If Not pLabeledArray.exists(label) Then
Err.Raise 89760, , "ElementNotFoundException: the label '" & label & "' does not exist"
End If
If IsObject(Item(k)(pLabeledArray.Item(label))) Then
Set getByLabel = Item(k)(pLabeledArray.Item(label))
Else
getByLabel = Item(k)(pLabeledArray.Item(label))
End If
End Function
' get length of the key-value pairs
' if recursive set to true, count the keys of all child-dicts
' allLevels only relevant in recursive-mode, count all the keys in the structure
Public Function Count(Optional ByVal recursive As Boolean = False, Optional ByVal allLevels As Boolean = True) As Long
If Not recursive Then
Count = pKeys.size
Else
If isDicted_(Me) Then
Dim k
Dim res As Long
For Each k In Me.Keys
res = IIf(allLevels, 1, 0) + res + Me.Item(k).Count(True)
Next k
Count = res
Else
Count = Me.Count
End If
End If
End Function
' get keys as Array, if no element return null-Array
Public Property Get keysArr() As Variant
keysArr = Me.Keys
End Property
' get keys as Array, if no element return null-Array
Public Property Get valsArr() As Variant
Dim res()
If Me.Count > 0 Then
ReDim res(0 To Me.Count - 1)
Dim k
Dim cnt As Long
cnt = 0
For Each k In Me.Keys
res(cnt) = Me.Item(k)
cnt = cnt + 1
Next k
End If
valsArr = res
Erase res
End Property
' get keys as iterable-object
Public Property Get Keys() As Variant
Keys = pKeys.toArray
End Property
' transfer N * 2 Matrix into Dicts [[k,v]] -> Dicts(k, v)
Public Function fromMatrix(ByRef l As Lists) As Dicts
Dim res As New Dicts
If l.length > 0 Then
Dim i
For i = 0 To l.length - 1
res.add l.getVal(i, 0), l.getVal(i, 1)
Next i
End If
Set fromMatrix = res
Set res = Nothing
End Function
Public Function fromArray(ByRef arr) As Dicts
If IsArray(arr) Then
Set fromArray = pList.addAll(arr).toDict
ElseIf TypeName(arr) = "Lists" Then
Set fromArray = arr.toDict
Else
Err.Raise 9876, , "Unknown Parameter Type!"
End If
End Function
'''''''''''''''''''''''''''
'@desc: get Worksheet
'@return: the target sht
'@param: targSht sheet name in string, by default the activesheet
' wb the workbook which contains the targSht
'''''''''''''''''''''''''''
Function getTargetSht(Optional ByVal targSht As String = "", Optional ByRef wb As Workbook) As Worksheet
Dim tmpWb As Workbook
Set tmpWb = IIf(wb Is Nothing, pWb, wb)
With tmpWb
Dim tmpname As String
tmpname = ActiveSheet.Name
If Trim(targSht) = "" Then
targSht = tmpname
End If
Set getTargetSht = .Worksheets(targSht)
End With
Set tmpWb = Nothing
End Function
'''''''''''''''''''''''''''
'@desc: load the content of range
'@return: the target range
'@param: targSht sheet name in string, by default the activesheet
' targKeyCol target key column, default to be 1
' targValCol target value column, the column to be read from, default to be the key column
' targRowBegine row number to begin
' targRowEnd row number ends, by default the last none-empty row of key column
' isVertical if true, data entries ranged vertically, i.e. model vlookup; if false, targKeyCol means actually targKeyRow and targValCol targValRow
'''''''''''''''''''''''''''
Function getRange(Optional ByVal targSht As String = "", Optional ByVal targKeyCol As Long = 1, Optional ByVal targValCol = 1, Optional targRowBegine As Variant, Optional ByVal targRowEnd As Variant, Optional ByRef wb As Workbook, Optional ByVal isVertical As Boolean = True) As Range
' get the target Range
With getTargetSht(targSht, wb)
If IsMissing(targRowBegine) Then
targRowBegine = 1
End If
' if the targValCol is single number, put it into array
If Not IsArray(targValCol) Then
targValCol = Array(targValCol)
End If
If IsMissing(targRowEnd) Then
If isVertical Then
targRowEnd = .Cells(.Rows.Count, targKeyCol).End(xlUp).row
Else
targRowEnd = .Cells(targKeyCol, .Columns.Count).End(xlToLeft).Column
End If
End If
If isVertical Then
Set getRange = .Cells(targRowBegine, targValCol(LBound(targValCol))).Resize(targRowEnd - targRowBegine + 1, targValCol(UBound(targValCol)) - targValCol(LBound(targValCol)) + 1)
Else
Set getRange = .Cells(targValCol(LBound(targValCol)), targRowBegine).Resize(targValCol(UBound(targValCol)) + 1 - targValCol(LBound(targValCol)), targRowEnd + 1 - targRowBegine)
End If
End With
End Function
'''''''''''
'@desc: get text value of each cells in the array
'@return: two-dimensional array containing format as text
'@param: rng as target Range
'''''''''''
Public Function rngToTextArr(ByRef rng As Range) As Variant
Dim i, j
Dim res()
Dim cnt As Long
cnt = 0
Dim w As Long
Dim h As Long
w = rng.Columns.Count
h = rng.Rows.Count
ReDim res(0 To h - 1, 0 To w - 1)
For i = 0 To h - 1
For j = 0 To w - 1
res(i, j) = CStr(rng.Cells(i + 1, j + 1).Text)
Next j
Next i
rngToTextArr = res
Erase res
End Function
'''''''''''
'@desc: get one-dimensional array based on the range
'@return: one-dimensional array containing value or address
'@param: rng as target Range
' isVertical if true, data entries ranged vertically, i.e. model vlookup
' asAddress keep the address as the content of the array
' asText keep the format of the range
'''''''''''
Public Function rngToArr(ByRef rng As Range, Optional ByVal isVertical As Boolean = True, Optional ByVal asAddress As Boolean = False, Optional ByVal asText As Boolean = False) As Variant
Dim i
Dim res()
Dim cnt As Long
cnt = 0
Dim arr() ' multi-dimensional array containing either value or address
' fill in the arr
If rng.Cells.Count = 1 Then
ReDim arr(1 To 1, 1 To 1)
arr(1, 1) = IIf(asText, rng.Text, IIf(asAddress, rng.Address, rng.value))
Else
If asAddress Then
arr = rngToAddress(rng)
Else
If asText Then
' TODO
arr = rngToTextArr(rng)
Else
arr = rng.value
End If
End If
End If
' slice the 2-dimensional array based on the direction specified
If isVertical Then
ReDim res(0 To rng.Rows.Count - 1)
For i = LBound(arr, 1) To UBound(arr, 1)
res(cnt) = sliceArr(arr, i, isVertical)
cnt = cnt + 1
Next i
Else
ReDim res(0 To rng.Columns.Count - 1)
For i = LBound(arr, 2) To UBound(arr, 2)
res(cnt) = sliceArr(arr, i, isVertical)
cnt = cnt + 1
Next i
End If
' if the result array contains only one element and the element is not array itself, return the result
If UBound(res) = LBound(res) Then
rngToArr = res(0)
Else
rngToArr = res
End If
Erase res
Erase arr
End Function
'''''''''''
'@desc: get two-dimensional array with the address of the target range
'@return: two-dimensional array with the address of the target range
'@param: rng as target Range
'''''''''''
Public Function rngToAddress(ByRef rng As Range, Optional ByVal withShtName As Boolean = True, Optional ByVal withWbName As Boolean = False) As Variant
Dim fst As Range
Set fst = rng.Cells(1, 1)
Dim lst As Range
Set lst = fst.offSet(rng.Rows.Count - 1, rng.Columns.Count - 1)
Dim shtName As String
Dim wbName As String
shtName = "'" & fst.Worksheet.Name & "'!"
wbName = "'[" & fst.Worksheet.parent.Name & "]" & fst.Worksheet.Name & "'!"
Dim i As Long
Dim j As Long
Dim res()
ReDim res(1 To rng.Rows.Count, 1 To rng.Columns.Count)
For i = fst.row To lst.row
For j = fst.Column To lst.Column
res(i - fst.row + 1, j - fst.Column + 1) = IIf(withWbName, wbName & Cells(i, j).Address(0, 0), IIf(withShtName, shtName & Cells(i, j).Address(0, 0), Cells(i, j).Address(0, 0)))
Next j
Next i
rngToAddress = res
Set fst = Nothing
Set lst = Nothing
Erase res
End Function
'''''''''''
'@desc: slice two-dimensional array into one-dimensional array based on the direction specified
'@return: one-dimensional array containing values of specific row or column
'@param: arr two-dimensional array
' n the n-th row, if isVertical else the n-th column
' isVertical if true, data entries ranged vertically, i.e. model vlookup
'''''''''''
Public Function sliceArr(arr, Optional ByVal n As Long = 0, Optional ByVal isVertical As Boolean = True) As Variant
Dim i
Dim res()
Dim cnt As Long
cnt = 0
If isVertical Then
ReDim res(0 To UBound(arr, 2) - LBound(arr, 2))
' n is row number, dimension 1
For i = LBound(arr, 2) To UBound(arr, 2)
res(cnt) = arr(n, i)
cnt = cnt + 1
Next i
Else
ReDim res(0 To UBound(arr, 1) - LBound(arr, 1))
' n is col number, dimension 2
For i = LBound(arr, 1) To UBound(arr, 1)
res(cnt) = arr(i, n)
cnt = cnt + 1
Next i
End If
sliceArr = res
Erase res
End Function
'''''''''''
'@return: dimension of the array
'@param: arr target array
'''''''''''
Private Function arrDimension(arr) As Long
On Error GoTo hdl:
Dim res As Long
res = 0
Dim cnt As Long
cnt = 1
If IsArray(arr) Then
Dim e
Do While True
e = UBound(arr, cnt)
cnt = cnt + 1
Loop
hdl:
res = cnt - 1
End If
arrDimension = res
End Function
'''''''''''
'@return: length of the one-dimensional array
'@param: arr target array
'''''''''''
Private Function arrLen(arr) As Long
arrLen = UBound(arr) - LBound(arr) + 1
End Function
'''''''''''
'@return: Dicts obj
'@param: keyArr keys in one-dimensional array
' valArr vals in one-dimensional array, which can contain arrays of value as its element
' isReversed read from bottom up if true
' keyCstr whether transfer the keys into trimmed string
'''''''''''
Function arrToDict(keyArr, valArr, Optional ByVal isReversed As Boolean = False, Optional ByVal keyCstr As Boolean = False) As Dicts
Dim res As New Dicts
' combine the key-value pair in a zipped mode
If arrLen(keyArr) = 0 Then
ElseIf arrLen(keyArr) = 1 And arrLen(valArr) > 1 Then
res.Item(keyArr(LBound(keyArr))) = valArr
Else
If arrLen(keyArr) <> arrLen(valArr) Then
Err.Raise 8888, "", "Arrays with different length can not be combined"
End If
Dim i, k
If isReversed Then
For i = UBound(keyArr) To LBound(keyArr) Step -1
If Len(Trim(CStr(keyArr(i)))) > 0 Then
k = IIf(keyCstr, Trim(CStr(keyArr(i))), keyArr(i))
If res.exists(k) Then
res.Remove k
End If
res.add k, valArr(i)
End If
Next i
Else
For i = LBound(keyArr) To UBound(keyArr)
If Len(Trim(CStr(keyArr(i)))) > 0 Then
k = IIf(keyCstr, Trim(CStr(keyArr(i))), keyArr(i))
If res.exists(k) Then
res.Remove k
End If
res.add k, valArr(i)
End If
Next i
End If
End If
Set arrToDict = res
Set res = Nothing
End Function
'''''''''''
'@desc: map the range to dictionary
'@return: dictionary-object
'@param: arr two-dimensional array
' n the n-th row, if isVertical else the n-th column
'''''''''''
Function rngToDict(ByRef keyRng As Range, ByRef valRng As Range, Optional ByVal isReversed As Boolean = False, Optional ByVal asAddress As Boolean = False, Optional ByVal asText As Boolean = False) As Dicts
' if the keyRng contains only one column, it is vertical
Dim isVertical As Boolean
isVertical = keyRng.Columns.Count = 1
Set rngToDict = arrToDict(rngToArr(keyRng, Not isVertical), IIf(IIf(isVertical, valRng.Columns.Count, valRng.Rows.Count) = 1, rngToArr(valRng, Not isVertical, asAddress, asText), rngToArr(valRng, isVertical, asAddress, asText)), isReversed)
End Function
'''''''''''
'@desc: read data from xlSht to Dicts-Collection
'@return: Dicts-object
'@param: Sht Name of the target Worksheet
' KeyCol the position of key, the n-th column if isVertical, else the n-th row
' ValCol the position of value, can be either a number or an array representing the n-th column if isVertical, else the n-th row
' RowBegine the firstRow of the data entries
' RowEnd the lastRow of the data entries, by default the last none-empty row
' wb the Workbook-Object which contains the Sht, by default thisWorkbook
' Reversed read from bottom up if true.
' asAddress load the addresses
' appendMode keep the old data if evoked multiple times
' isVertical vlook if true
'''''''''''
Public Function load(Optional ByVal sht As String = "", Optional ByVal KeyCol As Long = 1, Optional ByVal valCol = 1, Optional RowBegine As Variant = 1, Optional ByVal RowEnd As Variant, Optional ByRef wb As Workbook, Optional ByRef Reversed As Boolean = False, Optional ByRef asAddress As Boolean = False, Optional appendMode As Boolean = False, Optional ByVal isVertical As Boolean = True, Optional ByRef asText As Boolean = False) As Dicts
Dim keyRng As Range
Set keyRng = getRange(sht, KeyCol, KeyCol, RowBegine, RowEnd, wb, isVertical)
Dim valRng As Range
Set valRng = getRange(sht, KeyCol, valCol, RowBegine, RowEnd, wb, isVertical)
If Count = 0 Or Not appendMode Then
Set load = rngToDict(keyRng, valRng, Reversed, asAddress, asText)
Else
Set load = union(createInstance(Me.rngToDict(keyRng, valRng, Reversed, asAddress, asText)))
End If
Set keyRng = Nothing
Set valRng = Nothing
End Function
Public Function loadH(Optional ByVal sht As String = "", Optional ByVal KeyRow As Long = 1, Optional ByVal ValRow = 1, Optional ColBegine As Variant = 1, Optional ByVal ColEnd As Variant, Optional ByRef wb As Workbook, Optional ByRef Reversed As Boolean = False, Optional ByRef asAddress As Boolean = False, Optional appendMode As Boolean = False, Optional ByRef asText As Boolean = False) As Dicts
Set loadH = load(sht:=sht, KeyCol:=KeyRow, valCol:=ValRow, RowBegine:=ColBegine, RowEnd:=ColEnd, wb:=wb, Reversed:=Reversed, asAddress:=asAddress, appendMode:=appendMode, isVertical:=False, asText:=asText)
End Function
'@desc update self with new dictionary obj
'@deprecated only for the legacy code
Public Function of(ByRef dictObj As Dicts) As Dicts
Set of = dictObj
End Function
'@desc create a new instance with the dictionary obj
'@deprecated only for the legacy code
Public Function createInstance(ByRef dictObj As Dicts) As Dicts
Dim res As New Dicts
Set createInstance = res.of(dictObj)
Set res = Nothing
End Function
'@deprecated only for the legacy code
Public Function emptyInstance() As Dicts
Dim res As New Dicts
Set emptyInstance = res
Set res = Nothing
End Function
Public Function loadStruct(ByVal sht As String, ByVal KeyCol1 As Long, ByVal KeyCol2 As Long, ByVal valCol, Optional RowBegine As Variant, Optional ByVal RowEnd As Variant, Optional ByRef wb As Workbook, Optional ByRef Reversed As Boolean = False) As Dicts
With getTargetSht(sht, wb)
Dim dict As New Dicts
If IsMissing(RowBegine) Then
RowBegine = 1
End If
If IsMissing(RowEnd) Then
RowEnd = .Cells(Rows.Count, KeyCol2).End(xlUp).row
End If
Dim tmpPreviousRow As Long
Dim tmpCurrentRow As Long
Dim tmpDict As New Dicts
tmpPreviousRow = RowEnd
tmpCurrentRow = tmpPreviousRow
Do While tmpCurrentRow > RowBegine
tmpCurrentRow = .Cells(tmpCurrentRow, KeyCol1).End(xlUp).row
dict.add .Cells(tmpCurrentRow, KeyCol1).value, tmpDict.load(sht, KeyCol2, valCol, tmpCurrentRow + 1, tmpPreviousRow, wb, Reversed)
Set tmpDict = Nothing
tmpPreviousRow = tmpCurrentRow - 1
Loop
Set loadStruct = dict
Set dict = Nothing
End With
End Function
Public Function reset(Optional ByVal v As Variant = 0) As Dicts
Dim k
For Each k In Me.Keys
If isDict(Me.Item(k)) Then
Me.Item(k).reset v
Else
Me.Item(k) = v
End If
Next k
Set reset = Me
End Function
' incremental based on the data Dict feed
Public Function feed(ByRef d As Dicts, Optional ByVal isIncremental As Boolean = False, Optional ByVal shorten As Boolean = False) As Dicts
Dim k
For Each k In Me.Keys
If isDict(Me.Item(k)) Then
Me.Item(k).feed d, shorten:=shorten
Else
If d.exists(k) Then
If isIncremental Then
Me.Item(k) = Me.Item(k) + d.Item(k)
Else
Me.Item(k) = d.Item(k)
End If
Else
If shorten Then
Me.Remove k
End If
End If
End If
Next k
Set feed = Me
End Function
' rng can be Range Object or an array
Public Function frequencyCount(ByRef rng) As Dicts
Dim res As New Dicts
Dim k
If TypeName(rng) = "Range" Then
For Each k In rng.Cells
If Len(k.value) > 0 Then
If res.exists(k.value) Then
res.Item(k.value) = res.Item(k.value) + 1
Else
res.Item(k.value) = 1
End If
End If
Next k
Else
For Each k In rng
If Len(k) > 0 Then
If res.exists(k) Then
res.Item(k) = res.Item(k) + 1
Else
res.Item(k) = 1
End If
End If
Next k
End If
Set frequencyCount = res
Set res = Nothing
End Function
Public Sub unload(ByVal shtName As String, ByVal keyPos As Long, ByVal startingRow As Long, ByVal startingCol As Long, Optional ByVal endRow As Long, Optional ByVal endCol As Long, Optional ByRef wb As Workbook, Optional ByVal isVertical As Boolean = True)
Dim c
Dim tmp
Dim l
With getTargetSht(shtName, wb)
If isVertical Then
If IsMissing(endRow) Or endRow = 0 Then
endRow = .Cells(.Rows.Count, keyPos).End(xlUp).row
End If
For Each c In .Cells(startingRow, keyPos).Resize(endRow - startingRow + 1, 1).Cells
If exists(c.value) Then
tmp = Item(c.value)
If IsArray(tmp) Then
If IsMissing(endCol) Or endCol = 0 Then
.Cells(c.row, startingCol).Resize(1, arrLen(tmp)).value = tmp
Else
l = pList.fromArray(tmp, False).take(endCol - startingCol + 1).toArray
.Cells(c.row, startingCol).Resize(1, arrLen(l)).value = l
End If
Else
.Cells(c.row, startingCol).value = tmp
End If
End If
Next c
Else
If IsMissing(endCol) Or endCol = 0 Then
endCol = .Cells(keyPos, .Columns.Count).End(xlToLeft).Column
End If
For Each c In .Cells(keyPos, startingCol).Resize(1, endCol - startingCol + 1).Cells
If exists(c.value) Then
tmp = Item(c.value)
If IsArray(tmp) Then
If IsMissing(endRow) Or endRow = 0 Then
.Cells(startingRow, c.Column).Resize(arrLen(tmp), 1).value = Application.WorksheetFunction.Transpose(tmp)
Else
l = pList.fromArray(tmp, False).take(endCol - startingCol + 1).toArray
.Cells(startingRow, c.Column).Resize(arrLen(l), 1).value = Application.WorksheetFunction.Transpose(l)
End If
Else
.Cells(startingRow, c.Column).value = tmp
End If
End If
Next c
End If
End With
End Sub
Public Sub dump(ByVal shtName As String, Optional ByVal keyPos As Long = 1, Optional ByVal startingRow As Long = 1, Optional ByVal startingCol As Long = 2, Optional ByVal endRow As Long, Optional ByVal endCol As Long, Optional ByRef wb As Workbook, Optional ByVal isVertical As Boolean = True, Optional ByVal trailingRows As Long = 0, Optional ByVal withLabel As Boolean = False)
With getTargetSht(shtName, wb)
If Me.Count > 0 Then
If isDicted_(Me) Then
Dim k
Dim cnt As Long
For Each k In Me.Keys
If isVertical Then
.Cells(startingRow + cnt, keyPos) = k
Else
.Cells(keyPos, startingCol + cnt) = k
End If
Me.Item(k).dump shtName, keyPos + 1, startingRow + cnt + 1, startingCol + 1, startingRow + cnt + Me.Item(k).Count(True), endCol, wb, isVertical, trailingRows, withLabel
cnt = cnt + Me.Item(k).Count(True) + 1
Next k
Else
'unload the key
If isVertical Then
.Cells(startingRow, keyPos).Resize(Me.Count, 1) = Application.WorksheetFunction.Transpose(Me.keysArr)
Else
.Cells(keyPos, startingCol).Resize(1, Me.Count) = Me.keysArr
End If
Me.unload shtName, keyPos, startingRow, startingCol, endRow, endCol, wb, isVertical
End If
End If
If withLabel And Me.hasLabel Then
.Rows(startingRow).Insert Shift:=xlDown
.Range(.Cells(startingRow, startingCol), .Cells(startingRow, startingCol + Me.label.Count - 1)) = Me.label.keysArr
End If
End With
End Sub
' if delete if all the elements are empty
' if value specified, set all empty value in the range to the value
Public Function nulls(Optional ByVal toVal, Optional isRanged As Boolean = False) As Dicts
Dim k
If Not isRanged Then
isRanged = isRanged_(Me)
End If
If Not isRanged Then
If IsMissing(toVal) Then
For Each k In Me.Keys
If isEmpty(Me.Item(k)) Then
Me.Remove k
End If
Next k
Else
For Each k In Me.Keys
If isEmpty(Me.Item(k)) Then
Me.Item(k) = toVal
End If
Next k
End If
Else
Dim l As New Lists
If IsMissing(toVal) Then
For Each k In Me.Keys
If l.addAll(Me.Item(k), False).isEmptyList Then
Me.Remove k
End If
Next k
Else
For Each k In Me.Keys
Me.Item(k) = l.addAll(Me.Item(k), False).nullVal(toVal).toArray
Next k
End If
End If
Set nulls = Me