web-dev-qa-db-ja.com

ReDimはVisual Basic 6の多次元配列に保存します

VB6を使用していますが、ReDim Preserveを多次元配列に行う必要があります。

 Dim n, m As Integer
    n = 1
    m = 0
    Dim arrCity() As String
    ReDim arrCity(n, m)

    n = n + 1
    m = m + 1
    ReDim Preserve arrCity(n, m)

書いたとおりに行うと、次のエラーが発生します。

ランタイムエラー9:範囲外の添字

最後の配列次元のみを変更できるため、タスクでは配列全体(この例では2次元)を変更する必要があります!

これに対する回避策または別の解決策はありますか?

15
Ouerghi Yassine

正しく指摘すると、ReDim Preserve配列の最後の次元のみ(MSDNの- ReDimステートメント ):

Preserveキーワードを使用する場合、サイズ変更できるのは最後の配列次元のみであり、次元数をまったく変更することはできません。たとえば、配列に1つの次元しかない場合、その次元は最後で唯一の次元であるため、その次元のサイズを変更できます。ただし、配列に2つ以上の次元がある場合、最後の次元のみのサイズを変更しても、配列の内容を保持できます

したがって、決定する最初の問題は、2次元配列がジョブに最適なデータ構造であるかどうかです。おそらく、1次元配列は、ReDim Preserve

別の方法は、 Pieter Geerkensの提案 に従ってギザギザの配列を使用することです。 VB6ではギザギザの配列を直接サポートしていません。 VB6で「配列の配列」をコーディングする1つの方法は、Variantの配列を宣言し、各要素を目的のタイプの配列(あなたの場合はString)にすることです。デモコードは次のとおりです。

さらに別のオプションは、Preserve部分を独自に実装することです。そのためには、保存するデータのコピーを作成し、それを再次元化された配列に入力する必要があります。

Option Explicit

Public Sub TestMatrixResize()
    Const MAX_D1 As Long = 2
    Const MAX_D2 As Long = 3

    Dim arr() As Variant
    InitMatrix arr, MAX_D1, MAX_D2
    PrintMatrix "Original array:", arr

    ResizeMatrix arr, MAX_D1 + 1, MAX_D2 + 1
    PrintMatrix "Resized array:", arr
End Sub

Private Sub InitMatrix(a() As Variant, n As Long, m As Long)
    Dim i As Long, j As Long
    Dim StringArray() As String

    ReDim a(n)
    For i = 0 To n
        ReDim StringArray(m)
        For j = 0 To m
            StringArray(j) = i * (m + 1) + j
        Next j
        a(i) = StringArray
    Next i
End Sub

Private Sub PrintMatrix(heading As String, a() As Variant)
    Dim i As Long, j As Long
    Dim s As String

    Debug.Print heading
    For i = 0 To UBound(a)
        s = ""
        For j = 0 To UBound(a(i))
            s = s & a(i)(j) & "; "
        Next j
        Debug.Print s
    Next i
End Sub

Private Sub ResizeMatrix(a() As Variant, n As Long, m As Long)
    Dim i As Long
    Dim StringArray() As String

    ReDim Preserve a(n)
    For i = 0 To n - 1
        StringArray = a(i)
        ReDim Preserve StringArray(m)
        a(i) = StringArray
    Next i
    ReDim StringArray(m)
    a(n) = StringArray
End Sub
6
Ilya Kurnosov

VB6はVBAに非常に似ているため、ReDimを使用して2次元配列にTransposeを使用するのにそれほど多くのコードを必要としないソリューションがあると思います。

ソリューション(VBA):

Dim n, m As Integer
n = 2
m = 1
Dim arrCity() As Variant
ReDim arrCity(1 To n, 1 To m)

m = m + 1
ReDim Preserve arrCity(1 To n, 1 To m)
arrCity = Application.Transpose(arrCity)
n = n + 1
ReDim Preserve arrCity(1 To m, 1 To n)
arrCity = Application.Transpose(arrCity)

OPの質問と異なる点は、arrCity配列の下限は0ではなく1です。これは、Application.Transposeに仕事をさせるためです。

VB6にはTransposeメソッドが必要だと思います。

4
ZygD

これに関して:

「私のタスクでは、配列全体(2次元)を変更する必要があります」

ギザギザの配列(つまり、値の配列の配列)を使用するだけです。その後、必要に応じて寸法を変更できます。おそらくもう少し作業が、解決策。

2
Pieter Geerkens

これらの答えのすべてをテストしたわけではありませんが、これを達成するために複雑な機能を使用する必要はありません。それよりもずっと簡単です!以下の私のコードは、すべてのオフィスVBAアプリケーション(Word、Access、Excel、Outlookなど)で動作し、非常に簡単です。お役に立てれば:

