Kの備忘録(仮)

Python、VBA、Excelを中心に記事を投稿

Pythonのユーザ定義関数を「y=2x+1」を使って解説する

はじめに

以前ブログで「VBAのFunctionプロシージャを「y=2x+1」を使って解説する」という記事を書きました。

lbibouroku.hatenablog.com

今回はそのPython版です。

VBAの記事のときも書いたのですが、以前は「自分で関数を作れる」ということがどういうことなのか、正直よくわかりませんでした。「引数」と「戻り値」のことも、解説を読んでも理解しにくかったです。

そんなときにユーザ定義関数を一次関数で表したら、なんとなくわかりやすくなった気がしたので、Pythonでも書いてみようと思いました。

ちなみに一次関数とは、以下のように定義されています。

 a、bを定数とするときy=ax+b(a≠0)で表される関数

コトバンクより抜粋 https://kotobank.jp/word/%E4%B8%80%E6%AC%A1%E9%96%A2%E6%95%B0-433347

この記事のタイトルの一部の「y=2x+1」は一次関数です。

◆目次◆

用語のおさらい

用語 意味
ユーザ定義関数 ユーザーが独自に定義した関数
引数 関数に渡す値
戻り値 関数から受け取る値

ユーザ定義関数のおさらい

Pythonのユーザ定義関数は以下のように作ります。

def 関数名(引数):
    処理
    return 戻り値

引数に値をいれて関数を呼び出したいときは、以下のようにな呼び出し用のスクリプトを作ります。

関数名(引数の値)

「y=2x+1」をユーザ定義関数で作る

では、さっそく「y=2x+1」をPythonのユーザ定義関数で書いてみます。今回は2種類の書き方を紹介します。

まずは1つ目のユーザ定義関数です。処理の部分に「y=2x+1」を書きます。

def linear_function(x):
    y = 2 * x + 1
    return y

「linear_function(直訳:一次関数)」という関数名のユーザ定義関数を作ります。 行いたい処理は「y = 2 * x + 1」です。答えはyの値なので、変数yを戻り値にします。

呼び出し用のスクリプトは、例えば「x=2」の場合は、以下のように書きます。

linear_function(2)

2つ目のユーザ定義関数です。戻り値に処理を書きます。

def y(x):
    return 2 * x + 1

「y」という関数名のユーザ定義関数を作ります。

「2 * x + 1」の処理結果を戻り値とするので、「2 * x + 1」が戻り値となります。つまり「return 2 * x + 1」と書けばいいのです。

このような簡単な処理であれば、戻り値に、行いたい処理を設定してもいいと思います。

呼び出し用のスクリプトは、例えば「x=2」の場合は、以下のように書きます。

y(2)

さいごに

こうやって並べると、1つ目のユーザー定義関数の方がぱっと見でわかりやすいですね(「y = 2 * x + 1」がそのまま入っているので)。

1つ目の書き方だと、複雑な処理も設定できるので、個人的にはこちらの書き方をする場合が多いです。(もちろん、ケースバイケースですが)

このように一次関数で「引数はx、戻り値はy(または処理結果)」と考えていただくと、少しわかりやすくなるかもしれません。

Excelライブラリを学ぶ

はじめに

しばらくブログを書いていなかったので、リハビリとして、2か月前に受けたVBA中級講座の第4回の「Excelライブラリ」の内容について、(リハビリで選ぶには、ハードな題材だけど・・・)一部まとめを書きます。

◆目次◆

VBAで出てくる用語のおさらい

まずはExcelライブラリと向き合うにあたって、出てくる用語の意味を調べなおします。

用語 意味
ライブラリ モジュールをまとめたもの
クラス クラスモジュールにオブジェクトとそのメンバーを定義したもの
モジュール 宣言ステートメントをまとめたもの
オブジェクト 操作対象のすべてのもの(セル、シート、ブック、グラフなど)
コレクション 同じ種類のオブジェクトをまとめたもの
メンバー コレクションの1つ1つのオブジェクトのこと
グローバル どこからでも同じように参照できるもの
(対義語は「ローカル」、あるいは「プライベート」)

