こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

VBAで多次元配列のインデックス番号の取得

一次元配列の場合=UBOUND(array)-LBOUND(array)で配列の長さが求められますよね。これが二次元配列でarray(4,13)とかの場合上記式を入れても4という値が取得できますが、13という値を求めたい場合はどうすればよいでしょうか

投稿日時 - 2007-11-10 12:06:11

QNo.3505460

すぐに回答ほしいです

質問者が選んだベストアンサー

こんにちは。

>ちなみにこのモジュールに渡される配列の型は一次元、二次元ともにありますので、どちらにも対応できるようにするにはどうすればよいでしょう?

うーん、ご質問のレベルは、そのユーザー定義関数のコードのレベルよりワンランク上というよりも、一般的には、こういう場合、あくまでも、Excel VBAの範疇では、Excelでは、ワークシートを使いますね。もちろん、VBAでも技術的にも可能ですが、最初から行・列を想定しているなら、マトリックスになったワークシートに置き換えたほうが考え方が楽です。

元の質問と、「お礼」側とは、内容が変わりますが、そのユーザー定義関数を、1次元・2次元も両方できるように変えてみました。

以下のマクロは、最初から、1次元を2次元に切り替えてしまいます。本来、1次元と2次元の違いが検出できるのだから、別々に検索すればよいわけですが、紙面の関係上というのか、ここでは、2次元に切り替えてしまいました。

また、単に、各次元の上限の添え字を取るなら、Ubound で、エラーが出るまで、次元を増やせばよいのですが、その先に、文字列を探すということだと、あえて、ここでは、2次元までとして、それ以上は打ち切りました。

だから、解は、配列で返します。1次元でも、Ary(10) の3 に目的のものがあるとしても、「3,0」とします。関数内部では、引数が、1次元か2次元かは、If m = Empty Then で、取れていますから、もし、第一次引数Ar が、1次元だから、解も1次元にするというなら、分岐して、解の出力側で変えてあげれば済みます。どちらがよいかは、今考えておりません。

この関数のエラー値は、2次元以上の多次元は、解は「0」で、また、配列でない、第一次引数Ar も、解は「0」を返します。本来、関数の戻り値をVariant 型にしていますから、 CVErrで、エラー値を返してもよいのですが、処理が増えますから、一応、便宜的に数値にしました。

-----------------------------------------------------------------

Function FindArrayR(ByRef Ar, ByVal arg As String) As Variant
  Dim ub As Integer
  Dim Ar2 As Variant
  Dim n As Variant
  Dim m As Variant
  Dim i As Long
  Dim j As Long
  Dim flg As Boolean
  flg = False
  If Not IsArray(Ar) Then FindArrayR = 0: Exit Function
  On Error Resume Next
  Do
    i = i + 1
    ub = UBound(Ar, i)
    If Err.Number = 0 Then
      If i = 1 Then
        n = ub
      ElseIf i = 2 Then
        m = ub
      ElseIf i > 2 Then
         FindArrayR = 0: Exit Function
      End If
    Else
      Exit Do
    End If
  Loop
  On Error GoTo 0
  '1次元配列を、2次元配列に切り替え
  If m = Empty Then
    ReDim Ar2(n, 0)
    For j = 0 To n
      Ar2(j, 0) = Ar(j)
    Next j
    m = 0
  Else
    Ar2 = Ar
  End If
  For j = 0 To m
    For i = 0 To n
      If StrComp(Ar2(i, j), arg) = 0 And flg = False Then
        flg = True
        Exit For
      End If
    Next i
    If flg Then Exit For
  Next j
  FindArrayR = Array(i, j)
End Function


なお、今回使った、テスト用のマクロ

Sub TestMacro()
Dim i As Integer
Dim ret As Variant
Dim Ar(10, 10) As Variant
For i = 0 To 10
 Ar(8, i) = Chr(65 + i)
Next
 ret = FindArrayR(Ar, "E")

End Sub

------------------------------------------------------------------
なお、val$ という書き方はしないでね。Val は、VBA関数ですから、非常に見にくい状態になります。

投稿日時 - 2007-11-10 14:52:28

お礼

回答ありがとうございます。
DoLoopでUbound で、エラーが出るまで次元を増やせばよいという考えが思いつきませんでした。このコードを元に1次元は1次元で返すように変更してやってみようと思います。
重ねて質問なんですが、
>>本来、1次元と2次元の違いが検出できるのだから
とありますがそれはどういうことでしょうか?

投稿日時 - 2007-11-10 16:05:18

ANo.2

このQ&Aは役に立ちましたか?

2人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