''Dimension 2 Arrays
Dim InnerArray(1 To 3) As Variant ''The inner is for storing each column value of the current row
Dim OuterArray() As Variant ''The outer is for storing each row in
Dim i As Byte

    i = 1
    Do While i <= 5

        ''Enlarging our outer array to store a/another row
        ReDim Preserve OuterArray(1 To i)

        ''Loading the current row column data in
        InnerArray(1) = "My First Column in Row " & i
        InnerArray(2) = "My Second Column in Row " & i
        InnerArray(3) = "My Third Column in Row " & i

        ''Loading the entire row into our array
        OuterArray(i) = InnerArray

        i = i + 1
    Loop

    ''Example print out of the array to the Intermediate Window
    Debug.Print OuterArray(1)(1)
    Debug.Print OuterArray(1)(2)
    Debug.Print OuterArray(2)(1)
    Debug.Print OuterArray(2)(2)
2

私はこれが少し古いことを知っていますが、追加のコーディングを必要としないはるかに簡単なソリューションがあるかもしれないと思います:

転置、再配置、再転置の代わりに、2次元配列について説明する場合は、最初に転置された値だけを保存してください。その場合、redim preserveは実際には最初から右(2番目)の次元を増やします。または、言い換えれば、それを視覚化するために、列のnrのみをredim preserveで増やすことができる場合は、2列ではなく2行で保存してください。

インデックスは、00-01、10-11、20-21ではなく00-01、01-11、02-12、03-13、04-14、05-15 ... 0 25-1 25などです、30-31、40-41など。

再保存する必要があるディメンションが1つだけである限り、アプローチは引き続き機能します。そのディメンションを最後に置くだけです。

2番目の(または最後の)次元のみがリダイム中に保持できるため、これが配列の使用方法を最初から想定していると主張することができます。私はどこでもこの解決策を見たことがないので、何かを見落としているかもしれませんか?

(2つの次元に関する同様の質問に以前に投稿し、より多くの次元についてはここに回答を拡張しました)

1
hombibi

内部配列となる文字列の配列を含むユーザー定義型を使用できます。次に、このユーザー定義型の配列を外部配列として使用できます。

次のテストプロジェクトをご覧ください。

'1 form with:
'  command button: name=Command1
'  command button: name=Command2
Option Explicit

Private Type MyArray
  strInner() As String
End Type

Private mudtOuter() As MyArray

Private Sub Command1_Click()
  'change the dimensens of the outer array, and fill the extra elements with "1"
  Dim intOuter As Integer
  Dim intInner As Integer
  Dim intOldOuter As Integer
  intOldOuter = UBound(mudtOuter)
  ReDim Preserve mudtOuter(intOldOuter + 2) As MyArray
  For intOuter = intOldOuter + 1 To UBound(mudtOuter)
    ReDim mudtOuter(intOuter).strInner(intOuter) As String
    For intInner = 0 To UBound(mudtOuter(intOuter).strInner)
      mudtOuter(intOuter).strInner(intInner) = "1"
    Next intInner
  Next intOuter
End Sub

Private Sub Command2_Click()
  'change the dimensions of the middle inner array, and fill the extra elements with "2"
  Dim intOuter As Integer
  Dim intInner As Integer
  Dim intOldInner As Integer
  intOuter = UBound(mudtOuter) / 2
  intOldInner = UBound(mudtOuter(intOuter).strInner)
  ReDim Preserve mudtOuter(intOuter).strInner(intOldInner + 5) As String
  For intInner = intOldInner + 1 To UBound(mudtOuter(intOuter).strInner)
    mudtOuter(intOuter).strInner(intInner) = "2"
  Next intInner
End Sub

Private Sub Form_Click()
  'clear the form and print the outer,inner arrays
  Dim intOuter As Integer
  Dim intInner As Integer
  Cls
  For intOuter = 0 To UBound(mudtOuter)
    For intInner = 0 To UBound(mudtOuter(intOuter).strInner)
      Print CStr(intOuter) & "," & CStr(intInner) & " = " & mudtOuter(intOuter).strInner(intInner)
    Next intInner
    Print "" 'add an empty line between the outer array elements
  Next intOuter
End Sub

Private Sub Form_Load()
  'init the arrays
  Dim intOuter As Integer
  Dim intInner As Integer
  ReDim mudtOuter(5) As MyArray
  For intOuter = 0 To UBound(mudtOuter)
    ReDim mudtOuter(intOuter).strInner(intOuter) As String
    For intInner = 0 To UBound(mudtOuter(intOuter).strInner)
      mudtOuter(intOuter).strInner(intInner) = CStr((intOuter + 1) * (intInner + 1))
    Next intInner
  Next intOuter
  WindowState = vbMaximized