これらをふまえて、Excelライブラリとは何かを確認します。

Excelライブラリとは

Excelライブラリの概要

Excelを操作するためのライブラリです。

Excel VBAでは初期状態で「Microsoft Excel 16.0 Object Library」が参照可能な状態になっています。

f:id:lbibouroku:20211120001229p:plain

Excelライブラリでは、Excelのさまざまなものが、クラスとして定義されています。

f:id:lbibouroku:20211120001242p:plain

このExcelライブラリを使ってVBAで操作することを「Excel VBA」といいます。

VBAではExcelを操作するときに、上位のオブジェクトから配下のオブジェクトをたどって操作対象のオブジェクトを取得します。

Excelライブラリの階層構造はこんな感じです。

Applecation
    |
    |- WorkSheets
        |
        |- Sheets
            |
            |- Range

Excelライブラリの参照

Excelライブラリはすべてクラスで構成されています。言い換えると、Excelライブラリはすべてオブジェクトモジュールでできています。

Excelライブラリには、取得するオブジェクト型と同名のプロパティ名が存在します。配下のオブジェクトを取得するには、以下のようにコードを書きます。

オブジェクト.プロパティ

でも、VBAで以下のようにコードを書いてもエラーにはならないですよね(上位オブジェクトが省略できます)。

Sub test1()
    Debug.Print Range("A1")
End Sub

本来であれば、Rangeオブジェクトを操作するには、Applecationクラスからたどるように、コードを書かないといけないはずです。

なぜエラーにならないのでしょうか。

それは、VBAでは「グローバルのメンバーは上位オブジェクトを省略できる」というルールがあるからです。

(すべてのメンバーを表示しきれていませんが)以下の赤枠内「<グローバル>のメンバー」は、上位オブジェクトを省略できます。

f:id:lbibouroku:20211120001257p:plain

以下のとおり、RangeはExcelライブラリのグローバルのメンバーです。

f:id:lbibouroku:20211120001313p:plain

上位オブジェクトを省略できたのは、こういう背景があったのですね。

上位オブジェクト省略時の注意事項

さて、Excelライブラリのグローバルのメンバーであれば、上位オブジェクトを省略できることはわかりました。

しかし、むやみに省略すると問題が発生する場合あります。大きな理由は以下の2点です。

  • モジュールによって省略の意味が変わるから(シートモジュールと標準モジュールでは結果が異なる場合がある)
  • ユーザー操作の干渉を受けるから(ユーザーが選択しているオブジェクトによって結果が異なる場合がある)

さいごに

Excelライブラリについて、講座の内容をひとつずつかみ砕きながら解き進めました。今回の肝はここでした。

それは、VBAでは「グローバルのメンバーは上位オブジェクトを省略できる」というルールがあるからです。

実は、講座では続きがあって、「既定のメンバー」について解説を受けました。

が、長くなるので、それはまた別の機会に書きます(そのときは_Defaultプロパティについて書くのか、大変だなあ・・・)。

VBAツール ※この記事は個人的な備忘録です

はじめに

  • この記事は個人的な備忘録です。
  • 業務で使用するためのサンプルツール用のコードなどを記述します。書きかけのコードもあります。
  • あくまでサンプルのため、コードの実践は自己責任でお願いします。

概要

転記ツール

'[Module1]
Enum header
    旧型番 = 1
    旧製品名
    旧受注停止日
    旧出荷停止日
    新型番
    新製品名
    新受注停止日
    新出荷停止日
    製品名
    掲載順判定
End Enum

Sub 掲載用販売停止情報作成()

    Call 新型番表シート.最終行確認
    Call 旧型番表シート.配列作成
    Call 掲載順シート.最終行確認
    Call 掲載用_販売停止情報シート.最終行確認
    
    Call 対象外型番判定

    Call 販売停止情報転記
    Call 掲載順整理
    
    Call 掲載用_販売停止情報シート.セル結合
    Call 掲載用_販売停止情報シート.製品名挿入

