Excel (VBA)

Excel VBAに関するフォーラムです。
  • 掲示板への投稿には会員登録(無料)が必要です。会員登録がまだの方はこちら
  • 掲示板ご利用上のお願い」に反するご記入はご遠慮ください。
  • Q&A掲示板の使い方はこちらをご覧ください
トピックに返信
質問

 
(Windows 11 Pro : Microsoft 365)
可視セルの取得で、行番号が連番だと正しく取得できない理由と解決策が知りたい
投稿日時: 25/05/02 14:17:19
投稿者: mogu774

オートフィルターで絞り込んだ値を
SpecialCells(xlCellTypeVisible)で取得しようとしています。
 
絞り込んだ際の行番号が
31,36,40,45など飛んでいれば問題なく全て取得が出来ます。
 
しかし、絞り込んだあとの連続する行番号の範囲が複数ある場合、
何故か最初の範囲値しか取得できません。
 
ちゃんと表示されているセルですし、
SpecialCells(xlCellTypeVisible).select とやってみると
ちゃんと全部の行範囲が選択されています。
 
以下例だと、2〜4行目しか取得できません。
取得したいのは、2〜4行目+8〜10行目です。
試しに動かした場合と同じように、配列みたいに取得できるの希望です。
 
For Eachで回せば1行ずつ取得できるのは分かっているのですが
1発で取得出来るときもForEachで回すのは面倒くさいし手間なので
良い方法があれば教えてください。
 
---テストコード---
Sub Test()
Dim TargetA
 
                ' 複数列を文字列で絞り込み
            With ThisWorkbook.Worksheets("Sheet1").Range("A1")
                .AutoFilter 1, 1
            End With
 
            'フィルタ結果がある場合
            If WorksheetFunction.Subtotal(3, ThisWorkbook.Worksheets("Sheet1").Range("A:A")) >= 2 Then
                With ThisWorkbook.Worksheets("Sheet1").Range("A1").CurrentRegion
                    '見出しを除く表示している行をループ
                    TargetA = .Resize(.Rows.Count - 1).Offset(1, 0).SpecialCells(xlCellTypeVisible)
                End With
            End If
End Sub
-----------
テストデータ
 
データ1,データ2,
1,あ,
1,い,
1,う,
0,え,
3,お,
2,か,
1,き,
1,く,
1,け,
 
---------

回答
投稿日時: 25/05/02 17:21:26
投稿者: sk

引用:
取得したいのは、2〜4行目+8〜10行目です。
試しに動かした場合と同じように、配列みたいに取得できるの希望です。

引用:
TargetA = .Resize(.Rows.Count - 1).Offset(1, 0).SpecialCells(xlCellTypeVisible)

・単一のセルを参照している Excel.Range オブジェクトの
 Value プロパティが返すのは、そのセルの値である。
 
・単一のセル領域( 2 つ以上のセルが連続している範囲)を参照している
 Excel.Range オブジェクトの Value プロパティが返すのは、
 その領域の全てのセルの値が格納された 2 次元配列である。
 
複数のセルまたはセル領域を含む範囲を参照している Excel.Range オブジェクトの
 Value プロパティが返すのは、1 つめのセルの値か、もしくは 1 つめの領域に含まれる
 全てのセルの値が格納された 2 次元配列である。
 
引用:
以下例だと、2〜4行目しか取得できません。

したがって、それは仕様通りの動作です。
 
引用:
テストデータ
  
データ1,データ2,
1,あ,
1,い,
1,う,
0,え,
3,お,
2,か,
1,き,
1,く,
1,け,

(標準モジュール)
--------------------------------------------------------------------
Sub Test2()
         
    Dim wsTarget As Worksheet
     
    Set wsTarget = ThisWorkbook.Worksheets("Sheet1")
     
    With wsTarget
        PrintValues .Range("A2")
        PrintValues .Range("A2:B4")
        PrintValues .Range("A2,B2,A4,B4")
        PrintValues .Range("A2:B2,A4:B4")
        PrintValues .Range("A2,A4:B4")
    End With
     
    Set wsTarget = Nothing
     
End Sub
 
