0

I'm trying to sort lines in a file using VBScript (MS Internet Explorer). Basically I read the file and converted all the lines to an array. Basically I need to sort on whatever is written between character 23 and 27 of each line. So basically I can get those characters using MID. These characters are not unique per line (so I can't use System.Collections.Sortedlist directly, or something similar unfortunately).

I may be able to use System.Collections.Sortedlist and use the characters between 23 and 27 as a key and use an other System.Collections.Sortedlist object as a value, filled with every unique value (so a 2-dimensional array), but will this be the right way? e.g.:

(System.Collections.Sortedlist) {
    "0001" => (System.Collections.Sortedlist) {
        0 => "0001 some unique value1"
        1 => "0001 some unique value2"
        2 => "0001 some unique value3"
    },
    "0002" => (System.Collections.Sortedlist) {
        0 => "0002 some unique value1"
        1 => "0002 some unique value2"
        2 => "0002 some unique value3"
    }
}

Is there a better way to handle this? Also note the output format should be an array again.

van Nijnatten
  • 404
  • 5
  • 15
  • 1
    VBScript? in IE? Have I fallen into a time-warp and it's 2001 all over again? – SDC Jan 28 '13 at 14:12
  • Well.. The client uses it. And take a year from that. I'm currently working on a win2k machine. Fast as hell but really stripped down. I can't even install the .NET 3.5 framework.. – van Nijnatten Jan 28 '13 at 14:17
  • Wow, that sounds like a web developer's special version of hell. I'll bet they're stuck on IE6 as well, right? If you've got working code already, I suggest using it; don't worry about finding a better way to do it; the client clearly isn't interested in best practice anyway. – SDC Jan 28 '13 at 14:24
  • 1
    @JvN - look here: http://stackoverflow.com/a/13758037/603855 for ideas how to use a disconnected recordset or a .Net collection (and some extra work). – Ekkehard.Horner Jan 28 '13 at 14:51
  • @Ekkehard.Horner - Uhm. That looks like a nice object. Thanks! – van Nijnatten Jan 28 '13 at 15:47

2 Answers2

3