End Sub

Function 旧製品名検索一致(ByVal i As Long) As String

    Dim j As Long
    For j = 2 To UBound(旧型番表シート.配列, 1)
        If 新型番表シート.Cells(i, 11).Value = 旧型番表シート.配列(j, 1) Then
            旧製品名検索一致 = 旧型番表シート.配列(j, 3)
        End If
    Next j

End Function


Function 掲載順置換(ByVal i As Long) As String

    Dim order As String
    Dim j As Long
    For j = 2 To 掲載順シート.最終行_製品名
        
        If Left(新型番表シート.Cells(i, 11).Value, 6) = 掲載順シート.Cells(j, 1).Value Then
            order = 掲載順シート.Cells(j, 3).Value
        End If
    Next j
    
    order = order & "_" & 新型番表シート.Cells(i, 9).Value
    
    For j = 2 To 掲載順シート.最終行_分類
        If 新型番表シート.Cells(i, 2).Value = 掲載順シート.Cells(j, 4).Value Then
            order = order & "_" & 掲載順シート.Cells(j, 5).Value
        End If
    Next j

    掲載順置換 = order

End Function

Sub 対象外型番判定()

    Dim i As Long
    For i = 2 To 新型番表シート.最終行
        If Mid(新型番表シート.Cells(i, 1).Value, 4, 1) = "P" Then
            MsgBox 新型番表シート.Cells(i, 1).Value & "はPシリーズ製品です。処理を中止するので" & i & "行目を削除してください。"
            Exit Sub
        ElseIf 新型番表シート.Cells(i, 11) = "-" Then
            MsgBox 新型番表シート.Cells(i, 1).Value & "は前バージョンがない製品です。処理を中止するので" & i & "行目を削除してください。"
            Exit Sub
        End If
    Next i

End Sub

Sub 販売停止情報転記()


    Dim num As Long  '販売停止情報シートのカウンタ関数
    num = 1
    
    Dim i As Long
    For i = 2 To 新型番表シート.最終行
        num = num + 1
        With 販売停止情報シート
            .Cells(num, header.旧型番).Value = 新型番表シート.Cells(i, 11).Value
            .Cells(num, header.旧製品名).Value = 旧製品名検索一致(i)
            .Cells(num, header.旧受注停止日).Value = 旧製品受注出荷停止日シート.Cells(2, 1).Value
            .Cells(num, header.旧出荷停止日).Value = 旧製品受注出荷停止日シート.Cells(2, 2).Value
            .Cells(num, header.新型番).Value = 新型番表シート.Cells(i, 1).Value
            .Cells(num, header.新製品名).Value = 新型番表シート.Cells(i, 3).Value
            .Cells(num, header.新受注停止日).Value = 新型番表シート.Cells(i, 7).Value
            .Cells(num, header.新出荷停止日).Value = 新型番表シート.Cells(i, 8).Value
            .Cells(num, header.製品名).Value = 製品名抽出(i)
            .Cells(num, header.掲載順判定).Value = 掲載順置換(i)
        End With
    Next i

End Sub

Function 製品名抽出(ByVal i As Long) As String

    Dim j As Long
    For j = 2 To 掲載順シート.最終行_製品名
        If Left(新型番表シート.Cells(i, 11).Value, 6) = 掲載順シート.Cells(j, 1).Value Then
               製品名抽出 = 掲載順シート.Cells(j, 2).Value
        End If
    Next j

End Function

Sub 掲載順整理()

    Dim 転記範囲 As Range
    Set 転記範囲 = 販売停止情報シート.Range("A1").CurrentRegion
    
    With 転記範囲
        .Sort Key1:=.Range("J1"), Order1:=xlAscending, header:=xlYes
        .Resize(.Rows.Count - 1).Offset(1, 0).Copy
    End With
    
    掲載用_販売停止情報シート.Range("B9").PasteSpecial Paste:=xlPasteValues
    
