VBScriptでLINQみたいな事をする

仕事でどうしてもVBScriptで新規コードを書かねばならず、LINQに慣れ過ぎてしまった私には、もはやLINQの無い世界は考えられなかったのです。

Windows 2000 Server+IIS5。.NETも入っていません。

検索すると、こんな素敵なクラスを作ってくださってる方がいらっしゃいました。

[[VBS]ArrayListの代替クラス](https://yizm.work/sample_code/vbscript/vbsarraylist_class/)

素敵です。

しかし、いくら素敵なリストクラスがあっても、WhereやSelectが無いのでは、使いでがありません。
かといって、VBScriptには匿名関数などといった素敵なものはなく、引数に評価関数を渡す方法が無い以上、どうしようもありません・・・いや待て、我々にはEval関数があるではないですか！

そんなわけで、こんな風に書ける事を確認したので安心して今日は寝ます。

同じ地獄にいるみんなの心が多少なりとも癒されることを祈りつつ。

```vb
    Dim list
    Set list = New ArrayList

    Call list.Add( (New Person)( "001", "Taro Yamada", 20 ) )
    Call list.Add( (New Person)( "002", "Mike Davis", 20 ) )
    Call list.Add( (New Person)( "003", "Tom Wilson", 21 ) )

    Dim i
    For Each i In list.Where( "item.Age = 20").Items
        MsgBox i.Name 'Taro Yamada、Mike Davis を順に表示
    Next

    For Each i In list.Where( "item.Age = 20").Map( "item.ID & "":"" & item.Name" ).Items
        MsgBox i '001:Taro Yamada、002:Mike Davis を順に表示
    Next
```

　全てのソースコードはこちら。

```vb:ArrayList.vbs
Call Main()

Sub Main

    Dim list
    Set list = New ArrayList

    Call list.Add( (New Person)( "001", "Taro Yamada", 20 ) )
    Call list.Add( (New Person)( "002", "Mike Davis", 20 ) )
    Call list.Add( (New Person)( "003", "Tom Wilson", 21 ) )

    Dim i
    For Each i In list.Where( "item.Age = 20").Items
        MsgBox i.Name
    Next

    For Each i In list.Where( "item.Age = 20").Map( "item.ID & "":"" & item.Name" ).Items
        MsgBox i
    Next

End Sub

Class ArrayList 
    Private pItems
    Private pKeyMax

    Private Sub Class_Initialize
        Set pItems = Server.CreateObject("Scripting.Dictionary")
        pKeyMax = 0
    End Sub

    Private Sub Class_Terminate
        Set pItems = Nothing
    End Sub

    Public Property Get Count
        Count = pItems.Count
    End Property

    Public Property Get Items
        Items = pItems.Items
    End Property

    Public Sub Add( item )
        pItems.Add pKeyMax, item
        pKeyMax = pKeyMax + 1
    End Sub

    Public Function Where( expr )
        Dim list
        Set list = New ArrayList
        Dim item
        For Each item In pItems.Items
            If Eval( expr ) Then
                list.Add item      
            End If
        Next
        Set Where = list
    End Function

    Public Function Map( expr )
        Dim list
        Set list = New ArrayList
        Dim item
        For Each item In pItems.Items
            list.Add Eval( expr )
        Next
        Set Map = list
    End Function

End Class

Class Person
    Public ID
    Public Name
    Public Age

    Private Sub Class_Initialize
        ID = ""
        Name = ""
        Age = 0
    End Sub

    Public Default Property Get Init( pid, pname, page )
        ID = pid
        Name = pname
        Age = page
        Set Init = Me
    End Property

End Class

```

追記！

パワーアップして、C#のラムダ式っぽく、要素を渡す変数名も書けるようにしてみました。
けど、要素名はitem固定でいいと思うので、こっちは使わないかな？

```vb:ArrayList
Call Main()

Sub Main

    Dim list
    Set list = New ArrayList

    Call list.Add( (New Person)( "001", "Taro Yamada", 20 ) )
    Call list.Add( (New Person)( "002", "Mike Davis", 20 ) )
    Call list.Add( (New Person)( "003", "Tom Wilson", 21 ) )

    Dim item
    For Each item In list.Where( "p => p.Age = 20").Items
        MsgBox item.Name
    Next

    For Each item In list.Where( "p => p.Age = 20").Map( "p => p.ID & "":"" & p.Name" ).Items
        MsgBox item
    Next

End Sub

Class ArrayList 
    Private pItems
    Private pKeyMax

    Private Sub Class_Initialize
        Set pItems = Server.CreateObject("Scripting.Dictionary")
        pKeyMax = 0
    End Sub

    Private Sub Class_Terminate
        Set pItems = Nothing
    End Sub

    Public Property Get Count
        Count = pItems.Count
    End Property

    Public Property Get Items
        Items = pItems.Items
    End Property

    Public Sub Add( item )
        pItems.Add pKeyMax, item
        pKeyMax = pKeyMax + 1
    End Sub

    Public Function Where( expr )   
        Dim paramName, exprRight 
        paramName = Split( expr, "=>")(0)
        exprRight = Split( expr, "=>")(1)

        Dim list
        Set list = New ArrayList
        Dim itemInner
        For Each itemInner In pItems.Items
            Execute( "Dim " & paramName )
            Execute( "Set " & paramName & " = itemInner"  )
            If Eval( exprRight ) Then
                list.Add itemInner      
            End If
        Next
        Set Where = list
    End Function

    Public Function Map( expr )
        Dim paramName, exprRight 
        paramName = Split( expr, "=>")(0)
        exprRight = Split( expr, "=>")(1)

        Dim list
        Set list = New ArrayList
        Dim itemInner
        For Each itemInner In pItems.Items
            Execute( "Dim " & paramName )
            Execute( "Set " & paramName & " = itemInner"  )
            list.Add Eval( exprRight )
        Next
        Set Map = list
    End Function

End Class

Class Person
    Public ID
    Public Name
    Public Age

    Private Sub Class_Initialize
        ID = ""
        Name = ""
        Age = 0
    End Sub

    Public Default Property Get Constructor( pid, pname, page )
        ID = pid
        Name = pname
        Age = page
        Set Init = Me
    End Property

End Class
```