End Sub

プロジェクトを実行し、フォームをクリックして配列の内容を表示します。

Command1をクリックして外側の配列を拡大し、フォームをクリックして結果を表示します。

Command2をクリックして内部配列を拡大し、フォームをもう一度クリックして結果を表示します。

ただし注意してください:外側の配列を再利用するときは、外側の配列のすべての新しい要素の内側の配列も再利用する必要があります

0
Hrqls

私はこの問題に出くわしましたが、自分自身でこの障害にぶつかりました。最終的に、この_ReDim Preserve_を新しいサイズの配列(最初または最後の次元)で処理するためのコードを実際にすばやく記述しました。たぶん同じ問題に直面している他の人を助けるでしょう。

そのため、使用法として、配列を元々MyArray(3,5)として設定し、寸法を(最初も!)大きくしたい場合は、MyArray(10,20)とだけ言ってみましょう。あなたはこのようなことをすることに慣れていますか?

_ ReDim Preserve MyArray(10,20) '<-- Returns Error
_

しかし、残念ながら、最初の次元のサイズを変更しようとしたため、エラーが返されます。だから私の関数では、代わりにこのようなことをするでしょう:

_ MyArray = ReDimPreserve(MyArray,10,20)
_

配列が大きくなり、データが保持されます。多次元配列の_ReDim Preserve_が完成しました。 :)

そして最後に、奇跡的な関数:ReDimPreserve()

_'redim preserve both dimensions for a multidimension array *ONLY
Public Function ReDimPreserve(aArrayToPreserve,nNewFirstUBound,nNewLastUBound)
    ReDimPreserve = False
    'check if its in array first
    If IsArray(aArrayToPreserve) Then       
        'create new array
        ReDim aPreservedArray(nNewFirstUBound,nNewLastUBound)
        'get old lBound/uBound
        nOldFirstUBound = uBound(aArrayToPreserve,1)
        nOldLastUBound = uBound(aArrayToPreserve,2)         
        'loop through first
        For nFirst = lBound(aArrayToPreserve,1) to nNewFirstUBound
            For nLast = lBound(aArrayToPreserve,2) to nNewLastUBound
                'if its in range, then append to new array the same way
                If nOldFirstUBound >= nFirst And nOldLastUBound >= nLast Then
                    aPreservedArray(nFirst,nLast) = aArrayToPreserve(nFirst,nLast)
                End If
            Next
        Next            
        'return the array redimmed
        If IsArray(aPreservedArray) Then ReDimPreserve = aPreservedArray
    End If
End Function
_

これは20分ほどで書いたので、保証はありません。ただし、使用または拡張する場合は、お気軽に。私は誰かがこのようなコードをすでにここに持っているだろうと思っていたでしょう。だからここで仲間のギアヘッドに行きます。

0
Control Freak

これはよりコンパクトで、配列の最初の最初の位置を尊重し、初期値を使用して古い値を追加します。

Public Sub ReDimPreserve(ByRef arr, ByVal size1 As Long, ByVal size2 As Long)
Dim arr2 As Variant
Dim x As Long, y As Long

'Check if it's an array first
If Not IsArray(arr) Then Exit Sub

'create new array with initial start
ReDim arr2(LBound(arr, 1) To size1, LBound(arr, 2) To size2)

'loop through first
For x = LBound(arr, 1) To UBound(arr, 1)
    For y = LBound(arr, 2) To UBound(arr, 2)
        'if its in range, then append to new array the same way
        arr2(x, y) = arr(x, y)
    Next
Next
'return byref
arr = arr2
End Sub

この行でこのサブルーチンを呼び出して、最初の次元のサイズを変更します

ReDimPreserve arr2, UBound(arr2, 1) + 1, UBound(arr2, 2)

他のテストを追加して、初期サイズが新しい配列よりも大きくないかどうかを確認できます。私の場合は必要ありません

0
GeoStoneMarten

VBAでこれを行う最も簡単な方法は、配列、新しい行数、および新しい列数を取り込む関数を作成することです。

以下の関数を実行して、サイズ変更後にすべての古いデータをアレイにコピーして戻します。

 function dynamic_preserve(array1, num_rows, num_cols)

        dim array2 as variant

        array2 = array1

        reDim array1(1 to num_rows, 1 to num_cols)

        for i = lbound(array2, 1) to ubound(array2, 2)

               for j = lbound(array2,2) to ubound(array2,2)

                      array1(i,j) = array2(i,j)

               next j

        next i

        dynamic_preserve = array1

end function
0
AdamSanSensei