Private Sub PrintValues(Target As Excel.Range)
     
    Debug.Print "参照範囲のアドレス: " & Target.Address
     
    Dim rngArea As Excel.Range
    Dim lngAreaCount As Long
    Dim varValue As Variant
    Dim lngRow As Long
    Dim lngColumn As Long
    Dim varList As Variant
     
    For Each rngArea In Target.Areas
         
        lngAreaCount = lngAreaCount + 1
     
        Debug.Print lngAreaCount & " つめのセル領域のアドレス: " & rngArea.Address
         
        varValue = rngArea.Value
         
        '配列ではない場合
        If IsArray(varValue) = False Then
            Debug.Print vbTab & varValue
        '配列である場合
        Else
            For lngRow = LBound(varValue, 1) To UBound(varValue, 1)
                varList = ""
                For lngColumn = LBound(varValue, 2) To UBound(varValue, 2)
                    varList = varList & vbTab & varValue(lngRow, lngColumn)
                Next
                Debug.Print varList
            Next
        End If
    Next
 
    Debug.Print ""
 
End Sub
--------------------------------------------------------------------
 
(イミディエイトウィンドウへの出力結果)
--------------------------------------------------------------------
参照範囲のアドレス: $A$2
1 つめのセル領域のアドレス: $A$2
    1

参照範囲のアドレス: $A$2:$B$4
1 つめのセル領域のアドレス: $A$2:$B$4
    1   あ
    1   い
    1   う

参照範囲のアドレス: $A$2,$B$2,$A$4,$B$4
1 つめのセル領域のアドレス: $A$2
    1
2 つめのセル領域のアドレス: $B$2
    あ
3 つめのセル領域のアドレス: $A$4
    1
4 つめのセル領域のアドレス: $B$4
    う

参照範囲のアドレス: $A$2:$B$2,$A$4:$B$4
1 つめのセル領域のアドレス: $A$2:$B$2
    1   あ
2 つめのセル領域のアドレス: $A$4:$B$4
    1   う

参照範囲のアドレス: $A$2,$A$4:$B$4
1 つめのセル領域のアドレス: $A$2
    1
2 つめのセル領域のアドレス: $A$4:$B$4
    1   う

--------------------------------------------------------------------
 
引用:
For Eachで回せば1行ずつ取得できるのは分かっているのですが
1発で取得出来るときもForEachで回すのは面倒くさいし手間なので
良い方法があれば教えてください。

ただ Value プロパティを Variant 型の変数に代入しただけでは、
ご希望の結果は得ることは出来ません。
今回のような場合、むしろ「 1 発で取得できる」状況というのは
非常に限られているのです。
 
「列数が同一である複数のセル領域に含まれる全ての要素を
単一の 2 次元配列に変換(統合)したい」ということであれば、
別途そういう処理を実行するしかないでしょう。
(例えば動的配列を使用するなど)

回答
投稿日時: 25/05/02 18:42:54
投稿者: simple

既にご指摘のありましたとおりかと思います。
 

    Dim targetB
    Dim rng As Range    
を追加しておき
 
   If WorksheetFunction.Subtotal(3, Worksheets("Sheet1").Columns("A")) >= 2 Then
        With Worksheets("Sheet1").Range("A1").CurrentRegion
            Set rng = Worksheets("Sheet2").[A1]         ' 作業領域(任意の場所でOK)
            .Resize(.Rows.Count - 1).Offset(1, 0).Copy rng
            targetB = rng.CurrentRegion.Value
        End With
    End If
などとしては?
 
ちなみに、AutoFilterによる場合は.SpecialCells(xlCellTypeVisible)としなくても、
自動的に可視セルだけが対象です。(手作業による非表示の場合は別です)
# Thisworkbookを省略したのは、単に字数節約のためで特段の意図はありません。

回答
投稿日時: 25/05/03 07:29:34
投稿者: hatena
投稿者のウェブサイトに移動

Microsoft 365とのことなので、
オートフィールターで絞り込むのではなくFILTER関数で絞り込めばどうでしょう。
FILTER関数は配列を返しますので。
  
 
Sub Test1()
    Dim TargetA
    Dim rng As Range
     
    Set rng = Worksheets("Sheet1").Range("A1").CurrentRegion
 
    'A列が1のデータを絞り込み
    TargetA = WorksheetFunction.Filter(rng, Evaluate(rng.Columns(1).Address & "=1"))
     
    '配列TargetAの内容をD1に出力
    Range("D1").Resize(UBound(TargetA), UBound(TargetA, 2)).Value = TargetA
End Sub

トピックに返信