You could use the RC5-cSortedDictionary (which adds/contains Key/Value-pairs always sorted):
Code:
Option Explicit
Private Sub Form_Load()
Dim D As cSortedDictionary
Set D = New_c.SortedDictionary
'the cSortedDictionary is adding new Key/Values already sorted
D.Add "2017-01", 1
D.Add "2014-03", 2
D.Add "2017-02", 3
D.Add "2012-01", 4
Dim i As Long 'so you can (at any time) enumerate a sorted List this way
For i = 0 To D.Count - 1
Debug.Print D.KeyByIndex(i), D.ItemByIndex(i)
Next
End Sub
For example retrieving by index is not supported, all you can do is pull all of the items out as an array and index into that. This can be an expensive operation unless you want to iterate over all of the items.
Items cannot be ordered, all you have is "garbage in, garbage out" ordering, i.e. items are always retrieved in the order they were added.
Sure, you get the Exists() method, Keys() method, RemoveAll() method, and an option for case-sensitive Keys. And in some scenarios they can give better performance than a Collection, though improper use can squander that advantage and more.
Choosing a Collection, Scripting.Dictionary, or something else depends entirely on how you use the data structure. Neither supports any kind of "sorting" per se, though with a Collection you at least have the option of building it by doing ordered insertion.
Both normally have Key and Item values, but oddly you only showed a single datum. Are you sure you don't want a simple array?
Last edited by dilettante; Dec 1st, 2017 at 12:08 PM.
You could use the RC5-cSortedDictionary (which adds/contains Key/Value-pairs always sorted):
Code:
Option Explicit
Private Sub Form_Load()
Dim D As cSortedDictionary
Set D = New_c.SortedDictionary
'the cSortedDictionary is adding new Key/Values already sorted
D.Add "2017-01", 1
D.Add "2014-03", 2
D.Add "2017-02", 3
D.Add "2012-01", 4
Dim i As Long 'so you can (at any time) enumerate a sorted List this way
For i = 0 To D.Count - 1
Debug.Print D.KeyByIndex(i), D.ItemByIndex(i)
Next
End Sub
Option Explicit
Private List As Collection
Private Declare Function StrCSpn Lib "shlwapi" Alias "StrCSpnW" ( _
ByVal lpStr As Long, _
ByVal lpSet As Long) As Long
Private Function InStrAny(ByRef Str As String, ByRef Delims As String) As Long
Dim Length As Long
Dim Pos As Long
'Find position of first character from the set in Delims within Str.
'Returns 0 if not found.
Length = Len(Str)
If Length > 0 Then
Pos = StrCSpn(StrPtr(Str), StrPtr(Delims)) + 1
If Pos <= Length Then InStrAny = Pos
End If
End Function
Private Sub InsertAscending( _
ByVal Collection As Collection, _
ByRef Item As String)
'Item values are MM-YYYY or MM/YYYY, leading zeros optional.
Dim Min As Long, Middle As Long, Max As Long
Dim Pos As Long, KeyValue As Double, TestItem As String
With Collection
If .Count = 0 Then
.Add Item
Else
Min = 0
Max = .Count - 1
Pos = InStrAny(Item, "-/")
KeyValue = CDbl(DateSerial(CInt(Mid$(Item, Pos + 1)), _
CInt(Left$(Item, Pos - 1)), _
1))
Do Until Min > Max
Middle = (Min + Max) \ 2
TestItem = .Item(Middle + 1)
Pos = InStrAny(TestItem, "-/")
Select Case KeyValue - CDbl(DateSerial(CInt(Mid$(TestItem, Pos + 1)), _
CInt(Left$(TestItem, Pos - 1)), _
1))
Case Is > 0
Min = Middle + 1
Case Is < 0
Max = Middle - 1
Case 0
.Add Item, , , Middle + 1
Exit Sub
End Select
Loop
If Min = Middle Then
.Add Item, , Middle + 1
Else
.Add Item, , , Middle + 1
End If
End If
End With
End Sub
Private Sub Form_Load()
Dim F As Integer
Dim Item As String
Dim I As Long
Set List = New Collection
With Text1
F = FreeFile(0)
Open "data.txt" For Input As #F
Do Until EOF(F)
Line Input #F, Item
.SelText = Item & vbNewLine
InsertAscending List, Item
Loop
Close #F
.SelStart = 0
End With
'Now we have a "sorted" Collection:
With Text2
For I = 1 To List.Count
.SelText = List.Item(I) & vbNewLine
Next
.SelStart = 0
End With
End Sub
Private Sub Form_Resize()
If WindowState <> vbMinimized Then
With Label1
Text1.Move 0, .Height, ScaleWidth / 2, ScaleHeight - .Height
.Move Text1.Left, 0
End With
With Label2
Text2.Move ScaleWidth / 2, .Height, ScaleWidth / 2, ScaleHeight - .Height
.Move Text2.Left, 0
End With
End If
End Sub
Here is the same thing as above (using the same data.txt file, after placing it in c:\temp):
Needs a reference to vbRichClient5 and a List1-ListBox:
Code:
Option Explicit
Private Sub Form_Load()
FillListFromFile List1, "c:\temp\data.txt"
End Sub
Private Sub FillListFromFile(Lst As ListBox, FileName As String)
Dim D As cSortedDictionary, Line, i As Long
Set D = New_c.SortedDictionary(UniqueKeys:=False)
For Each Line In Split(New_c.FSO.ReadTextContent(FileName), vbCrLf)
If Val(Line) Then D.Add CDate(Line), Line 'Keys are Dates, Values the orig. String
Next
Lst.Clear
For i = 0 To D.Count - 1
Lst.AddItem Format(D.KeyByIndex(i), "yyyy-mm") & " (orig: " & D.ItemByIndex(i) & ")"
Next
End Sub
The ListBox-result shows the (sorted as true VB-Dates) result:
it is of course possible to sort "any" array, however you want, with any kind of different syntax, even with / and - mixed.
but you would need to add your own "sort" algorithm, like quicksort, where you don't include the "3" index in the string into the sorting.
of course you will need to "add" those exceptions and that would make the sorting a bit more slower.
however, its not the best way, better use an array that is easy to use and fast to sort. and when you show the date, then you format it.
you got all the suggestions you need from the other members,
i just wanted to point out that "it is possible" to do.
Collections or Dictionaries are not Arrays (since they support Key/Value pairs -
in other words: "a second Column" with additional Item- or Context-Data,
which will then be sorted along with the Keys in a Collection or Dictionary-Sort).
Originally Posted by baka
... with any kind of different syntax, even with / and - mixed.
I was already trying to point out, that in this special case of the OP no complicated parsing is needed,
since VBs CDate-conversion can handle all these cases (paste the line below into your Immediate-Window):
Using the above CDate-Conversion, here's what Array-Sorting can do
(but as said, with a loss of the contextual Info that was yet present in the Dictionary-approach).
Code:
Private Sub FillListFromFile2(Lst As ListBox, FileName As String)
Dim SA() As String, i As Long
SA = Split(New_c.FSO.ReadTextContent(FileName), vbCrLf)
Dim AL As cArrayList: Set AL = New_c.ArrayList(vbDate) '<force vbDate
AL.AddElements SA, 0, UBound(SA) 'auto-conv. String-Arr to vbDate
AL.Sort
Lst.Clear 'clear and re-fill the ListBox from the now sorted ArrayList
For i = 0 To AL.Count - 1: Lst.AddItem Format(AL(i), "yyyy-mm"): Next
End Sub
Collections or Dictionaries are not Arrays (since they support Key/Value pairs -
in other words: "a second Column" with additional Item- or Context-Data
The underlying abstract concept upon which Dictionaries and Hash tables are based is called an associative array. In the simplest terms, it is a type of array where elements can be retrieved by their association with other objects and not an ordinal like an index.
C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter
There's just no reason to use garbage like InputBox. - jmcilhinney
The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber
a diffrent approach.
just load the Data to a Listbox and sort it
Code:
Option Explicit
Private Sub Form_Load() 'Test
List3.AddItem "01-2017"
List3.AddItem "03-2014"
List3.AddItem "02-2017"
List3.AddItem "12/2017"
List3.AddItem "11-2017"
List3.AddItem "10-2017"
List3.AddItem "7/2017"
List3.AddItem "05-2017"
List3.AddItem "5/2017"
List3.AddItem "03-2017"
List3.AddItem "08/2012"
End Sub
Private Sub Command3_Click()
Dim s As String, s1 As String, z As String
Dim i As Long, j As Long
For i = 0 To List3.ListCount - 1
'swap the / for -
s = Replace(List3.List(i), "/", "-")
s1 = ""
For j = 1 To Len(s)
z = Mid(s, j, 1)
If Len(z) Then
s1 = s1 & z
End If
Next
List2.AddItem Format$(s1, "yyyy-mm")
Next
End Sub
Private Sub Command5_Click()
'now sort the Dates
Dim i As Integer, x As Integer, remove As Integer
Dim date1 As String
For i = List2.ListCount - 1 To 0 Step -1
date1 = List2.List(i)
remove = i
For x = 0 To i - 1
If DateValue(List2.List(x)) < DateValue(date1) Then
date1 = List2.List(x)
remove = x
End If
Next x
List2.RemoveItem remove
List2.AddItem date1
Next i
End Sub
regards
Chris
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.
... just load the Data to a Listbox and sort it ...
I'll probably never understand the obsession, to handle even basic, data-related tasks in GUI-Controls.
That's just wrong - such tasks should be done on the Data-Containers (which contain typed-values, not Strings).
Here again, how to do it by using an ADO.Recordset:
Code:
Private Sub Form_Load() 'Test
Dim Rs As Object, i As Long
Set Rs = CreateObject("ADODB.Recordset")
Rs.Fields.Append "Orig", vbString
Rs.Fields.Append "Date", vbDate
Rs.Open 'the above lines create a free-standing Rs with a String- and a Date-Column
With Rs 'now add Records
.AddNew 0, "01-2017": !Date = !Orig '<- convert from vbString to vbDate
.AddNew 0, "03-2014": !Date = !Orig
.AddNew 0, "02-2017": !Date = !Orig
.AddNew 0, "12/2017": !Date = !Orig
.AddNew 0, "11-2017": !Date = !Orig
.AddNew 0, "10-2017": !Date = !Orig
.AddNew 0, "7/2017": !Date = !Orig
.AddNew 0, "05-2017": !Date = !Orig
.AddNew 0, "5/2017": !Date = !Orig
.AddNew 0, "03-2017": !Date = !Orig
.AddNew 0, "08/2012": !Date = !Orig
End With
Rs.Sort = "Date Asc" 'sort the Rs by its Date-Column
For i = 1 To Rs.RecordCount
Rs.Absoluteposition = i
Debug.Print Format(Rs!Date, "yyyy-mm"), "Orig-Value: "; Rs!Orig
Next
End Sub
I'll probably never understand the obsession, to handle even basic, data-related tasks in GUI-Controls.
That's just wrong - such tasks should be done on the Data-Containers (which contain typed-values, not Strings).
Olaf
Hi Olaf,
you used the Listbox in Post#12 and 14 ?
thought that was what the OP wanted.
regards
Chris
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.
ChrisE, that would work of course, but it is bad.
the reason is that the list will update every time you change an item and that takes cpu and time, second you are using a sequential sort method, and while it works, its not the fastest way, and third as Schmidt already pointed out, we should always think performance, and in this case, we don't need strings, and if we don't need, then we should not use. of course we can do it in many ways, theres a lot of different ways to do this, but we should try to avoid bad code.
just try to sort 100.000 items using your method and you will know what we mean.
ChrisE, that would work of course, but it is bad.
the reason is that the list will update every time you change an item and that takes cpu and time, second you are using a sequential sort method, and while it works, its not the fastest way, and third as Schmidt already pointed out, we should always think performance, and in this case, we don't need strings, and if we don't need, then we should not use. of course we can do it in many ways, theres a lot of different ways to do this, but we should try to avoid bad code.
just try to sort 100.000 items using your method and you will know what we mean.
Hi baka,
i know it's bad Code, like I said I thought it was what the OP wanted.
I would read the Data directly to a new Table or append to an exixting one
The Table Data with format(yyyy-mm) already in place
Code:
Private Sub Command22_Click()
Dim strSql As String
'Insert to new Table
' strSql = "Select * Into tbl_Import From [data.txt] IN 'c:\' 'Text;'"
'Append to Table
strSql = "Insert Into tbl_ImportDate Select * From [data.txt] In 'C:\' 'Text;'"
adoConnection.Execute strSql
End Sub
Image after Import
regards
Chris
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.
Hi Olaf,
you used the Listbox in Post#12 and 14 ?
thought that was what the OP wanted.
The OP didn't mention any ListBoxes IIRC...
I've used the ListBoxes (already available in the vbRuntime) as "simple, but dumb renderers"
(in my examples for the DataContainers, which did the conversion- and final sorting).
If the Count of "Records" (or Key/Value-Pairs) is low (below 100 or so) - and no special Formatting is needed,
then a VB.ListBox is often enough to (re-)render the potentially changed Content of such DataContainers (with a preceding List.Clear).
But if the amount of data in the DataContainers is larger, then virtual List- or DataGrid-Views should be used
(virtual GUI-ListControls do not hold any data, which is IMO the right thing to do).
We have only two real virtual List-Controls (though slightly buggy ones) available with VB6:
- the VB-DataGrid (not meaning the FlexGrid or the MSHFlex)
- and the VB-DataRepeater
And both are not really as flexible as they could be, since they support only a single,
bindable DataContainer-Type (ADO-Recordsets).
The whole thing boils down not as much to performance (which is usually better with virtual Controls and bound data-sources),
but to a separation of what belongs to:
- the "Model" ... and ...
- the "View"
All modern GUI-development approaches have a clear separation in that regard
(WebApps with modern js-Frameworks among them).
I've used the ListBoxes (already available in the vbRuntime) as "simple, but dumb renderers"
(in my examples for the DataContainers, which did the conversion- and final sorting).
If the Count of "Records" (or Key/Value-Pairs) is low (below 100 or so) - and no special Formatting is needed,
then a VB.ListBox is often enough to (re-)render the potentially changed Content of such DataContainers (with a preceding List.Clear).
But if the amount of data in the DataContainers is larger, then virtual List- or DataGrid-Views should be used
(virtual GUI-ListControls do not hold any data, which is IMO the right thing to do).
We have only two real virtual List-Controls (though slightly buggy ones) available with VB6:
- the VB-DataGrid (not meaning the FlexGrid or the MSHFlex)
- and the VB-DataRepeater
And both are not really as flexible as they could be, since they support only a single,
bindable DataContainer-Type (ADO-Recordsets).
The whole thing boils down not as much to performance (which is usually better with virtual Controls and bound data-sources),
but to a separation of what belongs to:
- the "Model" ... and ...
- the "View"
All modern GUI-development approaches have a clear separation in that regard
(WebApps with modern js-Frameworks among them).
Olaf
ups sorry, my mistake then.
regards
Chris
to hunt a species to extinction is not logical !
since 2010 the number of Tigers are rising again in 2016 - 3900 were counted. with Baby Callas it's 3901, my wife and I had 2-3 months the privilege of raising a Baby Tiger.