• ベストアンサー

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

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

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

  • ベストアンサー
  • Wendy02
  • ベストアンサー率57% (3570/6232)
回答No.2

こんにちは。 >ちなみにこのモジュールに渡される配列の型は一次元、二次元ともにありますので、どちらにも対応できるようにするにはどうすればよいでしょう? うーん、ご質問のレベルは、そのユーザー定義関数のコードのレベルよりワンランク上というよりも、一般的には、こういう場合、あくまでも、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関数ですから、非常に見にくい状態になります。

takac4u
質問者

お礼

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

その他の回答 (3)

  • imogasi
  • ベストアンサー率27% (4737/17069)
回答No.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さんお許しください。

takac4u
質問者

お礼

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

  • Wendy02
  • ベストアンサー率57% (3570/6232)
回答No.3

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

takac4u
質問者

お礼

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

  • Wendy02
  • ベストアンサー率57% (3570/6232)
回答No.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\ フォルダに入れるのが一般的です。よけいなことかもしれませんが、ちょっと気になりました。

takac4u
質問者

お礼

先日の質問まで補足していただき、ありがとうございます。 私の質問が足りなかったせいで、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$を比較して、そのインデックス番号を取得するというコードがあるのですが、これを二次元配列に対応できるようにしたいんです。 ちなみにこのモジュールに渡される配列の型は一次元、二次元ともにありますので、どちらにも対応できるようにするにはどうすればよいでしょう?

関連するQ&A