End Sub
[旧型番表シート]
Public 配列 As Variant

Sub 配列作成()
  配列 = Cells(1, 1).CurrentRegion.Value
End Sub
[掲載順シート]
Public 最終行_製品名 As Variant
Public 最終行_分類 As Variant

Sub 最終行確認()    
    最終行_製品名 = Cells(Rows.Count, 1).End(xlUp).Row
    最終行_分類 = Cells(Rows.Count, 4).End(xlUp).Row
End Sub
[掲載用_販売停止情報シート]
Public 最終行 As Long

Sub 最終行確認()
  最終行 = Cells(Rows.Count, 2).End(xlUp).Row
End Sub

Sub セル結合()
    Call 最終行確認
    
    Application.DisplayAlerts = False
    Dim i As Long
    For i = 9 To 最終行 Step 3
        Range(Cells(i, 3), Cells(i + 2, 3)).Merge
        Range(Cells(i, 7), Cells(i + 2, 7)).Merge
    Next i
    Application.DisplayAlerts = True
    Range("C:C").WrapText = True
    Range("G:G").WrapText = True

End Sub

Sub 製品名挿入()
    Call 最終行確認
    Dim i As Long
    For i = 最終行 To 9 Step -1
        If Cells(i, 10) <> Cells(i - 1, 10) And Cells(i - 1, 10) <> "" Then
            Rows(8).Copy
            Rows(i).Insert Shift:=xlDown
            Application.CutCopyMode = False
            Cells(i, 2).Value = Cells(i + 1, 10).Value
        ElseIf Cells(i - 1, 10) = "" Then
            Cells(i - 1, 2).Value = Cells(i, 10).Value
        End If
    Next i
    
    Range("J:K").Delete
    Rows(最終行 & ":1000").Delete
End Sub
[新型番表シート]
Public 最終行 As Long

Sub 最終行確認()
  最終行 = Cells(Rows.Count, 1).End(xlUp).Row
End Sub

シート名など

f:id:lbibouroku:20211028081732p:plain f:id:lbibouroku:20211028081754p:plain f:id:lbibouroku:20211028081817p:plain f:id:lbibouroku:20211028081840p:plain

Excel VBAのクラスを学ぶ3 カプセル化と1~3回目までの講座の振り返り

はじめに

前回までの2回はVBA中級講座の受講内容についてまとめてました。

lbibouroku.hatenablog.com

その過程でクラスについていろいろ調べていたところ、結構気になることが出てきたので、別の記事でまとめることにしました。

今回は、「オブジェクト指向の3大要素って何?」「カプセル化って何?」という疑問の消化と、これまでの講座の振り返りです。普段の記事よりも個人的な備忘録の要素が強く、いろんな方のブログの引用が多めです。

◆目次◆

オブジェクト指向の3大要素とは

VBAの場合、継承と多態性は使えなくて、カプセル化だけを意識すれば良いようです。

以下引用

https://excel-ubara.com/vba_class/VBA_CLASS_01.html

VBAのことをネットで調べるとエクセルの神髄さんへたどり着く率高いですね)

心配はいりません、少なくともVBAでは、継承も多態性も使えないのですから、 (継承としてはインターフェースだけ使えますが、急ぎ覚える必要はないでしょう。) 従って、カプセル化だけを意識すれば良いのです。

カプセル化とは

以下引用 https://qiita.com/katolisa/items/6cfd1a2a87058678d646

  • フィールド=private、メソッド=public
  • フィールドを操作するためにgetterメソッドとsetterメソッドを用意する

= カプセル化である。

Javaカプセル化はこちらに掲載されています。

https://qiita.com/iverson3kobe0824/items/f3d7b93f6049b18b170e

やっていることはVBAも同じですね。ちなみにVBAではこんなコードになります。

例)Personクラス

'[Person]クラスモジュール
Private age_  As Long

Public Property Get Age() As Long
    Age = age_   
End Property

