読者です 読者をやめる 読者になる 読者になる

VBA 配列でつくったテーブルにSQLっぽいことしてみる(パクリ)

CallMeKoheiさんのブログ記事「VBA 配列でつくったテーブルにSQLっぽいことしてみる(その2)」をパクってみるという、はなし。


    arr = Array(Array(Array("品番", "a"), Array("品名", "apple"), Array("価格", 100#)), _
                Array(Array("品番", "b"), Array("品名", "banana"), Array("価格", 200#)), _
                Array(Array("品番", "c"), Array("品名", "cccc"), Array("価格", 300#)))

    arr2 = SQL(SELECT_("品番"), FROM(arr), WHERE(f))

上のようなジャグ配列 arr をテーブルと見立てて、SQLっぽくセレクトするというわけだ。
これをVBAHaslellでサンプルプログラムを書いてみる。
まず SQL(SELECT_(*), FROM(*), WHERE(*)) の部分だが、ひとつの関数に簡略化してしまう。
select_from_where(selector, from, where) というインタフェースだ。
・ selector はカラム選択関数で、何個目のカラム名を選ぶかのインデックスを返す
・ fromは対象テーブルそのもの
・ whereはレコード選択の関数

サンプルために、
・ カラム名を配列で渡すとその位置を返す関数select_col
・ 存在しないカラムだった場合に返ってくるNullを無視する関数remain_valid
をヘルパ関数として作った。

結果としてselect_from_whereの実装はこうなった。

Function select_from_where(ByRef selector As Variant, ByRef from_ As Variant, ByRef where_ As Variant) As Variant
    Dim where_result As Variant, select_index As Variant

    If count_if(where_, from_) = 0 Then
        select_from_where = VBA.Array()
    Else
        ' WHERE でレコードをフィルタリング
        where_result = filterR(from_, mapF(where_, from_))
        ' SELECTORで各レコードの対象カラムのインデックスを抽出
        select_index = mapF(selector, where_result)
        ' インデックスからNullを削除
        select_index = mapF(p_remain_valid, select_index)
        ' 各レコードから対象カラムを抽出
        select_from_where = zipWith(p_subV, where_result, select_index)
    End If
End Function

上に書いた arr を対象テーブルとして、セレクタを、p_select_col(Array("品名", "品番", "価格")) 、WHEREは無指定(恒真関数p_true)とすると以下のように出力される。

品名 apple
品番 a
価格 100
---------------
品名 banana
品番 b
価格 200
---------------
品名 cccc
品番 c
価格 300
---------------

SELECTは単にSELECT xxxx, yyyy だけでなく、SELECT xxxx + yyyy, zzzz のようなことができた方がいいが、今回は考えていない。

サンプルプログラムは、モジュールインポート済みWORDファイル(64bit版 , 32bit版 )にSub select_from_where_Testとして追加した。