インストールレスプログラミング( ´ー`)

VBA , JavaScript , HTAなど 365アプリはインストール必要ですが、仕事に無いケースはほぼないから(・_・;)

Excelデータを扱う コレクションラッパークラス②

chemiphys.hateblo.jp
つづきです。

かなりの部分のコードはthomさんのこの記事を参考・・というかけっこうそのまま使わせていただいているところが多々。
thom.hateblo.jp

あと,一連の記事で学んだことも多々利用しています。
chemiphys.hateblo.jp

さて,大変な作業が始まるかと思いきや,なんか形になりそうなので,進捗状況です。

細かいところを実装する前に,まずスタート地点の確認。

標準モジュール

Sub Test()
    Dim Data As DataCollection: Set Data = New DataCollection
    
    Data.GetDataAsCollection ThisWorkbook.Worksheets(1).Range("a1"), True
    
    Data.Sort 6, False
    
    Data.Output ThisWorkbook.Worksheets(1).Range("a11"), False

End Sub

DataCollection.cls
各種メソッドやプロパティを実装するクラスになります。 ※GetDataAsCollectionをFunction→Subに変更しました。

Option Explicit
Private Col As Collection
Private LabelCol As DataUnit
Private ラベル有 As Boolean
Private ParameterCount As Long

Sub GetDataAsCollection(BaseRange As Range, 先頭行ラベル As Boolean)
    Dim arr: arr = BaseRange.CurrentRegion.value
    Set Col = New Collection
    Set LabelCol = New DataUnit
    Dim i, j
    
    ParameterCount = UBound(arr, 2)
    For i = LBound(arr, 1) To UBound(arr, 1)
        With New DataUnit
            .SetParameterCount ParameterCount
            For j = 1 To ParameterCount
                .LetParameter j, arr(i, j)
            Next
            Col.Add .Self
        End With
    Next
    
    If 先頭行ラベル = True Then
        Set LabelCol = Col(1)
        Col.Remove 1
        ラベル有 = True
    End If
    
End Sub

Sub Output(BaseRange As Range, ラベル出力 As Boolean)
    Dim n As Long: n = 1
    Dim d As DataUnit
    Dim i As Long
    If ラベル出力 = True And ラベル有 = True Then
        Col.Add LabelCol, , 1
    End If
    Dim RangeArray(): ReDim RangeArray(1 To Col.Count, 1 To ParameterCount)
    
    For Each d In Col
        For i = 1 To ParameterCount
            RangeArray(n, i) = d.GetParameter(i)
        Next
        n = n + 1
    Next

    BaseRange.Resize(Col.Count, ParameterCount).value = RangeArray

End Sub

Sub Sort(Key As Long, 昇順 As Boolean)
    Dim d1 As Variant, d2 As Variant
    'バブルソート
    Dim i As Long, j As Long
    For i = 1 To Col.Count - 1
        For j = 1 To Col.Count - i
            
            d1 = CallByName(Col(j), "GetParameter", VbMethod, Key)
            d2 = CallByName(Col(j + 1), "GetParameter", VbMethod, Key)
            
            If IsGreater(昇順, d1, d2) Then CollectionSwap Col, j, j + 1
        Next j
    Next i

End Sub
Sub CollectionSwap(pCol As Collection, Index1 As Long, Index2 As Long)
    Dim Item1 As Variant, Item2 As Variant
    Set Item1 = pCol.Item(Index1)
    Set Item2 = pCol.Item(Index2)
    
    pCol.Add Item1, after:=Index2
    pCol.Remove Index2
    pCol.Add Item2, after:=Index1
    pCol.Remove Index1
End Sub

Function IsGreater(which, A, B) As Boolean
    Select Case which
        Case True: IsGreater = A > B
        Case False: IsGreater = A < B
    End Select
End Function

DataUnit.cls
データの構造体を規定しているクラスです。最低限の部分しかありませんが,汎用を考えているのでたぶんあまり変わっていかない。

Option Explicit
Private Parameter()

Property Get Self() As Object
    Set Self = Me
End Property

Sub SetParameterCount(パラメーター数 As Long)
    ReDim Parameter(1 To パラメーター数)
End Sub

Sub LetParameter(paramNo, value)
    Parameter(paramNo) = value
End Sub

Function GetParameter(paramNo) As Variant
    GetParameter = Parameter(paramNo)
End Function

試した結果はこんな感じ。
f:id:chemiphys:20170204112041p:plain

標準モジュールでは
Dim Data As DataCollection: Set Data = New DataCollection
宣言して
Data.GetDataAsCollection (ThisWorkbook.Worksheets(1).Cells(1, 1))
データを取得します。Cells(1,1)つまりA1からCurrentRegionで取得できるデータをコレクション化して保持します。
Data.Sort 6, False
1キーのバブルソートを実装しましたので,列6,降順でソートさせてみた
Data.Output ThisWorkbook.Worksheets(1).Range("a11")
結果をa11起点で出力します。


まだまだ,未実装のものもたくさんありますし,数値データはローカルウィンドウで確認したらDoubleになっていました。
小数もあるから まずLongはだめやんと,そこは納得し,Doubleのままでいいやとそこは仕様を改善する。

勢い的にはできそうな勢いを感じますし,標準モジュール上もすっきりなのでご機嫌です。

仕様にはありませんでしたが,CurrentRegionから取得するとき,一行目がラベルかどうかってのを引数で指定できるようにしないといけないな。
そして,ラベルも保持しておいて,出力時出す出さないを選択できるようにしたい。

一部無意図にFunctionとしていたところがあったので修正。その時にラベルの有無を実装しました。

今日外出する時間帯があるのでどこまで進めるかはわかりませんが,なんとなくいいスタートです。