Public Property Let Age(ByVal newAge As Long)
    If newAge > 0 Then
        age_ = newAge
    Else
        age_ = 0
    End If      
End Property

'[Module1]標準モジュール
Sub personclass_test1()
    Dim myPerson As Person
    Set myPerson = New Person
    
    myPerson.Age = 60   
End Sub

宣言フィールドでプライベート変数を宣言して、パブリックなPropertyメソッドを経由して値を返します。

2回目の講座で、オブジェクトモジュールで同じことをしていました。つまり、今まで講座で学んでいたことはカプセル化の仕方も含まれていました!

3回目の講座のクラスの講座も同様です。

カプセル化のメリットはこの辺りでしょうか。

  • 変数の中身が書き変わって、変な値で処理しないように制限する
  • 値に制限が書けられる

もうちょっと調べてみました。

カプセル化のメリット

以下引用 https://xtech.nikkei.com/atcl/nxt/column/18/00208/031300003/

カプセル化のメリットは大きく分けて2つあり、1つめは、オブジェクトを利用する人はその操作の仕様だけを知っていれば、オブジェクト内の操作の実装やデータの内容を知る必要がないことです。 2つめは、関連する操作がひとまとめになっているので、理解しやすく変更の影響も局所化できることです。

こう見ると、「保守性が高くなる」という印象ですね。自分でクラスを使って管理できるようになると、実感できそうです。

継承とポリモーフィズム多態性)とは

ほかの言語ではこの2つも使えるらしいので、それぞれの意味を調べました。

  • 継承

以下引用 https://blog.codecamp.jp/java-inheritance

クラスの中身である変数やメソッドを、他のクラスに受け継がせる(=継承する)ことができます。

個人的にこれは便利そうなので使ってみたいです。

同じ命令でも、受け取り手が変わると実行内容が変わる(結果は変わらない・・・?)。

参考サイト https://java2005.cis.k.hosei.ac.jp/materials/lecture18/polymorphism.html

これはイメージがいまいち湧かないですね・・・。難しいです。

おわりに

クラスについて調べると、Javaの記事が多い印象でした。

まずは、クラスを知るには実践あるのみですね。

講座については、1回目からずっと、クラスを学習するための準備をしてきた印象です。

講座の最終的な目標は「VBAの開発や保守を楽に&スマートにできる知識とスキルを身につける」です。個人的な目標としては、「自分以外の人が保守ができるマクロを作る」としているので、まずはクラスの使いどころを掴みたいところですね。

VBA中級講座も折り返し地点なので、引き続き頑張りたいと思います。

Excel VBAのクラスを学ぶ2

はじめに

前回はクラスについて、プロパティを定義する方法までを説明しました。

lbibouroku.hatenablog.com

今回は続きの、クラスにはメソッドを定義する方法から始めます。

◆目次◆

クラスにメソッドを定義する方法

オブジェクトにメソッドを追加する方法と同じです。

  • Subプロシージャ
  • Functionプロシージャ

例:Personクラスに、挨拶をするメソッドと、お腹がすいたら「ご飯を食べる」というメッセージを返すメソッドを追加

'[Person]
Public Sub Greet()
    MsgBox "Hello!"
End Sub

Public Function Eat() As String
    If MsgBox("お腹がすいた?", vbYesNo) = vbYes Then
        Eat = "ご飯を食べる"
    End If
End Function

'[Module1]
Sub personclass_test1()
    Dim myPerson As Person
    Set myPerson = New Person
    
    myPerson.Greet
    MsgBox myPerson.Eat
End Sub

実行結果は、以下のとおりです。 * 「Hello!」というメッセージボックスが表示される * 「お腹がすいた?」というメッセージボックスが表示され、「はい」を選択すると、「ご飯を食べる」というメッセージボックスが表示される

Cloneメソッド

自身のコピーをオブジェクトとして生成し返すメソッドです。

例:同じ名前と血液型を持つクローンメソッドを作る