ご大家の後で、素人の小生が言うのも気が引けるのですが、質問者は
http://msdn2.microsoft.com/ja-jp/library/95b8f22f(VS.80).aspx
で言っているようなことを聞いているのではないでしょうか。
http://dobon.net/vb/bbs/log3-1/511.html
にASPの例の質問があり、上記にいたりました。
上記WEB記事で言っているのは
Sub test02()
Dim a(100, 200)
MsgBox UBound(a, 2)
End Sub
で200と表示されますが、こんなのでよければ参考にしてください。
ーー
Array関数を使った場合は、今まで私の経験する程度の易しい範囲内では、Ubound+1でデータ数(使用要素数)が取れましたが
一般的にDim A(100) と宣言した場合
Sub test01()
Dim a(100)
MsgBox UBound(a)
End Sub
は100と表示されますが、実際何個使われているかは1発では判らないのでは。
配列要素内容を使うごとに1つづつRedim、ResizeでもしないとVariantのArrayと同じようには捉えられないのでは。
http://www.atmarkit.co.jp/fdotnet/dotnettips/444arrayresize/arrayresize.html
ーー
Wendy02さんのご回答に既に出ている内容なら、Wendy02さんお許しください。

投稿日時 - 2007-11-10 17:12:37

お礼

解答ありがとうございます。
Wendy02さんの解答で既に解決していました。
今までUBOUND(ary)という使い方しかしたことがなく、UBOUND(ary,Rank)というようにRankを指定すれば任意の次元の添字の最大値が求められるということを知らなかったんです。
お手数かけてすいません。

投稿日時 - 2007-11-10 17:50:37

ANo.3

こんにちは。

>>>本来、1次元と2次元の違いが検出できるのだから
>とありますがそれはどういうことでしょうか?

だから、

>引数が、1次元か2次元かは、If m = Empty Then で、取れていますから、

ということです。それ以上の多次元の話と、1・2次元の話とは、意味合いが違います。ある程度の予想はあるけれども、3次元以上の多次元で検索することは、今のところ考慮には入れていません。また、それ3次元以上の検索というのは、あまり現実的ではありません。もちろん、3次元検索自体は、Excel Cube に存在している検索ではあるのですが、実際には、やったことがないからです。

投稿日時 - 2007-11-10 16:17:16

お礼

>引数が、1次元か2次元かは、If m = Empty Then
そういうことですよね。てっきり配列の次元を調べる関数があるのかなと勘違いしてしまいました。
そもそも、なんでこうなるかと言いますと、表のに行タイトル(B5:M9)の上部(D1:M4)に項目名が記載されていて、項目名を配列Arに読込データベースの項目名(先程のval$)とArを比べArのインデックス(そのままセルアドレスになります)を取得し、その場所から相対的に値を代入する表を作りたいいう事が目的で、将来作成者がいなくても上部の項目名の並びや削除をするだけで、表が作れるということをしたかったんですよね。
いままでは上部の項目名(B1:M1)でタイトル(B2:M3)というような感じで一次元でよかったんですが、要求された表が多次元だったもので質問しました。
ですので、2次元までで十分ですので、先程のコードを活用させていただきます。ありがとうございました

投稿日時 - 2007-11-10 16:29:56

ANo.1

こんにちは。

Sub Test()
Dim ar(4, 13) As Variant
Dim one As Integer
Dim two As Integer
 one = UBound(ar, 1)
 two = UBound(ar, 2)
End Sub

array は、関数名ですから、変数には使わないほうがよいです。

p.s.別件で、ふだん、こういうことはローカルルールに反するので書いてはいけないので、もし問題があれば、削除されてもかまいませんが、

前回の「エクセルファイルの自動起動と内容更新」の質問の、20点側の回答の「起動時に全てのファイルを開く」に設定する方法は、非常にトラブルの多い設定で、万が一失敗すると、Excelのセーフモードでしか開けられなくなります。

C:\Documents and Settings\[ユーザー名]\Application Data\Microsoft\Excel\XLSTART\
フォルダに入れるのが一般的です。よけいなことかもしれませんが、ちょっと気になりました。

投稿日時 - 2007-11-10 12:50:38

お礼

先日の質問まで補足していただき、ありがとうございます。
私の質問が足りなかったせいで、Wendy02にはご迷惑をかけます。
Function FindArray(ByRef ary() As String, ByVal val$) As Long
  For idx = LBound(ary) To UBound(ary)
    If ary(idx) = val Then
      FindArray = idx
      Exit Function
    End If
  Next
End Function
というやりかたで一次元配列内の中身とval$を比較して、そのインデックス番号を取得するというコードがあるのですが、これを二次元配列に対応できるようにしたいんです。
ちなみにこのモジュールに渡される配列の型は一次元、二次元ともにありますので、どちらにも対応できるようにするにはどうすればよいでしょう?

投稿日時 - 2007-11-10 13:04:34

あなたにオススメの質問