As teaser (see also ArrayList, Disconnected Recordset and ArrayList ):

  Dim aTest : aTest = Array( _
      ". Z 0" _
    , ", B 2" _
    , "? A 1" _
  )
  WScript.Echo "----- ArrayList"
  WScript.Echo "original data:     ", "[""" & Join(aTest                          , """  """) & """]"
  WScript.Echo "AL:  (full)        ", "[""" & Join(sortAL(aTest)                  , """  """) & """]"
  WScript.Echo "AL:  (numerical)   ", "[""" & Join(sortAL(Array(12, 9, -1))       , """  """) & """]"
  WScript.Echo "AL:  (dates)       ", "[""" & Join(sortAL(Array(Now + 5, Now - 5)), """  """) & """]"
  WScript.Echo "AL:  (Mid(elm,3,1))", "[""" & Join(sortAL_31(aTest)               , """  """) & """]"
  WScript.Echo "----- Disconnected Recordset"
  WScript.Echo "DRS: (Mid(elm,3,1))", "[""" & Join(sortDRS_Substring(aTest, 3, 1) , """  """) & """]"
  WScript.Echo "DRS: (Mid(elm,5,1))", "[""" & Join(sortDRS_Substring(aTest, 5, 1) , """  """) & """]"

' ArrayList.Sort handles homogenous arrays of simple types just fine,
' but looks at the element in full. For other cases, you need extra work
' or ADO
Function sortAL(aX)
  Dim al : Set al = CreateObject("System.Collections.ArrayList")
  Dim elm
  For Each elm In aX
      al.Add elm
  Next
  al.Sort
  sortAL = al.ToArray()
End Function

' Extra work: feeding pre-processed data to the ArrayList; hardcoded
Function sortAL_31(aX)
  Const csSep = ":"
  Dim al : Set al = CreateObject("System.Collections.ArrayList")
  ReDim a(UBound(aX))
  Dim elm
  For Each elm In aX
      al.Add Mid(elm, 3, 1) & csSep & elm
  Next
  al.Sort
  For elm = 0 To UBound(a)
      a(elm) = Split(al(elm), csSep)(1)
  Next
  sortAL_31 = a
End Function

' more flexible: ADO DRS + a start at parametrization; handling different
' types is left as an exercise
Function sortDRS_Substring(aX, nFrom, nLen)
  Const adVarChar    = 200 ' 000000C8
  Const adClipString =   2 ' 00000002
  Const csSep        = ":"
  ReDim a(Ubound(aX))
  Dim oRS : Set oRS = CreateObject("ADODB.Recordset")
  oRS.Fields.Append "FORG", adVarChar, 250
  oRS.Fields.Append "FSUB", adVarChar, 250
  oRS.Open
  Dim elm
  For Each elm In aX
      oRS.AddNew
      oRS.Fields("FORG").value = elm
      oRS.Fields("FSUB").value = Mid(elm, nFrom, nLen)
      oRS.UpDate
  Next
  oRS.Sort = "FSUB"
  oRS.MoveFirst
  For elm = 0 To UBound(a)
      a(elm) = oRS.Fields("FORG").value
      oRS.MoveNext
  Next
  sortDRS_Substring = a
End Function

output:

----- ArrayList
original data:      [". Z 0"  ", B 2"  "? A 1"]
AL:  (full)         [", B 2"  ". Z 0"  "? A 1"]
AL:  (numerical)    ["-1"  "9"  "12"]
AL:  (dates)        ["1/23/2013 5:00:22 PM"  "2/2/2013 5:00:22 PM"]
AL:  (Mid(elm,3,1)) ["? A 1"  ", B 2"  ". Z 0"]
----- Disconnected Recordset
DRS: (Mid(elm,3,1)) ["? A 1"  ", B 2"  ". Z 0"]
DRS: (Mid(elm,5,1)) [". Z 0"  "? A 1"  ", B 2"]
Community
  • 1
  • 1
Ekkehard.Horner
  • 38,498
  • 2
  • 45
  • 96
1

The function I thought to suggest is almost the same as Ekkehard Horner's sortAL_31.

Function CustomSort(ByVal inpArray, ByVal nFrom, ByVal nLen)
    Dim oList, aTmp(), nSize, i
    nSize = UBound(inpArray)
    ReDim aTmp(nSize)
    With CreateObject("System.Collections.ArrayList")
        For i = 0 To nSize
            .Add Mid(inpArray(i), nFrom, nLen) & _
                vbNullChar & inpArray(i)
        Next
        .Sort
        For i = 0 To nSize
            aTmp(i) = Split(.Item(i), vbNullChar)(1)
        Next
    End With
    CustomSort = aTmp
End Function

[Edit] Append version with SortedList.

Function CustomSort2(inpArray, nFrom, nLen)
    Dim oSList, aTmp(), nSize, i
    nSize = UBound(inpArray)
    ReDim aTmp(nSize)
    Set oSList = CreateObject("System.Collections.SortedList")
    For i = 0 To nSize
        oSList.Add Mid(inpArray(i), nFrom, nLen), inpArray(i)
    Next
    For i = 0 To nSize
        aTmp(i) = oSList.GetByIndex(i)
    Next
    Set oSList = Nothing
    CustomSort2 = aTmp
End Function
Panayot Karabakalov
  • 3,109
  • 3
  • 19
  • 28
  • what I like (+1.3): using vbNullChar as separator; what I don't like (-0.2): aTmp() is a VB/VBA idiom foreign to VBScript (try to get its UBound()); what I wouldn't do (-0.05): using ByVal on any of the params - especially the copy of inpArray makes no sense. – Ekkehard.Horner Jan 28 '13 at 18:46
  • Thanks, I appreciate your comment. As for aTmp() I don't really get it what you mean. I just try to reduce call to UBound() to just once. Or you mean to omit Dim aTmp() and go directly with ReDim aTmp(nSize)? – Panayot Karabakalov Jan 28 '13 at 18:57
  • yes, "Dim aTmp()" creates an abomination which VBScript can't even UBound(); a plain "ReDim aTmp()" is sufficient (even for "Option Explicit"). – Ekkehard.Horner Jan 28 '13 at 19:02