Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(指定なし : 指定なし)
WorksheetFunction.Filter
投稿日時: 23/01/23 11:34:35
投稿者: tarima

WorksheetFunction.Filterで、テーブルをフィルタし配列を取得しています。
 
戻り値が1行の場合のみ2次元ではなく1次元配列になっているような?
UBound(配列名, 1)が列数で、UBound(配列名, 2)がエラーになります。
 
戻り値が2行以上の場合は、
UBound(配列名, 1)が行数で、UBound(配列名, 2)が列数です。
 
配列の次元数をチェックし、エラーを回避することはできるのでしょうか。
そもそも1行の場合はなぜ2次元配列でないのでしょうか。
 
該当なし(戻り値0行)の場合は、Filter関数自体がエラーになります

回答
投稿日時: 23/01/23 15:45:09
投稿者: taitani
投稿者のウェブサイトに移動

回答の方向性が間違っていたらすみません。
VBA で WorksheetFunction.Filter を使う前に、同じことをシート上で通常の関数でやってみた場合、同じエラーになる見解です。
シート上でエラー回避できるのであれば、VBA 上でも回避可能ではないでしょうか。
 
あと、.ListObjects("テーブル名").Range.Rows.Count などで、データを取得できると思いますので、そのカウントをもとに回避可能ではないでしょうか。

回答
投稿日時: 23/01/23 15:50:24
投稿者: simple

たぶん365だと想像しますが、Excelのバージョンを記載ください。(ルールですので)
私はそれを使えない環境ですので、話半分で受け止めてください。
 
次のように判定できるのではないでしょうか。(質問者さんの記述を前提にしています。)
Filter関数の戻り値を 仮にary とすると、

    Dim n As Long
    On Error Resume Next
    n = UBound(ary, 2)
    On Error GoTo 0
    If n = 0 Then
        MsgBox "二次元配列ではありません"
    End If

(2)二次元配列ではない理由はMS社に聞かないとわかりません。
そう決めたという性質のものだと想像します。
その理由はユーザーには開示されていないのでは?
 
# 365をお使いの方からの回答をお待ちください。

投稿日時: 23/01/23 16:51:35
投稿者: tarima

みなさま、ご親切にありがとうございます。
 
Excelのバージョンは、Microsoft365です。
ワークシートでは問題ないです。
今まで結果が1件ということがなかったので気づきませんでした。
VBAで取得時のみの問題です。
ListObjectも問題ないです。
 
On Error Resume Nextしか方法がないのであれば、あきらめます。

回答
投稿日時: 23/01/23 18:38:47
投稿者: simple

配列の次元を直接得るプロパティなり、関数なりはありませんね。
こちらのサイトの即効テクニックでも、
On Error Resume Nextを併用する方法を推奨しています。
「配列の次元数を取得する」
https://www.moug.net/tech/exvba/0100039.html
このあたりはVB6.0の仕様で、殆ど変化はありません。
 
諦めて下さい。

回答
投稿日時: 23/01/23 22:00:16
投稿者: んなっと

FILTER関数の結果が1行だけの場合、2次元配列ではなくて1次元配列(ベクトル)になってしまうようです。
1次元配列だと UBound(v, 2) は存在しない。
 
AdvancedFilterで簡単にできることですが、
「無理やり」WorksheetFunction.Filterを使ってC列が"R"の物をJ2に取り出すとき
 
   A   B   C  D E F G H I  J   K   L  M
 1 No. 名前 分類 値         No. 名前 分類 値
 2 101  あ   P 10                  
 3 102  い   Q 11                  
 4 103  う   P 12                  
 5 104  え   P 13                  
 6 105  お   Q 14                  
 7 106  か   Q 15                  
 8 107  き   P 16                  
 9 108  く   Q 17                  
10 109  け   R 18                  
 
  Dim r As Range
  Dim v
  Set r = ActiveSheet.ListObjects("テーブル1").Range
  v = WorksheetFunction.Filter(r, Evaluate(r.Columns(3).Address & "=""R"""), "")
  On Error Resume Next
  If IsEmpty(UBound(v, 2)) Then
    Range("J2").Resize(1, UBound(v, 1)).Value = v
  Else
    Range("J2").Resize(UBound(v, 1), UBound(v, 2)).Value = v
  End If
  On Error GoTo 0
 
とか
 
  Dim r As Range
  Dim v
  Set r = ActiveSheet.ListObjects("テーブル1").Range
  v = WorksheetFunction.Filter(r, WorksheetFunction.XLookup(r.Columns(3), "R", True, False), "")
  On Error Resume Next
  If IsEmpty(UBound(v, 2)) Then
    Range("J2").Resize(1, UBound(v, 1)).Value = v
  Else
    Range("J2").Resize(UBound(v, 1), UBound(v, 2)).Value = v
  End If
  On Error GoTo 0
 
こんなコード使う必要まったくなし。

回答
投稿日時: 23/01/24 11:10:23
投稿者: gombohori

 んなっとさんのテーブルを使わせてもらうとして、
 1個しかないときに1次元配列になるのが不都合なら、
 2個にしちゃえばいいじゃない というサンプルです
 取得した最後の行は処理しないように気をつけてください
 
    Sub sample()
      Dim tbl As ListObject
      Dim r As Range, f As Variant, buf As Variant
       
      Set tbl = ActiveSheet.ListObjects("テーブル1")
       
      Key = "R"
       
      n = WorksheetFunction.CountIf(Range("テーブル1[分類]"), Key)
      If n = 0 Then Exit Sub
       
      tbl.ListRows.Add.Range(, 3) = Key
      Set r = Range("テーブル1[#DATA]")
      f = Evaluate("テーブル1[分類]=""" & Key & """")
      buf = WorksheetFunction.Filter(r, f)
      tbl.ListRows(tbl.ListRows.Count).Delete
       
      For i = LBound(buf) To UBound(buf) - 1
          For j = LBound(buf, 2) To UBound(buf, 2)
              Debug.Print buf(i, j);
          Next
          Debug.Print
      Next
    End Sub

投稿日時: 23/01/25 16:10:39
投稿者: tarima

んなっと様
いつもありがとうございます。
AdvancedFilterを使うことは思いつきませんでした。
無理やりFilter関数を使うよりわかりやすいです。
AdvancedFilterを使用します。