'[Person]クラスモジュール
'変数Nameはパブリック変数にして、ほかのモジュールでも参照可能にする
Public Name As String

'変数bloodType_はプライベート変数にして、Propertyプロシージャでセッターとゲッターを作る
Private bloodType_ As String

Public Property Let BloodType(ByVal newBloodType As String)
    bloodType_ = newBloodType
End Property

Public Property Get BloodType() As String
    BloodType = bloodType_
End Property

'クローンメソッドを使って
'同じ名前と血液型を持つインスタンスを戻り値を使って生成する
Public Function Clone() As Person
    Dim objNew As Person
    Set objNew = New Person

    objNew.Name = Me.Name
    objNew.BloodType = Me.BloodType
    
    Set Clone = objNew

End Function


'[Module1]標準モジュール
Sub cloneTest1()

'myPersonというPersonクラスのインスタンスを生成する
    Dim myPerson As Person
    Set myPerson = New Person
    myPerson.Name = "Bob"
    myPerson.BloodType = "A"

'clonePersonというmyPersonと同じ名前と血液型をクローンのインスタンスを生成する
    Dim clonePerson As Person
    Set clonePerson = myPerson.Clone
    Debug.Print clonePerson.Name '出力結果:Bob
    Debug.Print clonePerson.BloodType  '出力結果:A

'clonePersonの名前を変更する
    clonePerson.Name = "Mike"

'myPersonとclonePersonの名前と血液型を確認する
    Debug.Print myPerson.Name    '出力結果:Bob
    Debug.Print myPerson.BloodType  '出力結果:A

    Debug.Print clonePerson.Name '出力結果:Mike
    Debug.Print clonePerson.BloodType  '出力結果:A

End Sub

clonePersonはmyPersonとは異なるインスタンスなので、clonePersonの名前(Name)を変更しても、myPersonに影響はありません。

コンストラクタとデストラク

どちらもイベントプロシージャで設定します。 f:id:lbibouroku:20210919213230p:plain

インスタンスが生成されたときに実行される。

Initializeイベントで呼び出されるプロシージャ

Private Sub Class_Initialize()

'インスタンスが生成されたときに行う処理

End Sub

インスタンスへの参照がすべて失われたときに実行される。

Terminateイベントで呼び出されるプロシージャ

Private Sub Class_Terminate()

'インスタンスへの参照がすべて失われたときに行う処理

End Sub

さいごに

まだクラスの使いどころがわかっていないので、これから活用して覚えていきたいと思います。

Excel VBAのクラスを学ぶ1

はじめに

以前「Excel VBAのクラスを学ぶ前の準備 - モジュールとプロパティ -」という記事を書きました。 (現在受講中VBAの講座内容です)

lbibouroku.hatenablog.com

先日とうとうクラスの講座を受講しました!

まだまだ理解不足のところもありますが、そこで学んだことをまとめたいと思います。

◆目次◆

クラスがあると何ができるのか?

  • クラス:設計書(オブジェクトとそのメンバーを定義)
  • インスタンス化:クラスから実体のオブジェクトを生成

つまり、設計書をもとにオブジェクトが生成できます。 利点は、複数のオブジェクトが作れるので、コードの管理がしやすくなります。

クラスモジュールの作り方

  1. 挿入→クラスモジュール
  2. プロパティウィンドウのオブジェクト名を編集してクラスの名前をつける

オブジェクトの作り方

  1. クラスモジュールを作成する
  2. 標準モジュールでクラス型の変数を宣言し、インスタンス化する

例:人間の設計図として、Personクラスを作成し、オブジェクトを生成

'[Person]クラスモジュールを作成後に
'[Module1]で以下を宣言すると、オブジェクトが生成される

Dim myPerson As Person
Set myPerson = New Person

まだこの段階ではPersonクラスモジュールに何も書いていないので、設計図の中身がない状態です。

では、クラス(設計図)にプロパティを加えます。

クラスにプロパティを定義する方法

オブジェクトにプロパティを追加する方法と同じです。

  • モジュールレベル変数
  • Property Let/Setプロシージャ
  • Property Getプロシージャ

例:Personクラスに名前と年齢の設定を追加

'[Person]クラスモジュールにプロパティを追加する
Public Name As String
Private age_  As Long

Public Property Get Age() As Long
    Age = age_   
End Property

Public Property Let Age(ByVal newAge As Long)
    If newAge > 0 Then
        age_ = newAge
    Else
        age_ = 0
    End If      
End Property

'[Module1]で以下のように宣言すると
'Personクラスの設計図をもとにmyPersonオブジェクトが生成され
'myPersonオブジェクトに名前と年齢が設定できる
Sub personclass_test1()
    Dim myPerson As Person
    Set myPerson = New Person
    
    myPerson.Name = Bob
    myPerson.Age = 15   
End Sub

クラスにはメソッドも定義できますが長くなるので、続きは次回にします。

VBAでOutlookの受信メールフォルダから特定のメールの情報を抽出しExcelへ転記する ※この記事は個人的な備忘録です

はじめに

  • この記事は個人的な備忘録です。
  • 業務で使用するためのサンプルツール用のコードなどを記述します。書きかけのコードもあります。
  • あくまでサンプルのため、コードの実践は自己責任でお願いします。

概要

まずはOutlookの参照設定をする

そして、マクロ記述してテストする

まずはどれくらい時間がかかるのかをチェックする

Option Base 1

Enum Header_
    件名 = 1
    担当者
    添付ファイル名
End Enum

Sub メール内容抽出転記()

    Dim appOL As Outlook.Application
    Set appOL = New Outlook.Application
    
    '受信トレイのメールをすべて抽出
    Dim objItems As Object
    Set objItems = appOL.GetNamespace("MAPI").GetDefaultFolder(6).Items
    
    Dim objMailItem As Object
    Dim arr() As String
    Dim txt As String
    
        Dim i As Long
    i = 1
    For Each objMailItem In objItems
    
        With objMailItem
            If .Subject Like "*抽出のトリガー(件名)*" Then
                txt = .Body
                ReDim Preserve arr(2, i)
                arr(Header_.件名, i) = .Subject
                arr(Header_.担当者, i) = Mid(txt, InStr(txt, "抽出のトリガー(本文)") + 8, 2)
                i = i + 1
            End If
        End With
    
    Next objMailItem
    
    Dim arr2 As Variant
    arr2 = WorksheetFunction.Transpose(arr)
    
    With Sheet4
      .Range(.Cells(1, 1), .Cells(UBound(arr2, 1), UBound(arr2, 2))) = arr2
    End With

End Sub
Option Base 1

Enum Header_
    受信日時 = 1
    件名
    担当者
End Enum



Sub メール内容抽出転記2()

   Dim appOL As Outlook.Application
   Set appOL = New Outlook.Application
    
 '   '受信トレイのメールをすべて抽出
    Dim objItems As Object
     Set objItems = appOL.GetNamespace("MAPI").GetDefaultFolder(6).Items
    
    Dim objMailItem As Object
    Dim arr() As String
    Dim txt As String
    
        Dim i As Long
    i = 1
    For Each objMailItem In objItems
    
        With objMailItem
    '        If .Subject Like "*1234*" Then
            If .Subject Like "*1234*" And .ReceivedTime Like "*2021/09*" Then
                txt = .Body
                ReDim Preserve arr(3, i)
                arr(Header_.受信日時, i) = .ReceivedTime
                arr(Header_.件名, i) = .Subject
                arr(Header_.担当者, i) = Mid(txt, InStr(txt, "12345678") + 8, 2)
                i = i + 1
            End If
        End With

   Next objMailItem
    
    Dim arr2 As Variant
    arr2 = WorksheetFunction.Transpose(arr)
    
    With Sheet1
      .Range(.Cells(1, 1), .Cells(UBound(arr2, 1), UBound(arr2, 2))) = arr2
    End With

End Sub