<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>VBA共通 | VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<atom:link href="https://officevba.info/category/vba%E5%85%B1%E9%80%9A/feed/" rel="self" type="application/rss+xml" />
	<link>https://officevba.info</link>
	<description>仕事の役に立つVBA・GAS・Pythonのコードを紹介していきます。</description>
	<lastBuildDate>Sat, 31 Jan 2026 12:17:12 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://officevba.info/wp-content/uploads/2017/04/cropped-Excel_1-32x32.jpg</url>
	<title>VBA共通 | VBA・GAS・Pythonで仕事を楽しく効率化</title>
	<link>https://officevba.info</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WindowsAPIを用いてウインドウの一覧表を作成するVBAコード（再帰処理）</title>
		<link>https://officevba.info/recursion-getwindow/</link>
					<comments>https://officevba.info/recursion-getwindow/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 03 Dec 2023 02:20:41 +0000</pubDate>
				<category><![CDATA[ウインドウの取得]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[WindowsAPI]]></category>
		<category><![CDATA[再帰処理]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2479</guid>

					<description><![CDATA[目次 ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したいウインドウハンドルとは？再帰処理とは？再帰処理を用いたウインドウハンドルを取得するVBAコードについて ウインドウ操作に必要になるウイ [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したい</a></li><li><a href="#toc2" tabindex="0">ウインドウハンドルとは？</a></li><li><a href="#toc3" tabindex="0">再帰処理とは？</a></li><li><a href="#toc4" tabindex="0">再帰処理を用いたウインドウハンドルを取得するVBAコードについて</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">ウインドウ操作に必要になるウインドウハンドルを子ウインドウも含めて全て取得したい</span></h2>
<p>以前こちらの記事で（https://officevba.info/window-list/）ウインドウの一覧を取得するコードをご紹介しました。</p>
<p>このコードは階層構造になっている最上段のウインドウのみを取得するものだったのですが、ウインドウの内部をクリックしたり操作したりする際は子ウインドウや孫ウインドウなど下層にあるウインドウを順に取得しないといけないことも多く、一回で必要なすべてのウインドウの情報を取得することができませんでした。</p>
<p>子ハンドルや孫ハンドルを含めてウインドウハンドルを取得できない理由はウインドウの階層が一律でなく一般的なループ処理では対応できないことなのですが、以前フォルダとファイルを全て取得する処理で学んだ再帰処理を使うことで様々な階層のウインドウハンドルを全て取得できるようになりました。</p>
<p>今回は再帰処理を用いて開いているウインドウのすべてのハンドルを取得するコードをご紹介します。</p>
<h2><span id="toc2">ウインドウハンドルとは？</span></h2>
<p>Windowsは開いたウインドウや、ウインドウに配置されているパーツなどそれぞれに一つずつにハンドルという数値型の値を割り当てて制御しているらしいです。</p>
<p>「ウインドウ」ハンドルと言ってもVBAでいうところのオブジェクトのような感じで、エクスプローラーの開いた画面のような見た目がウインドウのものから、テキストボックスやアイコン、ステータスバーなど色々なものに割り当てられていて、理論上はウインドウハンドルさえ取得できればWindowsAPIを用いて対象を自由に操作できるらしいです。（私自身が何でもできるわけではありませんのでらしいと記載しましたがかなり機能は豊富そうです。）</p>
<p>上記の通りWindowsAPIを用いて操作対象のウインドウを指定する際、このウインドウハンドルを指定することが大事なのですが、ウインドウハンドルはウインドウを開くたびにランダムに新しい数値が割り当てられるため、プログラムを実行する過程でウインドウハンドルを取得する手段がないとWindowsAPIでは操作ができないということになります。</p>
<h2><span id="toc3">再帰処理とは？</span></h2>
<p>再帰処理はプロシージャの中に自分自身を呼び出すコードを記載することで、ループ処理時の階層が一定でなくても各階層に同じ処理を行うことができることです。</p>
<p>よく使われるのはあるフォルダの中に様々な階層のフォルダが格納されていてそれぞれにファイルが格納されている際に、各階層を順に探ってファイルとフォルダの一覧を書き出したり、ファイルをコピーしたりするのに使われます。</p>
<p>ちなみに上記のような処理をしたい場合、Pythonの標準モジュールのglobを使えば引数を一つ指定するだけで再帰処理を実行することが可能です。<br />
VBAではコードを書いて実装しないといけないですが書き方に慣れが必要なので、この辺りはPythonの方が新しい言語でより洗練されている気がします。</p>
<h2><span id="toc4">再帰処理を用いたウインドウハンドルを取得するVBAコードについて</span></h2>
<p>ウインドウが階層構造になっていて、かつウインドウごとに一定の階層ではないというのが再帰で解決できるぴったりの環境で、以前ウインドウハンドルの一覧表を作成するコードを勉強していた時には再帰処理の書き方を知らなかったので実装できず、作成したコードはウインドウの最上位だけを取得するのみでした。</p>
<p>必要なウインドウハンドルを取得するため、必要な場合は一層下ったものを探る処理を何度も繰り返して取得していましたが、再帰処理を使えば一回で全部解決させることができますし、今回の以下のようなコードの書き方では階層の情報も把握できるのでかなり便利になります。</p>
<div class="VBACode">
<pre>
'ウインドウの中の次または前のウインドウハンドルを取得する
Declare Function GetNextWindow Lib "user32" _
    Alias "GetWindow" _
    (ByVal hWnd As Long, _
    ByVal wFlag As Long) As Long

'ウインドウのキャプションタイトルを取得する
Declare Function GetWindowText Lib "user32" _
    Alias "GetWindowTextA" _
    (ByVal hWnd As Long, ByVal lpString As String, _
    ByVal cch As Long) As Long

'ウインドウが可視かどうかを取得する
Declare Function IsWindowVisible Lib "user32" _
    (ByVal hWnd As Long) As Long

'ウインドウのクラス名を取得する
Declare Function GetClassName Lib "user32" _
    Alias "GetClassNameA" _
    (ByVal hWnd As Long, _
    ByVal lpClassName As String, _
    ByVal nMaxCount As Long) As Long
    
'ウインドウハンドルの子を取得する
Declare Function FindWindowEx Lib "User32.dll" _
    Alias "FindWindowExA" ( _
    ByVal hWndParent As Long, _
    ByVal hwndChildAfter As Long, _
    ByVal lpszClass As String, _
    ByVal lpszWindow As String) As Long
 
'ウインドウハンドルを取得する
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
        (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Public Const GW_HWNDLAST = 1
Public Const GW_HWNDNEXT = 2

Sub 再帰でWindow一覧のキャプションとクラスを取得する()
    
    With ActiveSheet
        .Cells(1, 1) = "ハンドル"
        .Cells(1, 2) = "クラス名"
        .Cells(1, 3) = "キャプション名"
    End With
    
    Dim k As Long: k = 2
    Dim hWnd As Long: hWnd = FindWindow(vbNullString, vbNullString) '引数をvbNullStringにすると1つめのウインドウを取得する

    Call ウインドウの取得recursion(hWnd, k)

End Sub

Sub ウインドウの取得recursion(ByRef hWnd As Long, ByRef k As Long)
    
    Dim strClassNameCh As String * 100
    Dim strCaptionCh As String * 80
    
    Do
        If IsWindowVisible(hWnd) Then
            
            GetWindowText hWnd, strCaptionCh, Len(strCaptionCh)
            GetClassName hWnd, strClassNameCh, Len(strClassNameCh)
            
            Dim i As Long: i = ActiveSheet.UsedRange.Rows.Count + 1
            
            With ActiveSheet
                .Cells(i, 1).Value = hWnd
                .Cells(i, k).Value = Left(strClassNameCh, InStr(strClassNameCh, vbNullChar) - 1)
                .Cells(i, k + 1).Value = Left(strCaptionCh, InStr(strCaptionCh, vbNullChar) - 1)
            End With
            
            DoEvents
            
            Dim hWndCh As Long: hWndCh = FindWindowEx(hWnd, 0, vbNullString, vbNullString)
            Call ウインドウの取得recursion(hWndCh, k + 2)
        
        End If
        
        hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)
        
    Loop Until hWnd = GetNextWindow(hWnd, GW_HWNDLAST)
            
End Sub


</pre>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/recursion-getwindow/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ExcelVBAで再帰処理を用いてフォルダ一覧を取得</title>
		<link>https://officevba.info/recursion-getfolder/</link>
					<comments>https://officevba.info/recursion-getfolder/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 06 Nov 2022 11:25:08 +0000</pubDate>
				<category><![CDATA[ファイル・フォルダ操作]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[再帰処理]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2469</guid>

					<description><![CDATA[目次 VBAで再帰処理を実行再帰処理とは再帰処理の実例 VBAで再帰処理を実行 最近、ExcelVBAの再帰処理の方法を人から教えてもらったので、以前断念したフォルダ一覧の取得をVBAで試してみました。 使い方の基本さえ [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4" checked><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">VBAで再帰処理を実行</a></li><li><a href="#toc2" tabindex="0">再帰処理とは</a></li><li><a href="#toc3" tabindex="0">再帰処理の実例</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">VBAで再帰処理を実行</span></h2>
<p>最近、ExcelVBAの再帰処理の方法を人から教えてもらったので、以前断念したフォルダ一覧の取得をVBAで試してみました。<br />
使い方の基本さえわかっていればかなり便利に使用できますのでこちらでご紹介します。</p>
<h2><span id="toc2">再帰処理とは</span></h2>
<p>再帰処理とはあるプログラムの中で自分自身を呼び出して繰り返し処理を行うことです。<br />
繰り返しの数があらかじめ定まっていない、ネストの階層がわからない場合など一般的な繰り返しが適用しにくい場合に有効な処理となります。</p>
<h2><span id="toc3">再帰処理の実例</span></h2>
<p>再帰処理はいつでもどこでも使うものというより特定の状況で知っておくとめちゃくちゃ便利なものだと思います。<br />
今回は再帰処理を用いて「フォルダの一覧を取得」するコードをご紹介します。</p>
<div class="VBACode">
<pre>Sub フォルダとファイル一覧を取得()

    '参照設定「MicrosoftScriptingRuntime」を有効にしておく
    Dim fso As New FileSystemObject
    '参照設定「Windows Script Host Object Model」を有効にしておく
    Dim wsh As New WshShell
    
    Dim tarPath As String: tarPath = wsh.SpecialFolders("Desktop") &#038; "\マクロ"
    Dim tarFolder As Folder: Set tarFolder = fso.GetFolder(tarPath)
    Call recursion(tarFolder)
    
    Set fso = Nothing
    Set wsh = Nothing
    
End Sub

Sub recursion(ByRef f As Folder)

    Dim tarSubFolder As Folder
    
    For Each tarSubFolder In f.SubFolders
        Debug.Print tarSubFolder.Path
        Call recursion(tarSubFolder)
    Next

End Sub</pre>
</div>
<p>デスクトップにある「マクロ」ファイルの下部にあるサブフォルダを全階層にわたり取得してイミディエイトに出力するコードになります。</p>
<p>Subプロシージャの「recursion」（再帰）の中で、 ①サブフォルダを取得して、②そのフォルダオブジェクトを引数にして自分自身をCallで呼び出す作りになっています。 </p>
<p>Subプロシージャが自分自身を呼び出すことができることに自分では考えが至らず、教わったときに衝撃を受けました。</p>
<p>この処理は対象となるフォルダが多いと終了するまでに時間がかかるのでご注意ください。</p>
<p>普通にデスクトップのパスを文字列で指定してもいいのですが、Windowsのユーザー名に依存せずにデスクトップを指定するためにWSH(Windows Scripting Host)のSpecialFoldersを使用しています。</p>
<p>Debug.printでイミディエイトウィンドウに表示する代わりに、配列に入れたりワークシートに出力することでより高速に処理ができるようになります。</p>
<p>ちなみにPythonではglobというライブラリを使用すれば、以下の1行で再帰処理を実行して全フォルダを取得することができます。</p>
<div class="PythonCode">
<pre>print(glob.glob('C:/Users/<ユーザー名>/Desktop/マクロ/**', recursive=True))</pre>
</div>
<p>この辺りはまた別の記事でご紹介できればと思います。<br />
今回の目的だけならPythonを使った方が便利そうですが、VBAでも再帰処理ができるというのは大きな発見でした。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/recursion-getfolder/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>複数のパスワード付きzipに一括で色々なファイルを格納するVBAコード</title>
		<link>https://officevba.info/vbazipfilewithpassword/</link>
					<comments>https://officevba.info/vbazipfilewithpassword/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 19 Dec 2021 13:54:46 +0000</pubDate>
				<category><![CDATA[DOSコマンドの実行]]></category>
		<category><![CDATA[LhaPlus]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[zip]]></category>
		<category><![CDATA[コマンドプロンプト]]></category>
		<category><![CDATA[自動化]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2353</guid>

					<description><![CDATA[目次 同僚からの依頼でzipファイル格納のVBA検討zip格納に「LhaPlus」を使用する今回考えた自動zip格納VBAツールの仕組み事前準備ツールファイルの画面ツール操作実行手順今回作成したVBAコード①フォルダ取得 [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6" checked><label class="toc-title" for="toc-checkbox-6">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">同僚からの依頼でzipファイル格納のVBA検討</a></li><li><a href="#toc2" tabindex="0">zip格納に「LhaPlus」を使用する</a></li><li><a href="#toc3" tabindex="0">今回考えた自動zip格納VBAツールの仕組み</a><ol><li><a href="#toc4" tabindex="0">事前準備</a></li><li><a href="#toc5" tabindex="0">ツールファイルの画面</a></li><li><a href="#toc6" tabindex="0">ツール操作実行手順</a></li></ol></li><li><a href="#toc7" tabindex="0">今回作成したVBAコード</a><ol><li><a href="#toc8" tabindex="0">①フォルダ取得</a></li><li><a href="#toc9" tabindex="0">②zip格納</a></li></ol></li><li><a href="#toc10" tabindex="0">動作確認の動画とあとがき</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">同僚からの依頼でzipファイル格納のVBA検討</span></h2>
<p>最近社内でメールのシステムが変更になり、これまで外部への添付ファイルは自動で「①zipに格納→②パスワード生成→③別メールでパスワードを送付」ができていたのですが、手作業でzipを作成することになりました。</p>
<p>元々パスワード付きzipファイルをメールで送るフロー自体が色々と問題を引き起こす可能性も指摘されているので、その運用自体止めてしまえばよい気もするのですが、セキュリティ対策の主観部署がこれまで行っていたzip格納を止める判断をしなかったらしく上記のように手作業の負担が増えていました。</p>
<p>今回はパスワード付きzipファイルに必要なファイルを連続で圧縮するExcelVBAコードを考えてみました。</p>
<h2><span id="toc2">zip格納に「LhaPlus」を使用する</span></h2>
<p>以前<a href="https://officevba.info/vbazipfile/" target="_blank">こちらの記事</a>で紹介していますが、zip格納を安定して実行するためにLhaPlusというフリーソフトを使用します。</p>
<p>Windows標準の機能を呼び出してzip格納をする方法もあるのですが、公式なサポート外らしくエラーが起こるし動作が不安定でした。<br />
LhaPlusはコマンドプロンプトから操作ができるようになっていて、それを利用することでVBAからも自動操作が可能になります。</p>
<h2><span id="toc3">今回考えた自動zip格納VBAツールの仕組み</span></h2>
<p>今回私が考えたツールの動作と業務のフローは以下の通りです。</p>
<h3><span id="toc4">事前準備</span></h3>
<p>①zipファイルに格納したいファイルをフォルダごとにまとめてあるフォルダに格納</p>
<p>②zip変換用フォルダをまとめて格納しているフォルダにツールファイルも一緒に格納（vba162_zip一括返還211219.xlsm）</p>
<p>詳細は以下の画像のような状態です。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/12/vba162-1.jpg"><img fetchpriority="high" decoding="async" src="https://officevba.info/wp-content/uploads/2021/12/vba162-1-700x248.jpg" alt="" width="700" height="248" class="alignnone size-large wp-image-2349" srcset="https://officevba.info/wp-content/uploads/2021/12/vba162-1-700x248.jpg 700w, https://officevba.info/wp-content/uploads/2021/12/vba162-1-300x106.jpg 300w, https://officevba.info/wp-content/uploads/2021/12/vba162-1-768x272.jpg 768w, https://officevba.info/wp-content/uploads/2021/12/vba162-1-1536x545.jpg 1536w, https://officevba.info/wp-content/uploads/2021/12/vba162-1.jpg 2019w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h3><span id="toc5">ツールファイルの画面</span></h3>
<p>ツールファイルは以下のような画面で作成しています。<br />
まずzipを作成するためのフォルダ一覧を取得して、パスワードを手入力したあと、zipを作成するフローを想定しています。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/12/vba162-2.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/2021/12/vba162-2-700x314.jpg" alt="" width="700" height="314" class="alignnone size-large wp-image-2350" srcset="https://officevba.info/wp-content/uploads/2021/12/vba162-2-700x314.jpg 700w, https://officevba.info/wp-content/uploads/2021/12/vba162-2-300x135.jpg 300w, https://officevba.info/wp-content/uploads/2021/12/vba162-2-768x345.jpg 768w, https://officevba.info/wp-content/uploads/2021/12/vba162-2.jpg 1226w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<h3><span id="toc6">ツール操作実行手順</span></h3>
<p>①フォルダ取得ツールでzipに格納したいフォルダ一覧を取得<br />
「フォルダ取得」ボタンをクリックするとツールフォルダが格納されているフォルダをB1セルに表示し、その中のサブフォルダを一覧にしてA列に取得して入力</p>
<p>②作成するzipファイルのパスワードを入力（B列）<br />
パスワードが規則性のあるものにするならVBAで自動化することもできますが、今回は手入力するフローにしています。</p>
<p>③zip格納ボタンをクリック<br />
サブフォルダの中のファイルが同じ名前のzipに格納され、作成されたサブフォルダ「zip格納」の中にzipファイルが作成される</p>
<h2><span id="toc7">今回作成したVBAコード</span></h2>
<h3><span id="toc8">①フォルダ取得</span></h3>
<div class="GASCode">
<pre>Sub フォルダ取得()
    
    Dim FSO As Object, WSH As Object
    
    Dim FoldPath As String
    Dim FolderName As String
    Dim ArrFolderName() As String
    
    Dim i As Long, k As Long
    
    Set FSO = CreateObject("Scripting.FileSystemObject")
    FoldPath = ThisWorkbook.Path

    Cells(1, 2) = FoldPath
    i = 0
    FolderName = Dir(FoldPath &#038; "\*", vbDirectory)
    Do While FolderName <> ""
        If InStr(FolderName, ".") = 0 And FolderName <> "zip格納" Then
            ReDim Preserve ArrFolderName(i)
            ArrFolderName(i) = FolderName
            Cells(i + 4, 1) = FolderName
            'Cells(i + 4, 2) = Left(Cells(i + 4, 1), 4)
            i = i + 1
        End If
    FolderName = Dir()
    Loop
    
End Sub</pre>
</div>
<h3><span id="toc9">②zip格納</span></h3>
<div class="GASCode">
<pre>Sub サブフォルダzip格納()

    Dim Cmd As String
    Dim ArrFileName, FileName
    Dim FSO, WSH
    Dim 前FoldPath, 後FoldPath

    Set FSO = CreateObject("Scripting.FileSystemObject")
    Set WSH = CreateObject("Wscript.Shell")

    WSH.CurrentDirectory = "C:\Program Files (x86)\Lhaplus"

    '前FoldPathに格納されているサブフォルダをzip格納して後FoldPathに格納
    前FoldPath = Cells(1, 2)
    後FoldPath = Cells(1, 2) &#038; "\zip格納"

    'zip格納用のフォルダ作成
    If FSO.FolderExists(後FoldPath) = False Then
        FSO.CreateFolder 後FoldPath 'フォルダ作成
    End If

    '後で使用する対象ファイル一覧表が残っていたら削除
    If FSO.FileExists(前FoldPath &#038; "\圧縮リスト.txt") = True Then
        FSO.DeleteFile 前FoldPath &#038; "\圧縮リスト.txt"
    End If

    Dim i
    i = 4
    Do Until Cells(i, 1) = ""
        ArrFileName = ""
        FileName = Dir(前FoldPath &#038; "\" &#038; Cells(i, 1) &#038; "\*.*", vbNormal)
        Do While FileName <> ""
            If ArrFileName <> "" Then
                ArrFileName = ArrFileName &#038; vbCrLf &#038; 前FoldPath &#038; "\" &#038; Cells(i, 1) &#038; "\" &#038; FileName
            Else
                ArrFileName = 前FoldPath &#038; "\" &#038; Cells(i, 1) &#038; "\" &#038; FileName
            End If
        FileName = Dir()
        Loop

        'コマンドプロンプトは文字数の上限があるので、対象ファイル一覧を「圧縮リスト.txt」に入力
        With FSO.CreateTextFile(Cells(1, 2) &#038; "\圧縮リスト.txt")
            .WriteLine ArrFileName
            .Close
        End With

        'cells(i,2)がPassword 空欄ならパスワード設定なしでzip格納
        Cmd = "Lhaplus.exe /c:zip /n:" &#038; 後FoldPath &#038; "\" &#038; Cells(i, 1) &#038; ".zip" &#038; " /p:" &#038; Cells(i, 2).Text &#038; " /l:" &#038; Cells(1, 2) &#038; "\圧縮リスト.txt"

        WSH.Run "%ComSpec% /c " &#038; Cmd, 7, True

        FSO.DeleteFile Cells(1, 2) &#038; "\圧縮リスト.txt"

    i = i + 1
    Loop

    Set FSO = Nothing
    Set WSH = Nothing

End Sub</pre>
</div>
<h2><span id="toc10">動作確認の動画とあとがき</span></h2>
<p>今回作成したコードを実行した動画は以下の通りです。</p>
<div style="width: 1280px;" class="wp-video"><video class="wp-video-shortcode" id="video-2353-1" width="1280" height="720" preload="metadata" controls="controls"><source type="video/mp4" src="https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4?_=1" /><a href="https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4">https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4</a></video></div>
<p>パスワード付きzipファイルをメールで送る文化には賛否両論ありますが、私の会社含めまだまだ根強く残ると思います。<br />
自動サービスがなくなっていってしまう中手作業でこの処理を続けるのは結構つらいと思うので自動処理を考えてみました。</p>
<p>今回ご紹介したコードはコマンドプロンプトを起動している時間PCを使えない（キャンセルしてしまう可能性がある）ので、大量のファイルをzipに格納する場合、別のPCを使うか休憩時間前などに実行だけして休憩時間中に作成することをおすすめします。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/vbazipfilewithpassword/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://officevba.info/wp-content/uploads/2021/12/vba162-3.mp4" length="0" type="video/mp4" />

			</item>
		<item>
		<title>連想配列（Dictionary）④-複数項目を一気に連想配列に格納して取得する方法-</title>
		<link>https://officevba.info/multidictionary/</link>
					<comments>https://officevba.info/multidictionary/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 22 Aug 2021 10:44:01 +0000</pubDate>
				<category><![CDATA[連想配列]]></category>
		<category><![CDATA[配列]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[複数]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2285</guid>

					<description><![CDATA[目次 連想配列（Dictionary）で一つのキーに対して複数のアイテムを格納する方法を検討連想配列（Dictionary）で一つのキーに対して複数のアイテムを格納するには配列を使う連想配列を用いて集計をするためのサンプ [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-8" checked><label class="toc-title" for="toc-checkbox-8">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">連想配列（Dictionary）で一つのキーに対して複数のアイテムを格納する方法を検討</a></li><li><a href="#toc2" tabindex="0">連想配列（Dictionary）で一つのキーに対して複数のアイテムを格納するには配列を使う</a></li><li><a href="#toc3" tabindex="0">連想配列を用いて集計をするためのサンプルシート</a></li><li><a href="#toc4" tabindex="0">①一つのキーに対してアイテムにセル範囲を格納した配列を割り当てる</a></li><li><a href="#toc5" tabindex="0">②Dictionary型のオブジェクトを配列として宣言する</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">連想配列（Dictionary）で一つのキーに対して複数のアイテムを格納する方法を検討</span></h2>
<p>事務仕事を行っているとVBAのほかExcelの関数も使用する機会が多いです。</p>
<p>中でもVlookup関数やSUMIFS関数の頻度が高いのですが、これらの関数は対象の行数が多いと結果の表示に時間がかかることがあります。<br />
（Office2016からVlookup関数は高速化されましたが、VBAの中で組み込むと計算が終了する前に次の処理に移ってしまったり、速度が遅かったりします。）</p>
<p>以前から連想配列（Dictionary）を用いてこれらの関数と同様の処理を高速にする方法をご紹介していますが、Dictionary型はキーとアイテムが一対一の関係にあるので、複数のアイテムを参照したい場合、都度Dicrionaryを宣言するのが手間になります。</p>
<p>今回はこの問題を解消した、複数カラム（アイテム）を参照する方法をご紹介します。</p>
<h2><span id="toc2">連想配列（Dictionary）で一つのキーに対して複数のアイテムを格納するには配列を使う</span></h2>
<p>手順の考え方を先にお伝えすると、配列を使用することで一つのキーに対して複数のアイテムを持つことができるようになります。<br />
配列の使い方は以下2通りが想定されました。</p>
<p>①一つのDictionary型のオブジェクトを宣言して、一つのキーに対してアイテムにセル範囲を格納した配列を割り当てる</p>
<p>②Dictionary型のオブジェクトを配列として宣言して、一つのキーに対して一つのアイテムを格納する。</p>
<p>少しイメージがつきにくいと思いますので、サンプルシートを用いて具体的な手順を記載します。</p>
<h2><span id="toc3">連想配列を用いて集計をするためのサンプルシート</span></h2>
<p><a href="https://officevba.info/wp-content/uploads/2021/08/b539b93fede403dd99bca9a8e394438a.jpg"><img decoding="async" src="https://officevba.info/wp-content/uploads/2021/08/b539b93fede403dd99bca9a8e394438a-700x394.jpg" alt="VBA158" width="700" height="394" /></a></p>
<p>ある会社における店舗ごとの3か月ごとの売り上げ実績表のイメージになります。</p>
<p>A列に店固有のコード・B列は店舗名称になります。<br />
C列には商品コードが入っていて、D列から後は3か月分の数量と原価・売価が書いてある表です。</p>
<p>サンプルで用意したこちらのシートは店舗数が77店舗、行数は約40,000行となっています。<br />
Excelで加工するワークシートとしては容量が大きめかと思います。</p>
<p>こちらの数量・原価・売価について<span class="marker-under">連想配列を用いて店舗ごとに集計するというのが今回ご紹介するコードの目的</span>になります。</p>
<p>また、同じ処理はSUMIFS関数やピポットテーブルを使えば行うことができます。<br />
ただし、SUMIFS関数は処理が遅い、ピポットテーブルはVBAで実行すると独特の挙動になるため、準備と後処理が面倒くさいのがネックになります。</p>
<p>その点連想配列を用いればスマートかつ高速に処理が可能です。</p>
<h2><span id="toc4">①一つのキーに対してアイテムにセル範囲を格納した配列を割り当てる</span></h2>
<div class="VBACode">Sub 店舗ごと金額集計Itemを直接配列に格納()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’エラー起こるので非推奨</span></span><br />
<span class="VBA_Tab1">Sheets(1).Select</span><br />
<span class="VBA_Tab1">Dim JANシート</span><br />
<span class="VBA_Tab1">Set JANシート = Sheets(1)</span><br />
<br />
<span class="VBA_Tab1">Dim JANシート下端行</span><br />
<span class="VBA_Tab1">JANシート下端行 = JANシート.Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim JANシート範囲</span><br />
<span class="VBA_Tab1">JANシート範囲 = Range(JANシート.Cells(1, 1), JANシート.Cells(JANシート下端行, 15))</span><br />
<br />
<span class="VBA_Tab1">Dim i, x, k</span><br />
<span class="VBA_Tab1">Dim dict実績 As Object</span><br />
<br />
<span class="VBA_Tab1">Set dict実績 = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim itemRange</span><br />
<span class="VBA_Tab1">Dim storeCode()</span><br />
<span class="VBA_Tab1">x = 0</span><br />
<br />
<span class="VBA_Tab1">For i = 3 To JANシート下端行</span><br />
<span class="VBA_Tab2">If dict実績.Exists(JANシート範囲(i, 1)) = False Then</span><br />
<span class="VBA_Tab3">dict実績.Add (JANシート範囲(i, 1)), Range(Cells(i, 1), Cells(i, 15))</span><br />
<br />
<span class="VBA_Tab3">ReDim Preserve storeCode(x)</span><br />
<span class="VBA_Tab3">storeCode(x) = JANシート範囲(i, 1)</span><br />
<span class="VBA_Tab3">x = x + 1</span><br />
<br />
<span class="VBA_Tab2">Else</span><br />
<span class="VBA_Tab3">For k = 4 To 15</span><br />
<span class="VBA_Tab4">dict実績(JANシート範囲(i, 1))(k) = dict実績(JANシート範囲(i, 1))(k) + JANシート範囲(i, k)</span><br />
<span class="VBA_Tab3">Next k</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">Sheets.Add before:=Sheets(1)</span><br />
<span class="VBA_Tab1">Dim r</span><br />
<span class="VBA_Tab1">r = Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(UBound(storeCode) + 2, 15))</span><br />
<span class="VBA_Tab1">For i = 1 To UBound(storeCode) + 1</span><br />
<span class="VBA_Tab2">r(i + 1, 1) = storeCode(i － 1)  <span class="VBA_Comment">’行番号は2からスタート、配列は0からスタート</span></span><br />
<span class="VBA_Tab2">For k = 4 To 15</span><br />
<span class="VBA_Tab3">r(i + 1, k) = dict実績(r(i + 1, 1))(k)</span><br />
<span class="VBA_Tab2">Next k</span><br />
<span class="VBA_Tab1">Next i</span><br />
<span class="VBA_Tab1">Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(UBound(storeCode) + 1, 15)) = r</span><br />
<br />
<span class="VBA_Tab1">Set dict実績 = Nothing</span><br />
<span class="VBA_Tab1">Set JANシート = Nothing</span><br />
<br />
End Sub</div>
<p>dict実績という連動配列を宣言し、店舗コードをキーに、その行に含まれている実績を配列に格納します。<br />
重複されるデータは繰り返し処理を用いてアイテムの配列に値を追加していく流れになります。</p>
<p>こちらで処理を実行すると、なぜかよくわからないのですが、<span class="marker-under">セルの値が更新される不具合（？）が生じました。</span><br />
実績自体は別シートに出力するので、もともとのシートは別に保存しておけば大丈夫かとも思いますが少し不気味な結果です。</p>
<p>セルを配列に格納する場合はセルの値を格納するというよりは、セルオブジェクトを配列に格納していると思われます。<br />
そのため、加算をしたものがダイレクトにセルの値に反映されたと推測されるのですが、通常セルを配列に格納してもこの事象は起こらない気がしますので、正しいかどうかは不明です。</p>
<p>どのような原因にせよ連想配列のアイテムにセルを格納した配列を用いるのはあまり良くなさそうです。</p>
<p>一応、セルの値を配列に格納して更新する方法も試したのですが、<span class="marker-under">こちらはセルの値が加算されず、最初に格納されたアイテムだけが吐き出される</span>結果になりました。</p>
<p>こちらも不具合かもしれませんが、私には判断がつきませんでした。連想配列の挙動は少し不思議な感じがします。<br />
それにそもそも最初にArray関数を使うところの記述が面倒で、要素数が増えるとかなり辛いのでそもそも却下です。</p>
<div class="VBACode">Sub 店舗ごと金額集計ItemにArray関数で配列を格納()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’エラー起こるし、きちんと動かない</span></span><br />
<span class="VBA_Tab1">Sheets(1).Select</span><br />
<span class="VBA_Tab1">Dim JANシート</span><br />
<span class="VBA_Tab1">Set JANシート = Sheets(1)</span><br />
<br />
<span class="VBA_Tab1">Dim JANシート下端行</span><br />
<span class="VBA_Tab1">JANシート下端行 = JANシート.Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim JANシート範囲</span><br />
<span class="VBA_Tab1">JANシート範囲 = Range(JANシート.Cells(1, 1), JANシート.Cells(JANシート下端行, 15))</span><br />
<br />
<span class="VBA_Tab1">Dim i, x, k</span><br />
<span class="VBA_Tab1">Dim dict実績 As Object</span><br />
<br />
<span class="VBA_Tab1">Set dict実績 = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim itemRange</span><br />
<span class="VBA_Tab1">Dim storeCode()</span><br />
<span class="VBA_Tab1">x = 0</span><br />
<br />
<span class="VBA_Tab1">For i = 3 To JANシート下端行</span><br />
<span class="VBA_Tab2">If dict実績.Exists(JANシート範囲(i, 1)) = False Then</span><br />
<span class="VBA_Tab3">dict実績.Add (JANシート範囲(i, 1)), Array(Cells(i, 1).Value, Cells(i, 2).Value, Cells(i, 3).Value, Cells(i, 4).Value, Cells(i, 5).Value, Cells(i, 6).Value, Cells(i, 7).Value, Cells(i, 8).Value, Cells(i, 9).Value, Cells(i, 10).Value, Cells(i, 11).Value, Cells(i, 12).Value, Cells(i, 13).Value, Cells(i, 14).Value, Cells(i, 15))</span><br />
<br />
<span class="VBA_Tab3">ReDim Preserve storeCode(x)</span><br />
<span class="VBA_Tab3">storeCode(x) = JANシート範囲(i, 1)</span><br />
<span class="VBA_Tab3">x = x + 1</span><br />
<br />
<span class="VBA_Tab2">Else</span><br />
<span class="VBA_Tab3">For k = 4 To 15</span><br />
<span class="VBA_Tab4">dict実績(JANシート範囲(i, 1))(k － 1) = dict実績(JANシート範囲(i, 1))(k － 1) + Cells(i, k).Value</span><br />
<span class="VBA_Tab3">Next k</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">Sheets.Add before:=Sheets(1)</span><br />
<span class="VBA_Tab1">Dim r</span><br />
<span class="VBA_Tab1">r = Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(UBound(storeCode) + 2, 15))</span><br />
<br />
<span class="VBA_Tab1">For i = 0 To UBound(storeCode)</span><br />
<span class="VBA_Tab2">r(i + 2, 1) = storeCode(i)  <span class="VBA_Comment">’行番号は2からスタート、配列は0からスタート</span></span><br />
<span class="VBA_Tab2">For k = 4 To 15</span><br />
<span class="VBA_Tab3">r(i + 2, k) = dict実績(r(i + 2, 1))(k － 1)</span><br />
<span class="VBA_Tab2">Next k</span><br />
<span class="VBA_Tab1">Next i</span><br />
<span class="VBA_Tab1">Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(UBound(storeCode) + 2, 15)) = r</span><br />
<br />
<span class="VBA_Tab1">Set dict実績 = Nothing</span><br />
<span class="VBA_Tab1">Set JANシート = Nothing</span><br />
<br />
End Sub</div>
<h2><span id="toc5">②Dictionary型のオブジェクトを配列として宣言する</span></h2>
<p>上記の2つの配列を使う方法がうまくいかなかったので、他に方法を検討して思いついたのがこちらの方法です。<br />
若干手順が面倒ですが、繰り返し処理を付け加えるだけで上記①と似た手間になるので私としては現在この方法が最も現実的かと思います。</p>
<div class="VBACode">Sub 店舗ごと金額集計連想配列を配列として宣言()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’宣言のところで繰り返しが必要だが基本使いやすい</span></span><br />
<br />
<span class="VBA_Tab1">Sheets(1).Select</span><br />
<span class="VBA_Tab1">Dim JANシート</span><br />
<span class="VBA_Tab1">Set JANシート = Sheets(1)</span><br />
<br />
<span class="VBA_Tab1">Dim JANシート下端行</span><br />
<span class="VBA_Tab1">JANシート下端行 = JANシート.Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim JANシート範囲</span><br />
<span class="VBA_Tab1">JANシート範囲 = Range(JANシート.Cells(1, 1), JANシート.Cells(JANシート下端行, 15))</span><br />
<br />
<span class="VBA_Tab1">Dim i, x, k</span><br />
<span class="VBA_Tab1">Dim dict実績(1 To 15) As Object</span><br />
<br />
<span class="VBA_Tab1">For k = 1 To 15</span><br />
<span class="VBA_Tab2">Set dict実績(k) = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<span class="VBA_Tab1">Next k</span><br />
<br />
<span class="VBA_Tab1">Dim itemRange</span><br />
<span class="VBA_Tab1">Dim storeCode()</span><br />
<span class="VBA_Tab1">x = 0</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Itemにセルを格納した配列はあまり良くない?</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’array使ってvalueを格納すると加算ができない</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’variant型の変数にセルを直接配列として格納するとセルの値が変更される</span></span><br />
<br />
<span class="VBA_Tab1">For i = 3 To JANシート下端行</span><br />
<span class="VBA_Tab2">If dict実績(1).Exists(JANシート範囲(i, 1)) = False Then</span><br />
<span class="VBA_Tab3">For k = 1 To 15</span><br />
<span class="VBA_Tab4">dict実績(k).Add (JANシート範囲(i, 1)), JANシート範囲(i, k)</span><br />
<span class="VBA_Tab3">Next k</span><br />
<br />
<span class="VBA_Tab3">ReDim Preserve storeCode(x)</span><br />
<span class="VBA_Tab3">storeCode(x) = JANシート範囲(i, 1)</span><br />
<span class="VBA_Tab3">x = x + 1</span><br />
<span class="VBA_Tab2">Else</span><br />
<span class="VBA_Tab3">For k = 4 To 15</span><br />
<span class="VBA_Tab4">dict実績(k)(JANシート範囲(i, 1)) = dict実績(k)(JANシート範囲(i, 1)) + JANシート範囲(i, k)</span><br />
<span class="VBA_Tab3">Next k</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">Sheets.Add before:=Sheets(1)</span><br />
<span class="VBA_Tab1">Dim r</span><br />
<span class="VBA_Tab1">r = Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(UBound(storeCode) + 2, 15))</span><br />
<span class="VBA_Tab1">For i = 0 To UBound(storeCode)</span><br />
<span class="VBA_Tab2">r(i + 2, 1) = storeCode(i)  <span class="VBA_Comment">’行番号は2からスタート、配列は0からスタート</span></span><br />
<span class="VBA_Tab2">For k = 4 To 15</span><br />
<span class="VBA_Tab3">r(i + 2, k) = dict実績(k)(r(i + 2, 1))</span><br />
<span class="VBA_Tab2">Next k</span><br />
<span class="VBA_Tab1">Next i</span><br />
<span class="VBA_Tab1">Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(UBound(storeCode) + 2, 15)) = r</span><br />
<br />
<span class="VBA_Tab1">For k = 1 To 15</span><br />
<span class="VBA_Tab2">Set dict実績(k) = Nothing</span><br />
<span class="VBA_Tab1">Next k</span><br />
<span class="VBA_Tab1">Set JANシート = Nothing</span><br />
<br />
End Sub</div>
<p>先ほどとは違いDictionary型を配列として宣言し、<span class="marker-under">15個分の連想配列を繰り返し処理で準備</span>します。</p>
<p>吐き出すときはdict実績(インデックス番号)(“キー”)で出力できるようにして、かつ列番号とインデックス番号を揃えておくことで見やすくもなります。<br />
こちらの処理は問題なく動きました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/multidictionary/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>連想配列（Dictionary）③-キー一覧を取得する方法と高速化のテクニック-</title>
		<link>https://officevba.info/dictionary-keys/</link>
					<comments>https://officevba.info/dictionary-keys/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 22 Aug 2021 10:10:22 +0000</pubDate>
				<category><![CDATA[連想配列]]></category>
		<category><![CDATA[配列]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[Keys]]></category>
		<category><![CDATA[sumifs]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2274</guid>

					<description><![CDATA[目次 キー一覧を入力するのが面倒？サンプルシート連想配列のキー一覧を出力する方法①-Keysプロパティを使用-連想配列のキー一覧を出力する方法②-通常の配列を使用する連想配列を使用する際の最速の処理方法について キー一覧 [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-10" checked><label class="toc-title" for="toc-checkbox-10">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">キー一覧を入力するのが面倒？</a></li><li><a href="#toc2" tabindex="0">サンプルシート</a></li><li><a href="#toc3" tabindex="0">連想配列のキー一覧を出力する方法①-Keysプロパティを使用-</a></li><li><a href="#toc4" tabindex="0">連想配列のキー一覧を出力する方法②-通常の配列を使用する</a></li><li><a href="#toc5" tabindex="0">連想配列を使用する際の最速の処理方法について</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">キー一覧を入力するのが面倒？</span></h2>
<p>連想配列（Dictionary）を使用するようになって行数の多いデータの加工が楽になったのですが、連想配列を使用する際によく使う「キーの一覧を別シートに出力する方法」がないか調べてみたのでその手順についてご紹介します。</p>
<p>連想配列のキー一覧を出力するケースとしては、SUMIFS関数の代わりに連想配列を使用した場合などがあります。<br />
キー一覧を別シートに出力して、その右に連想配列に格納されている集計結果を表示させることが多いと思います。</p>
<p>これを知らないとせっかく連想配列を用いていても、別シートを作成する際に元シートのキー部分をコピーして貼り付け後に重複削除をするという面倒な手順を踏むことになります。</p>
<h2><span id="toc2">サンプルシート</span></h2>
<p>サンプルシートは前回こちらの記事で使ったものと同じものになります。<br />
商品の売り上げ一覧表の中で商品ごとの金額の合計を別シートに出力する用途をイメージしたものです。</p>
<p><a href="https://officevba.info/dictionary-sumifs/" target="_blank">前回ご紹介したコード</a>では、すでに重複のないJANコードの一覧はすでに別シートに出力している状態でVBAを実行していましたが、今回はその重複のないJANコード一覧も出力するフローを想定しています。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-700x814.jpg" alt="vba156シート1" width="700" height="814" /></a></p>
<h2><span id="toc3">連想配列のキー一覧を出力する方法①-Keysプロパティを使用-</span></h2>
<p>まずオーソドックスな方法です。<br />
連想配列に格納後、Keyプロパティでキーの一覧を取得します。</p>
<p>前回ご紹介したコードとほぼ同じですが、連想配列の宣言の時に参照設定を使用する方法にしないとKeyプロパティは参照できないようです。</p>
<div class="VBACode">Sub DictionaryでKeyを使ってキーの一覧取得()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;Dictionaryで項目入力開始-&#8221; &#038; Time</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’Dim dicJAN</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’Set dicJAN = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span></span><br />
<span class="VBA_Tab1"><span class="marker-under">Dim dicJAN As New Scripting.Dictionary</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN と金額の合計をDictionaryに格納</span></span><br />
<span class="VBA_Tab1">Dim y, i</span><br />
<span class="VBA_Tab1">With Sheets(1)</span><br />
<span class="VBA_Tab2">y = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がない場合、Dictionaryに追加</span></span><br />
<span class="VBA_Tab3">If Not dicJAN.Exists(.Cells(i, 1).Value) Then</span><br />
<span class="VBA_Tab4">dicJAN.Add .Cells(i, 1).Value, .Cells(i, 5)</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がある場合、Dictionaryに格納されている値を更新</span></span><br />
<span class="VBA_Tab3">Else</span><br />
<span class="VBA_Tab4">dicJAN(.Cells(i, 1).Value) = dicJAN(.Cells(i, 1).Value) + .Cells(i, 5)</span><br />
<span class="VBA_Tab3">End If</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN一覧B列に合計金額の出力</span></span><br />
<span class="VBA_Tab1">Dim r</span><br />
<span class="VBA_Tab1">r = Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2))</span><br />
<br />
<span class="VBA_Tab2">For i = 0 To dicJAN.Count － 1</span><br />
<span class="VBA_Tab3">r(i + 2, 1) = <span class="marker-under">dicJAN.Keys(i)</span></span><br />
<span class="VBA_Tab3">r(i + 2, 2) = dicJAN(r(i + 2, 1))</span><br />
<span class="VBA_Tab2">Next i</span><br />
<br />
<span class="VBA_Tab1">Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2)) = r</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;Dictionaryで項目入力終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<p>この方法で当初コードを書いていたのですが、Keys一覧の出力がものすごく遅くて悲しくなります。<br />
せっかく連想配列を使用しているのが台無しになるレベルでした。</p>
<p>そのため、次の方法を検討してみました。</p>
<h2><span id="toc4">連想配列のキー一覧を出力する方法②-通常の配列を使用する</span></h2>
<p>連想配列を使用する一番の理由は重複を判定するメソッドがあることですので、こちらを活用してキーを別の配列に格納する方法を考えました。</p>
<p>具体的にはキーとアイテムを連想配列に格納するタイミングで、キーを別の配列に格納しておいて、後でキーを格納した配列を呼び出すというフローです。</p>
<div class="VBACode">Sub Dictionaryでキーを配列に格納してから一覧取得()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;配列で項目出力開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’Dim dicJAN</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’Set dicJAN = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span></span><br />
<br />
<span class="VBA_Tab1">Dim dicJAN As New Scripting.Dictionary</span><br />
<span class="VBA_Tab1">Dim x</span><br />
<span class="VBA_Tab1">x = 0</span><br />
<span class="VBA_Tab1">Dim JANコード一覧()</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN と金額の合計をDictionaryに格納</span></span><br />
<span class="VBA_Tab1">Dim y, i</span><br />
<span class="VBA_Tab1">With Sheets(1)</span><br />
<span class="VBA_Tab2">y = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がない場合、Dictionaryに追加、JANコード一覧に登録</span></span><br />
<span class="VBA_Tab3">If Not dicJAN.Exists(.Cells(i, 1).Value) Then</span><br />
<span class="VBA_Tab4">dicJAN.Add .Cells(i, 1).Value, .Cells(i, 5)</span><br />
<br />
<span class="VBA_Tab4"><span class="VBA_Comment0">’キーを「JANコード一覧」配列に格納</span></span><br />
<span class="VBA_Tab4"><span class="marker-under">ReDim Preserve JANコード一覧(x)</span></span><br />
<span class="VBA_Tab4"><span class="marker-under">JANコード一覧(x) = .Cells(i, 1).Value</span></span><br />
<span class="VBA_Tab4"><span class="marker-under">x = x + 1</span></span><br />
<br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がある場合、Dictionaryに格納されている値を更新</span></span><br />
<span class="VBA_Tab3">Else</span><br />
<span class="VBA_Tab4">dicJAN(.Cells(i, 1).Value) = dicJAN(.Cells(i, 1).Value) + .Cells(i, 5)</span><br />
<span class="VBA_Tab3">End If</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN一覧B列に合計金額の出力</span></span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">For i = 0 To dicJAN.Count － 1</span><br />
<span class="VBA_Tab3"><span class="marker-under">.Cells(i + 2, 1) = JANコード一覧(i)</span></span><br />
<span class="VBA_Tab3">.Cells(i + 2, 2) = dicJAN(.Cells(i + 2, 1).Value)</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;配列で項目出力終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<p>こちらのコードを実行すると明らかに先ほどのコードよりも高速なことが確認できました。<br />
連想配列のキーを取り出す必要がある場合、別の配列にキーの一覧を格納して取得するフローが一番高速になると思います。</p>
<table border=1>
<tr>
<th align=center>キー入力速度比較</th>
<th align=center>Dictionary-Keys使用</th>
<th align=center>配列使用</th>
</tr>
<tr>
<td align=center>1回目</td>
<td align=center>0:00:39</td>
<td align=center>0:00:04</td>
</tr>
<tr>
<td align=center>2回目</td>
<td align=center>0:00:38</td>
<td align=center>0:00:04</td>
</tr>
<tr>
<td align=center>3回目</td>
<td align=center>0:00:40</td>
<td align=center>0:00:04</td>
</tr>
<tr>
<td align=center>平均</td>
<td align=center>0:00:39</td>
<td align=center>0:00:04</td>
</tr>
</table>
<h2><span id="toc5">連想配列を使用する際の最速の処理方法について</span></h2>
<p>キーを取り出す場合以外に、①連想配列にキーとアイテムを格納する際や、②連想配列の一覧を出力する際も配列を使用することでより高速化することが可能です。</p>
<p>使用するのはセルを二次元配列に格納して一気に値を取得・値を入力する方法になります<br />
興味がある方は<a href="https://officevba.info/cellstoarray/" target="_blank">こちらのページ</a>をご参照ください。</p>
<p>今回の処理においては1秒以下の実行時間まで短縮できました。<br />
セルを配列に格納する方法は慣れればすごく有効な手段になりますので、使いこなせるならぜひ覚えていただくのをおすすめします。</p>
<div class="VBACode">Sub Dictionaryを使用する際に配列を使って最速の処理()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’セルを配列に格納した後にDicrionaryに登録・セルへの入力も配列に格納したものを一気に入力</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;配列と連想配列最速開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’Dim dicJAN</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’Set dicJAN = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span></span><br />
<br />
<span class="VBA_Tab1">Dim dicJAN As New Scripting.Dictionary</span><br />
<span class="VBA_Tab1">Dim x</span><br />
<span class="VBA_Tab1">x = 0</span><br />
<span class="VBA_Tab1">Dim JANコード一覧()</span><br />
<span class="VBA_Tab1">Dim y, i</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN と金額の合計をDictionaryに格納</span></span><br />
<span class="VBA_Tab1"><span class="marker-under">Dim 参照範囲</span></span><br />
<span class="VBA_Tab1">y = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1"><span class="marker-under">参照範囲 = Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(y, 5))</span></span><br />
<br />
<span class="VBA_Tab1">For i = 2 To y</span><br />
<span class="VBA_Tab2"><span class="VBA_Comment0">’JANコードの登録がない場合、Dictionaryに追加、JANコード一覧に登録</span></span><br />
<span class="VBA_Tab2">If Not dicJAN.Exists(参照範囲(i, 1)) Then</span><br />
<span class="VBA_Tab3">dicJAN.Add 参照範囲(i, 1), 参照範囲(i, 5)</span><br />
<br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’キーを「JANコード一覧」配列に格納</span></span><br />
<span class="VBA_Tab3">ReDim Preserve JANコード一覧(x)</span><br />
<span class="VBA_Tab3">JANコード一覧(x) = 参照範囲(i, 1)</span><br />
<span class="VBA_Tab3">x = x + 1</span><br />
<br />
<span class="VBA_Tab2"><span class="VBA_Comment0">’JANコードの登録がある場合、Dictionaryに格納されている値を更新</span></span><br />
<span class="VBA_Tab2">Else</span><br />
<span class="VBA_Tab3">dicJAN(参照範囲(i, 1)) = dicJAN(参照範囲(i, 1)) + 参照範囲(i, 5)</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN一覧B列に合計金額の出力</span></span><br />
<span class="VBA_Tab1">Dim 入力範囲</span><br />
<span class="VBA_Tab1"><span class="marker-under">入力範囲 = Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2))</span></span><br />
<br />
<span class="VBA_Tab2">For i = 0 To dicJAN.Count － 1</span><br />
<span class="VBA_Tab3">入力範囲(i + 2, 1) = JANコード一覧(i)</span><br />
<span class="VBA_Tab3">入力範囲(i + 2, 2) = dicJAN(入力範囲(i + 2, 1))</span><br />
<span class="VBA_Tab2">Next i</span><br />
<br />
<span class="VBA_Tab1"><span class="marker-under">Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2)) = 入力範囲</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;配列と連想配列最速終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/dictionary-keys/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Dictionaryオブジェクトを用いたVBAの高速化②（SUMIFS関数の代用）</title>
		<link>https://officevba.info/dictionary-sumifs/</link>
					<comments>https://officevba.info/dictionary-sumifs/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 18 Jul 2021 03:46:57 +0000</pubDate>
				<category><![CDATA[マクロの処理時間の短縮]]></category>
		<category><![CDATA[連想配列]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[sumifs]]></category>
		<category><![CDATA[高速化]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2262</guid>

					<description><![CDATA[目次 行数が多いExcelシートを操作する際の集計作業の高速化SUMIFS関数とはDictionaryオブジェクト（連想配列）についてDictionaryオブジェクトを用いたSUMIFS関数と同様の処理の高速化用意したサ [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-12" checked><label class="toc-title" for="toc-checkbox-12">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">行数が多いExcelシートを操作する際の集計作業の高速化</a></li><li><a href="#toc2" tabindex="0">SUMIFS関数とは</a></li><li><a href="#toc3" tabindex="0">Dictionaryオブジェクト（連想配列）について</a></li><li><a href="#toc4" tabindex="0">Dictionaryオブジェクトを用いたSUMIFS関数と同様の処理の高速化</a><ol><li><a href="#toc5" tabindex="0">用意したサンプルシート</a></li><li><a href="#toc6" tabindex="0">処理内容について</a></li></ol></li><li><a href="#toc7" tabindex="0">処理時間の比較</a><ol><li><a href="#toc8" tabindex="0">①VBAでWorkSheetFunctionのSUMIFS関数を使用する</a></li><li><a href="#toc9" tabindex="0">②計算式としてSUMIFS関数をセルに格納する</a></li><li><a href="#toc10" tabindex="0">③計算式としてSUMIFS関数をセルに格納するが、1つ目のみ関数を入力、下の行についてはコピペをする</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">行数が多いExcelシートを操作する際の集計作業の高速化</span></h2>
<p>以前にVlookup関数の代わりにVBAでDictionaryオブジェクトを使用して高速化する手段をご紹介しました。<br />
この方法はVlookup関数以外の他の関数についても適用することができ、使いこなせるようになるとかなり便利です。</p>
<p>今回はDictionaryオブジェクトを使用してSUMIFS関数で算出される計算の高速化をご紹介します。</p>
<h2><span id="toc2">SUMIFS関数とは</span></h2>
<p>SUM関数・IF関数を使用される方にとってはSUMIFS関数を使ったことがなくても字面でイメージは湧きやすいと思いますが、SUMIFS関数は「範囲の中で特定の条件に一致するものを合計する」関数です。</p>
<p>私は売上一覧の明細の中から特定の商品の売上を抜き出したり、同じものが何行も含まれている一覧から商品カテゴリごとの売上を算出するのによく使用しています。</p>
<p>SUMIFS関数と似た関数でSUMIF関数というものもあります。<br />
SUMIF関数はSUMIFS関数とよく似ていますが、合致する条件項目を1つしか設定できません。</p>
<p>SUMIFS関数の方が条件設定を複数設定できる分汎用性が高く、私は基本的に条件が1つでもSUMIFS関数を使用しています。</p>
<h2><span id="toc3">Dictionaryオブジェクト（連想配列）について</span></h2>
<p>Dictionaryオブジェクトは特定のキーに対するアイテムを設定することで、該当のキーに対するアイテムを素早く呼び出す仕組みです。</p>
<p>通常のワークシート関数では行数が増えるごとに処理に時間がかかりますが、Dictionaryオブジェクトを用いた計算では処理時間が大幅に短縮されるのが特徴です。</p>
<p>また、キーは一意のものになりますので重複データから必要なものだけ取り出すときにも便利です。</p>
<h2><span id="toc4">Dictionaryオブジェクトを用いたSUMIFS関数と同様の処理の高速化</span></h2>
<h3><span id="toc5">用意したサンプルシート</span></h3>
<p>用意したシートは以下のようなものになります。<br />
シート1はJANコードと品名・包装単位・金額（仮）を記載していて、品名は何度か重複しています。<br />
全体の行数は約27万行、重複除いた品名の種類は約2万行で、重複の削除機能を使ってシート2にJANコードの一覧を用意しています。</p>
<li>シート1</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-700x814.jpg" alt="vba156シート1" width="700" height="814" class="alignnone size-large wp-image-2260" srcset="https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-700x814.jpg 700w, https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-258x300.jpg 258w, https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967-768x893.jpg 768w, https://officevba.info/wp-content/uploads/2021/07/439815dd8e5784bbb079387253538967.jpg 992w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<li>シート2</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475.jpg" alt="vba156シート2" width="537" height="1161" class="alignnone size-full wp-image-2261" srcset="https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475.jpg 537w, https://officevba.info/wp-content/uploads/2021/07/bfe92b580f9418d7de5f6fcdb3a50475-139x300.jpg 139w" sizes="(max-width: 537px) 100vw, 537px" /></a></p>
<h3><span id="toc6">処理内容について</span></h3>
<p>今回私が作成したコードは以下のものになります。</p>
<div class="VBACode">Sub Dictionaryを使ってSUMIFS関数の代わりを実行()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;Dictionary開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim dicJAN</span><br />
<span class="VBA_Tab1">Set dicJAN = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN と金額の合計をDictionaryに格納</span></span><br />
<span class="VBA_Tab1">Dim y, i</span><br />
<span class="VBA_Tab1">With Sheets(1)</span><br />
<span class="VBA_Tab2">y = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がない場合、Dictionaryに追加</span></span><br />
<span class="VBA_Tab3">If Not dicJAN.Exists(.Cells(i, 1).Value) Then</span><br />
<span class="VBA_Tab4">dicJAN.Add .Cells(i, 1).Value, .Cells(i, 5)</span><br />
<span class="VBA_Tab3"><span class="VBA_Comment0">’JANコードの登録がある場合、Dictionaryに格納されている値を更新</span></span><br />
<span class="VBA_Tab3">Else</span><br />
<span class="VBA_Tab4">dicJAN(.Cells(i, 1).Value) = dicJAN(.Cells(i, 1).Value) + .Cells(i, 5)</span><br />
<span class="VBA_Tab3">End If</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’JAN一覧B列に合計金額の出力</span></span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">For i = 2 To dicJAN.Count + 1</span><br />
<span class="VBA_Tab3">.Cells(i, 2) = dicJAN(.Cells(i, 1).Value)</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;Dictionary終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<p>まずシート1のJANコードと金額をDicrionaryオブジェクトに格納します。</p>
<p>Dictionary関数に格納する際、Vlookup関数の代わりの場合は重複分を無視することで同じ挙動になりますが、SUMIFS関数の代わりに使用する場合は以下の条件分岐を用います。</p>
<p>・①JANコードがキーとして登録されていない場合：キー（JANコード）とアイテム（金額）を追加<br />
・②JANコードがキーとして登録されていない場合：キー（JANコード）に登録されているアイテム（金額）に現在の行の金額を追加</p>
<h2><span id="toc7">処理時間の比較</span></h2>
<p>上記Dictionaryオブジェクトを用いた処理時間と通常通りSUMIFS関数を用いた場合の処理時間について比較してみました。<br />
比較するのに用いたコードは以下の3通りです。</p>
<h3><span id="toc8">①VBAでWorkSheetFunctionのSUMIFS関数を使用する</span></h3>
<div class="VBACode">Sub WorksheetFunctionのSUMIFS関数を使う()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;worksheet関数開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim y1, y2, i</span><br />
<br />
<span class="VBA_Tab1">y1 = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">y2 = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y2</span><br />
<span class="VBA_Tab3">.Cells(i, 3) = WorksheetFunction.SumIfs(Range(Sheets(1).Cells(2, 5), Sheets(1).Cells(y1, 5)), Range(Sheets(1).Cells(2, 1), Sheets(1).Cells(y1, 1)), .Cells(i, 1))</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;worksheet関数終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<h3><span id="toc9">②計算式としてSUMIFS関数をセルに格納する</span></h3>
<div class="VBACode">Sub SUMIFS関数()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim y1, y2, i</span><br />
<br />
<span class="VBA_Tab1">y1 = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">y2 = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">For i = 2 To y2</span><br />
<span class="VBA_Tab3">.Cells(i, 4) = &#8220;=SUMIFS(Sheet1!$E$2:$E$&#8221; &#038; y1 &#038; &#8220;,Sheet1!$A$2:$A$&#8221; &#038; y1 &#038; &#8220;,Sheet2!&#8221; &#038; Replace(.Cells(i, 1).Address, &#8220;$&#8221;, &#8220;&#8221;) &#038; &#8220;)&#8221;</span><br />
<span class="VBA_Tab2">Next i</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<h3><span id="toc10">③計算式としてSUMIFS関数をセルに格納するが、1つ目のみ関数を入力、下の行についてはコピペをする</span></h3>
<div class="VBACode">Sub SUMIFS関数コピペ()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’開始時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数コピペ開始-&#8221; &#038; Time</span><br />
<span class="VBA_Tab1">Dim y1, y2, i</span><br />
<br />
<span class="VBA_Tab1">y1 = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">With Sheets(2)</span><br />
<span class="VBA_Tab2">y2 = .Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab2">.Cells(2, 5) = &#8220;=SUMIFS(Sheet1!$E$2:$E$&#8221; &#038; y1 &#038; &#8220;,Sheet1!$A$2:$A$&#8221; &#038; y1 &#038; &#8220;,Sheet2!&#8221; &#038; Replace(.Cells(2, 1).Address, &#8220;$&#8221;, &#8220;&#8221;) &#038; &#8220;)&#8221;</span><br />
<span class="VBA_Tab2">.Cells(2, 5).Copy</span><br />
<span class="VBA_Tab2">Range(.Cells(3, 5), Cells(y2, 5)).PasteSpecial</span><br />
<span class="VBA_Tab1">End With</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment0">’終了時間の記載</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;関数コピペ終了-&#8221; &#038; Time</span><br />
<br />
End Sub</div>
<p>処理時間の違いは以下の表の通りです。</p>
<table border=1>
<tr>
<td align=center></td>
<th align=center>Dictionary</th>
<th align=center>①WorkSheetFunction</th>
<th align=center>②計算式の入力</th>
<th align=center>③計算式コピペ</th>
</tr>
<tr>
<td align=center>1回目</td>
<td align=center>0:00:03</td>
<td align=center>0:21:14</td>
<td align=center>0:21:09</td>
<td align=center>0:01:57</td>
</tr>
<tr>
<td align=center>2回目</td>
<td align=center>0:00:04</td>
<td align=center>0:21:02</td>
<td align=center>0:21:09</td>
<td align=center>0:01:56</td>
</tr>
<tr>
<td align=center>3回目</td>
<td align=center>0:00:03</td>
<td align=center>0:21:04</td>
<td align=center>0:21:05</td>
<td align=center>0:01:54</td>
</tr>
<tr>
<td align=center>平均</td>
<td align=center>0:00:03</td>
<td align=center>0:21:07</td>
<td align=center>0:21:08</td>
<td align=center>0:01:56</td>
</tr>
</table>
<p>Dictionaryオブジェクトを用いることで処理が圧倒的に高速になりました。</p>
<p>関数自体のコピペも遅くはないですが、もっと条件分岐を複雑にすれば差はより大きく広がります。<br />
WorkSheetFunctionなどは行数が増えると処理にかなり時間がかかることから、ある程度たくさんの行を扱う業務では使いにくいと思います。</p>
<p>ピポットテーブルも方法としてはありですが、そのあとの加工がやりにくい、フォーマットが自由に設定できないなどがあるため、今回のように単純に関数の代わりに高速処理ができるVBAのコードは必要な場面も多いと思います。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/dictionary-sumifs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>タスクスケジューラを使用してVBA（マクロ）を自動実行する方法</title>
		<link>https://officevba.info/taskscheduler/</link>
					<comments>https://officevba.info/taskscheduler/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Wed, 05 May 2021 10:13:45 +0000</pubDate>
				<category><![CDATA[VBScript]]></category>
		<category><![CDATA[タスクスケジューラ]]></category>
		<category><![CDATA[Batファイル]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[VBS]]></category>
		<category><![CDATA[自動実行]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2229</guid>

					<description><![CDATA[目次 決まった時間に実行することが決まっているVBAの完全自動化を目指すタスクスケジューラについてタスクスケジューラでVBAを実行する手順の概要タスクスケジューラでVBAを実行する手順タスクスケジューラでVBAを実行する [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-14" checked><label class="toc-title" for="toc-checkbox-14">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">決まった時間に実行することが決まっているVBAの完全自動化を目指す</a></li><li><a href="#toc2" tabindex="0">タスクスケジューラについて</a></li><li><a href="#toc3" tabindex="0">タスクスケジューラでVBAを実行する手順の概要</a></li><li><a href="#toc4" tabindex="0">タスクスケジューラでVBAを実行する手順</a></li><li><a href="#toc5" tabindex="0">タスクスケジューラでVBAを実行する設定方法</a><ol><li><a href="#toc6" tabindex="0">1.VBSファイルの作成</a></li><li><a href="#toc7" tabindex="0">2.タスクスケジューラの設定</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">決まった時間に実行することが決まっているVBAの完全自動化を目指す</span></h2>
<p>今回はタスクスケジューラを用いて定期的にVBAを自動実行する方法についてご紹介します。</p>
<p>最初だけ設定・準備が少し面倒ですが、これを設定しておくことで面倒な操作を完全自動化することができるようになります。</p>
<p>ダブルクリックだけでVBAは実行できるので特に時短にならないのでは？と思う方もいるかと思いますが、このタスクスケジューラを用いての自動実行については完全自動化による「作業忘れの防止」と「頻回の操作」を目的としています。</p>
<p>例えば私が実際に使っているものとしては「1週間に1回あるサービスの利用状況を集計してメールで報告する」や「同僚のスケジュールを毎日全員に送信する」というような処理が完全に自動化されます。</p>
<p>処理にかかる時間がVBAを手動で実行したとして1週間に1回10分・毎日1回5分であったとしても、他にやらなければならないことがあったりするとつい忘れることもあると思います。</p>
<p>またこの小さな作業が積み重なるとそこそこ時間がとられることになります。<br />
完全自動化することにより、まず作業忘れを完全になくすことが可能です。</p>
<p>さらに作業時間が0なので「毎日1時間おきに作業する」や、「1か月のうち1日だけの作業」などもあらかじめ登録しておけば手間も負担も全くありません。<br />
以上のことから手作業でVBAを実行する自動化のレベルとは明らかに1ランク上の自動化が可能になります。</p>
<h2><span id="toc2">タスクスケジューラについて</span></h2>
<p>タスクスケジューラはWindowsに標準の機能で、実行するための条件（トリガー）と実行内容（操作）を設定しておくことで、条件を満たした場合に特定の操作を行うことが可能です。</p>
<p>主にアプリケーションのアップデートの確認などに使用することが多いようですが、我々もこの機能を活用して自動処理の仕組みを作ることが可能です。</p>
<p>実行内容はBatファイルを開くという操作を指定しておくのが最も手間がかからないらしく、VBAを実行するためにはBatファイルとVBSファイルを用意して順に実行するというフローを使うことが多いようです。（詳細の手順は後で示します。）</p>
<h2><span id="toc3">タスクスケジューラでVBAを実行する手順の概要</span></h2>
<p>タスクスケジューラでVBAを実行する方法としては①VBSファイルのみを使用する方法と②VBSファイルとBatファイルを使用する方法の2通りがあります。</p>
<p>私は汎用性と利便性を考慮して①VBSファイルのみを使用する方法をよく使っていますのでこちらを紹介します。</p>
<h2><span id="toc4">タスクスケジューラでVBAを実行する手順</span></h2>
<li>〇準備</li>
<p>(1)実行したいVBAコードを記載したファイル</p>
<p>(2)VBAを実行するためのVBSファイル</p>
<p>(3)VBAを描いたファイル・VBSファイルを同じフォルダに格納する</p>
<li>〇手順</li>
<p>①タスクスケジューラでVBSファイルを起動</p>
<p>②VBSファイルでVBAを実行する</p>
<p>タスクスケジューラでVBSファイルを起動→VBSでVBAが実行されるというフローになります。</p>
<p><a href="https://officevba.info/vbsvbastart/" target="_blank">前回VBSを用いてVBAをダブルクリックで実行する方法</a>をご紹介しましたが、VBSを用いてダブルクリックする方法は単体ではそこまで便利ではなく、今回ご紹介するタスクスケジューラを用いた自動実行と組み合わせて初めて本領を発揮します。</p>
<h2><span id="toc5">タスクスケジューラでVBAを実行する設定方法</span></h2>
<h3><span id="toc6">1.VBSファイルの作成</span></h3>
<p>先ほど概要をご説明した通り、VBAのコードを書いたファイルのほか、VBSファイルを作成してVBS起動→VBA実行のフローを構築します。</p>
<p>また、VBSの中でこの格納したフォルダのパスを取得しておくことで使いまわす際に便利なようにしています。</p>
<p>VBSファイルはテキストエディタなどでコードを書いた後、拡張子をそれぞれ「.vbs」に変更すればOKです。<br />
今回例として下記の2つのファイルを作成して、デスクトップの「タスクスケジューラ」というフォルダに格納しています。</p>
<p><a href="https://officevba.info/wp-content/uploads/2024/09/vba155-1_2.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2024/09/vba155-1_2-700x140.jpg" alt="" width="700" height="140" class="alignnone size-large wp-image-2526" srcset="https://officevba.info/wp-content/uploads/2024/09/vba155-1_2-700x140.jpg 700w, https://officevba.info/wp-content/uploads/2024/09/vba155-1_2-300x60.jpg 300w, https://officevba.info/wp-content/uploads/2024/09/vba155-1_2-768x154.jpg 768w, https://officevba.info/wp-content/uploads/2024/09/vba155-1_2.jpg 1017w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<li>〇VBSファイル（ファイル名：01_タスクスケジューラでVBA実行.vbs）</li>
<div class="VBACode">
<pre>
Dim ExApp:Set ExApp = CreateObject("Excel.Application")
Dim FSO:Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WSH:Set WSH = CreateObject("Wscript.Shell")
'格納されているフォルダのパスを取得(ExcelVBAで言うところのThisWorkbook.Pathみたいなもの)
Dim objPath:objPath = FSO.GetParentFolderName(WScript.ScriptFullName)

ExApp.visible = true
WScript.Sleep 3000

'ファイル開く
ExApp.Workbooks.open objPath &#038; "\" &#038; "02_タスクスケジューラでVBA実行.xlsm"

'マクロ実行
ExApp.Application.Run "メッセージ表示"

'ファイルを閉じる
WScript.Sleep 3000
ExApp.Quit

set ExApp = Nothing
set objPath = Nothing
set WSH = Nothing
set FSO = Nothing
</pre>
</div>
<li>〇VBA（ファイル名：02_タスクスケジューラでVBA実行.xlsm）</li>
<div class="VBACode">
<pre>
Sub メッセージ表示()
    MsgBox "実行しました"
End Sub
</pre>
</div>
<p>VBSファイルをダブルクリックした場合にExcelファイルが起動し、マクロが実行できればOKです。</p>
<p>今回の場合メッセージボックスが表示されるのでOKをクリックすると作業が次に進み、ファイルが閉じる仕様となります。</p>
<h3><span id="toc7">2.タスクスケジューラの設定</span></h3>
<p>上記3つのファイルが作成できて、手動できちんと動作することが確認できたらタスクスケジューラでBatファイルを起動する準備をしていきます。</p>
<li>①「Windowsマーク」→「Windows管理ツール」→「タスクスケジューラ」を選択します。</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/05/vba155-2.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-2.jpg" alt="vba155-2" width="403" height="818" class="alignnone size-full wp-image-2234" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-2.jpg 403w, https://officevba.info/wp-content/uploads/2021/05/vba155-2-148x300.jpg 148w" sizes="(max-width: 403px) 100vw, 403px" /></a></p>
<li>②起動したタスクスケジューラの画面で「タスクの作成」をクリックします。</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/05/vba155-3.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-3-700x383.jpg" alt="vba155-3" width="700" height="383" class="alignnone size-large wp-image-2235" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-3-700x383.jpg 700w, https://officevba.info/wp-content/uploads/2021/05/vba155-3-300x164.jpg 300w, https://officevba.info/wp-content/uploads/2021/05/vba155-3-768x420.jpg 768w, https://officevba.info/wp-content/uploads/2021/05/vba155-3-1536x839.jpg 1536w, https://officevba.info/wp-content/uploads/2021/05/vba155-3-2048x1119.jpg 2048w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<li>③全般タブでタスクの名前を入力します。（名前は自分で覚えやすい名前でOK）</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/05/vba155-4.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-4.jpg" alt="vba155-4" width="677" height="487" class="alignnone size-full wp-image-2236" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-4.jpg 677w, https://officevba.info/wp-content/uploads/2021/05/vba155-4-300x216.jpg 300w" sizes="(max-width: 677px) 100vw, 677px" /></a> <a href="https://officevba.info/wp-content/uploads/2021/05/vba155-5.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-5.jpg" alt="vba155-5" width="672" height="480" class="alignnone size-full wp-image-2237" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-5.jpg 672w, https://officevba.info/wp-content/uploads/2021/05/vba155-5-300x214.jpg 300w" sizes="(max-width: 672px) 100vw, 672px" /></a></p>
<li>④「トリガータブ」で「新規タブ」をクリックして起動の条件を設定します。</li>
<p>今回は1時間に1回毎日実行するように設定してみます。<br />
設定項目の左側を毎日に指定、詳細設定の繰り返し間隔を1時間に設定してOKをクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/05/vba155-6.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-6.jpg" alt="vba155-6" width="675" height="483" class="alignnone size-full wp-image-2238" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-6.jpg 675w, https://officevba.info/wp-content/uploads/2021/05/vba155-6-300x215.jpg 300w" sizes="(max-width: 675px) 100vw, 675px" /></a> <a href="https://officevba.info/wp-content/uploads/2021/05/vba155-7.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-7-700x593.jpg" alt="vba155-7" width="700" height="593" class="alignnone size-large wp-image-2239" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-7-700x593.jpg 700w, https://officevba.info/wp-content/uploads/2021/05/vba155-7-300x254.jpg 300w, https://officevba.info/wp-content/uploads/2021/05/vba155-7.jpg 754w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<li>⑤前の画面に戻るので「操作タブ」で「新規」をクリックして起動するプログラムを選択します。</li>
<p>プログラム/スクリプトの欄に作成したVBSファイルのフルパスを入力します。<br />
「参照」ボタンをクリックしてファイルを選択すれば入力されます。</p>
<p>また、開始（オプション）に格納されているフォルダのパスを入力します。<br />
詳しくは私も把握していませんが、これを忘れると起動しないので、忘れずに入力するようにしましょう。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/05/vba155-8.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-8.jpg" alt="vba155-8" width="673" height="483" class="alignnone size-full wp-image-2240" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-8.jpg 673w, https://officevba.info/wp-content/uploads/2021/05/vba155-8-300x215.jpg 300w" sizes="(max-width: 673px) 100vw, 673px" /></a> <a href="https://officevba.info/wp-content/uploads/2021/05/vba155-9.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-9.jpg" alt="vba155-9" width="565" height="620" class="alignnone size-full wp-image-2241" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-9.jpg 565w, https://officevba.info/wp-content/uploads/2021/05/vba155-9-273x300.jpg 273w" sizes="(max-width: 565px) 100vw, 565px" /></a></p>
<li>⑥元の画面に戻るので「OK」をクリックします。</li>
<p><a href="https://officevba.info/wp-content/uploads/2021/05/vba155-10.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-10.jpg" alt="vba155-10" width="675" height="484" class="alignnone size-full wp-image-2242" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-10.jpg 675w, https://officevba.info/wp-content/uploads/2021/05/vba155-10-300x215.jpg 300w" sizes="(max-width: 675px) 100vw, 675px" /></a></p>
<li>⑦タスクスケジューラの一覧に作成したタスクが表示されます。</li>
<p>右の実行ボタンをクリックしてきちんと起動すれば決まったタイミングから自動実行が開始されます。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/05/vba155-11-2.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/05/vba155-11-2-700x394.jpg" alt="vba155-11-2" width="700" height="394" class="alignnone size-large wp-image-2246" srcset="https://officevba.info/wp-content/uploads/2021/05/vba155-11-2-700x394.jpg 700w, https://officevba.info/wp-content/uploads/2021/05/vba155-11-2-300x169.jpg 300w, https://officevba.info/wp-content/uploads/2021/05/vba155-11-2-768x432.jpg 768w, https://officevba.info/wp-content/uploads/2021/05/vba155-11-2-1536x864.jpg 1536w, https://officevba.info/wp-content/uploads/2021/05/vba155-11-2-2048x1152.jpg 2048w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p>これらのファイルは一回作っておくとあとはコピペをベースに少し設定を変更すればすぐに使えるようになるので、慣れてくると簡単に作れるようになります。</p>
<p>最初の設定だけ少し面倒ですが、そのあと勝手に仕事が進むので私はかなり重宝しています。<br />
また便利な使い方など思いついたらアップするようにします。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/taskscheduler/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>VBSを使ってマクロ（VBA）をダブルクリックで実行</title>
		<link>https://officevba.info/vbsvbastart/</link>
					<comments>https://officevba.info/vbsvbastart/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Sun, 18 Apr 2021 07:18:44 +0000</pubDate>
				<category><![CDATA[VBA共通]]></category>
		<category><![CDATA[VBScript]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[VBS]]></category>
		<category><![CDATA[実行]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2212</guid>

					<description><![CDATA[目次 マクロを実行するのが面倒な場合があるマクロ（VBA）をボタン一つで実行する方法についてExcelのマクロ（VBA）を実行するサンプルコードAccessのマクロ（VBA）を実行するサンプルコード マクロを実行するのが [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-16" checked><label class="toc-title" for="toc-checkbox-16">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">マクロを実行するのが面倒な場合がある</a></li><li><a href="#toc2" tabindex="0">マクロ（VBA）をボタン一つで実行する方法について</a></li><li><a href="#toc3" tabindex="0">Excelのマクロ（VBA）を実行するサンプルコード</a></li><li><a href="#toc4" tabindex="0">Accessのマクロ（VBA）を実行するサンプルコード</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">マクロを実行するのが面倒な場合がある</span></h2>
<p>ショートカットやボタンで実行できるように設定していたとしても、毎回ファイルを開いてマクロを実行するのが面倒に感じることがあります。</p>
<p>一日に何回も実行する必要がある場合、特に面倒に感じるのですが、イベントプロシージャのようにファイルを開くたびに毎回勝手に実行されるのも困るし…と悩んでいたところVBS（VBScript）を使う便利な方法がありましたのでご紹介します。</p>
<p>この方法を使えば作成したVBSファイルをダブルクリックするだけでマクロを実行できるようになります。</p>
<p>またこの方法を少し応用すれば「1日1回朝8時」や、「平日1時間おきに実行」などパソコンに登録したタイミングで自動実行することができるようになります。<br />
まず今回はダブルクリックでファイルを開いて実行する処理ができるVBSファイルの作成方法についてご紹介します。</p>
<h2><span id="toc2">マクロ（VBA）をボタン一つで実行する方法について</span></h2>
<p>VBAを実行するためにVBSファイルというものを作成します。<br />
VBSファイルはマクロ（VBA）を実行するためのコードを記入したもので、テキストで簡単に作成できます。</p>
<p>VBSの基本的な文法はVBAとほぼ同じですし、マクロ（VBA）を実行するためのコードはすごく簡単なので、少しVBAを触っている人なら手間取らずに作成できると思います。</p>
<p>コードを書いた後のテキストファイルは保存後に拡張子を「.txt」から「.vbs」に変更することで、VBSファイルとしてパソコンからプログラムを実行することができるようになります。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/04/eb10f35216006f89ff563d389525bd51.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/04/eb10f35216006f89ff563d389525bd51.jpg" alt="vba154_拡張子変更" width="221" height="142" class="alignnone size-full wp-image-2226" /></a></p>
<p>拡張子が表示されていない方はどこかのフォルダを開いた際の表示タブの中にある「表示/非表示」項目の中の「ファイル名拡張子」にチェックを入れましょう。</p>
<p><a href="https://officevba.info/wp-content/uploads/2021/04/1a1e722db7dc78a78fd1e2131064d673.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2021/04/1a1e722db7dc78a78fd1e2131064d673-300x85.jpg" alt="vba154_ファイル名拡張子" width="300" height="85" class="alignnone size-medium wp-image-2225" srcset="https://officevba.info/wp-content/uploads/2021/04/1a1e722db7dc78a78fd1e2131064d673-300x85.jpg 300w, https://officevba.info/wp-content/uploads/2021/04/1a1e722db7dc78a78fd1e2131064d673-700x199.jpg 700w, https://officevba.info/wp-content/uploads/2021/04/1a1e722db7dc78a78fd1e2131064d673-768x219.jpg 768w, https://officevba.info/wp-content/uploads/2021/04/1a1e722db7dc78a78fd1e2131064d673.jpg 1536w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2><span id="toc3">Excelのマクロ（VBA）を実行するサンプルコード</span></h2>
<p>Excelのマクロ（VBA）を実行するサンプルコードは下記の通りです。<br />
下記は同じフォルダの中の「ファイル1.xlsm」ファイルの「マクロ1」というプロシージャ名のマクロを実行します。</p>
<div class="VBACode">
<pre>
Dim ExApp:Set ExApp = CreateObject("Excel.Application")
Dim FSO:Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WSH:Set WSH = CreateObject("Wscript.Shell")
'格納されているフォルダのパスを取得(ExcelVBAで言うところのThisWorkbook.Pathみたいなもの)
Dim objPath:objPath = FSO.GetParentFolderName(WScript.ScriptFullName)

ExApp.visible = true
WScript.Sleep 3000

'ファイル開く
ExApp.Workbooks.open objPath &#038; "\" &#038; "ファイル1.xlsm"

'マクロ実行
ExApp.Application.Run "メッセージ表示"

'ファイルを閉じる
WScript.Sleep 3000
ExApp.Quit

set ExApp = Nothing
set objPath = Nothing
set WSH = Nothing
set FSO = Nothing
</pre>
</div>
<p>VBSファイルはプロシージャ名などを書いたり、Sub～End Subなどの記述は必要なく、書いたものを即座に実行してくれます。</p>
<p>ExcelVBAからほかのOfficeソフト（AccessとかOutlookなど）を呼び出すときと同じく、まずはExcelを呼び出す設定をします。<br />
Excelをオブジェクト型の変数に格納したあと、対象のファイル名とマクロ名（プロシージャ名）を指定することで実行できるようになります。</p>
<p>ファイルを開いたり、マクロを実行したりする間には少し余裕を持って待機時間を設定しています。<br />
最後に対象のファイルを閉じるように設定しておくとダブルクリックするだけで処理が完結します。</p>
<p>マクロを実行したExcelファイルそのものを使用したり確認したりする場合、最後の閉じる操作は不要ですので削除してください。<br />
（VBAと同じくアポストロフィ「’」でコメントアウトも可能です。）</p>
<p>WSHを用いてデスクトップなどの特殊フォルダを取得したり、FSOでVBSの格納フォルダを指定してから相対パスなどを使って対象ファイルを指定することも可能ですが、別記事でお伝えする定期実行などをする際にうまく働かないことがあるようです。</p>
<p>ですので個人的にはフルパスで対象ファイルを指定することがおすすめです。</p>
<h2><span id="toc4">Accessのマクロ（VBA）を実行するサンプルコード</span></h2>
<p>基本的にAccessについてもExcelとほぼ同じコードになります。<br />
以下は同じフォルダの中の「データベース.accdb」ファイルの「マクロ1」というプロシージャ名のマクロを実行します。</p>
<div class="VBACode">
<pre>
Dim AcApp:Set AcApp = CreateObject("Access.Application")
Dim FSO:Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WSH:Set WSH = CreateObject("Wscript.Shell")
'格納されているフォルダのパスを取得(AccessVBAで言うところのApplication.CurrentProject.Pathみたいなもの)
Dim objPath:objPath = FSO.GetParentFolderName(WScript.ScriptFullName)

AcApp.visible = true
WScript.Sleep 3000
'ファイル開く
AcApp.OpenCurrentDatabase objPath &#038; "\" &#038; "データベース.accdb"

'マクロ実行
AcApp.Application.Run "マクロ1"

'ファイルを閉じる
WScript.Sleep 3000
AcApp.Quit

set AcApp = Nothing
set objPath = Nothing
set WSH = Nothing
set FSO = Nothing
</pre>
</div>
<p>これらは手作業でマクロを何度も実行する際に役立ちます。<br />
より自動化の幅を増やせるようにするため、次は<a href="https://officevba.info/taskscheduler/" target="_blank">こちら</a>でマクロの定期実行の方法をご紹介します。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/vbsvbastart/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Dictionaryオブジェクトを用いたVBAの高速化①（VlookUp関数の代用）</title>
		<link>https://officevba.info/dictionary-vlookup/</link>
					<comments>https://officevba.info/dictionary-vlookup/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Tue, 03 Nov 2020 04:58:29 +0000</pubDate>
				<category><![CDATA[マクロの処理時間の短縮]]></category>
		<category><![CDATA[連想配列]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[VBA]]></category>
		<category><![CDATA[配列]]></category>
		<category><![CDATA[高速化]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2192</guid>

					<description><![CDATA[目次 行数が多いExcelシートを操作するのにVBAで関数を使うと遅いDictionaryオブジェクトの使用するための事前準備今回処理を行うワークシートについてVlookUp関数を使用した値の記載方法についてDictio [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-18" checked><label class="toc-title" for="toc-checkbox-18">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">行数が多いExcelシートを操作するのにVBAで関数を使うと遅い</a></li><li><a href="#toc2" tabindex="0">Dictionaryオブジェクトの使用するための事前準備</a></li><li><a href="#toc3" tabindex="0">今回処理を行うワークシートについて</a></li><li><a href="#toc4" tabindex="0">VlookUp関数を使用した値の記載方法について</a></li><li><a href="#toc5" tabindex="0">Dictionaryオブジェクトを使用したVBAの使用方法とサンプルコードについて</a></li><li><a href="#toc6" tabindex="0">処理速度の差について集計</a></li></ol>
    </div>
  </div>

<h2><span id="toc1">行数が多いExcelシートを操作するのにVBAで関数を使うと遅い</span></h2>
<p>VlookUp関数をVBAで使うにあたって、対象となるデータが少ない場合は特に問題ないのですが、多くなるにつれてデータ処理に時間がかかります。</p>
<p>私は仕事で20万行のデータに10万行のマスタデータからコードを抽出する必要があり、VBAでWorkSteetFunctionのVlookUp関数を使用すると数時間単位で時間がかかってしまうことがありました。</p>
<p>行数の多いExcelについてはVBAの処理速度が遅くなってしまうことが多くの人にとって悩みになると思います。</p>
<p>私の事例では連想配列（Dictionaryオブジェクト）を使用してコードを書きなおすことで処理速度が格段に速くなりましたので、連想配列（Dictionary）オブジェクトの使い方について何回かに分けてご紹介します。</p>
<p>今回はVlookup関数の代用としてDictionaryオブジェクトを使用する方法についてご紹介します。</p>
<h2><span id="toc2">Dictionaryオブジェクトの使用するための事前準備</span></h2>
<p>事前準備については<a href="https://officevba.info/dictionary-top/" target="_blank">こちら</a>のページに記載しておりますので詳しく知りたい方はリンク先をご確認ください。</p>
<p>また、セルの値を取得するのにそのまま取得するのではなく、配列に一度格納する方法を使用しております。<br />
これはVBAの高速化の手順で最も有効な手段の1つです。</p>
<p>詳細は<a href="https://officevba.info/cellstoarray/" target="_blank">こちら</a>のページに記載しておりますので併せてご確認いただけるとありがたいです。</p>
<h2><span id="toc3">今回処理を行うワークシートについて</span></h2>
<p>私が今回業務で行ったものの一部になるのですが、商品の仕入伝票一覧に商品マスタの「YJコード」というコードを結合するものです。<br />
（データの内容は変更していますので、世の中に存在しない商品などが記載されています。）</p>
<p>商品の仕入一覧が約20万行、商品マスタが約10万行あり、VlookUpを使用するだけでは時間がかかっていました。<br />
サンプルの伝票一覧と商品マスタは下記のようなイメージです。</p>
<p><a href="https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-300x49.jpg" alt="" width="300" height="49" class="alignnone size-medium wp-image-2191" srcset="https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-300x49.jpg 300w, https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-700x113.jpg 700w, https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced-768x124.jpg 768w, https://officevba.info/wp-content/uploads/2020/11/5315246759b65c0eb4f5124e675f2ced.jpg 1136w" sizes="(max-width: 300px) 100vw, 300px" /></a>　<a href="https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-300x174.jpg" alt="" width="300" height="174" class="alignnone size-medium wp-image-2190" srcset="https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-300x174.jpg 300w, https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-700x405.jpg 700w, https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d-768x444.jpg 768w, https://officevba.info/wp-content/uploads/2020/11/bf33ca4973fac54e1e0161293426745d.jpg 795w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2><span id="toc4">VlookUp関数を使用した値の記載方法について</span></h2>
<p>Dictionaryオブジェクトを使用したVBAコードがどれくらい高速になるかを検証するために、通常のVlookUp関数をWorkSheetFunctionで呼び出した場合と比較することにします。</p>
<p>配列に格納してからセルの値を変更するものも用意しています。</p>
<p>コードは「伝票一覧」の最後の列に「商品マスタ」に記載されている「YJコード」を追記するものになります。</p>
<div class="VBACode">Sub 配列なしのVlookupのVBAコード1()<br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<br />
<span class="VBA_Tab1">Sheets(&#8220;伝票&#8221;).Cells(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">On Error Resume Next <span class="VBA_Comment">’VlookUpで引きあたるものがないとき無視する</span></span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">Sheets(&#8220;伝票&#8221;).Cells(i, 9) = WorksheetFunction.VLookup(Sheets(&#8220;伝票&#8221;).Cells(i, 4), Range(Sheets(&#8220;商品マスタ&#8221;).Cells(1, 1), Sheets(&#8220;商品マスタ&#8221;).Cells(マスタ下端行, 4)), 2, False)</span><br />
<span class="VBA_Tab1">Next i</span><br />
<span class="VBA_Tab1">On Error GoTo 0</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<p>こちらはセルに直接値を書き込むのではなく、配列に格納して変更を加えてから一括でセルに書き込むフローになります。<br />
一般的な処理ではステップは増えますが処理速度が高速になります。</p>
<div class="VBACode">Sub 配列ありのVlookupのVBAコード2()<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルに直接書き込むのではなく配列に格納して変更した後、一括で書き込む</span></span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">Dim 参照範囲</span><br />
<span class="VBA_Tab1">参照範囲 = Range(Sheets(&#8220;商品マスタ&#8221;).Cells(1, 1), Sheets(&#8220;商品マスタ&#8221;).Cells(マスタ下端行, 4))</span><br />
<br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルに直接書き込むよりスピードアップのために配列に格納する</span></span><br />
<span class="VBA_Tab1">Dim 伝票範囲</span><br />
<span class="VBA_Tab1">伝票範囲 = Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9))</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<span class="VBA_Tab1">伝票範囲(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">On Error Resume Next <span class="VBA_Comment">’VlookUpで引きあたるものがないとき無視する</span></span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">伝票範囲(i, 9) = WorksheetFunction.VLookup(伝票範囲(i, 4), 参照範囲, 2, False)</span><br />
<span class="VBA_Tab1">Next i</span><br />
<span class="VBA_Tab1">On Error GoTo 0</span><br />
<br />
<span class="VBA_Tab1">Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9)) = 伝票範囲</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<h2><span id="toc5">Dictionaryオブジェクトを使用したVBAの使用方法とサンプルコードについて</span></h2>
<p>キーは商品固有の値「JANコード」としています。<br />
キーに持ってくるのは重複のないものなら何でも大丈夫ですが、半角・全角・ひらがな・カタカナが全部揃えないといけないので日本語の名称などは使いにくいと思います。</p>
<p>こちらもそのままセルの値を取得するか、もしくはセルを配列に格納してから参照、書き込みをするかの2パターン用意しています。</p>
<div class="VBACode">Sub 配列なしのDictionaryオブジェクトを用いたコード()<br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’配列を用いない</span></span><br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dictionaryの利用準備</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’<span class="VBA_Comment">’参照設定をする場合</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dim dictJANYJ As Dictionary</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Set dictJANYJ = New Dictionary</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’参照設定をしない場合</span></span><br />
<span class="VBA_Tab1">Dim dictJANYJ As Object</span><br />
<span class="VBA_Tab1">Set dictJANYJ = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’JANをKey、YJをItemに格納</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルの値を直接Dictionaryに格納する場合、Textプロパティを指定しないとうまくいかない</span></span><br />
<span class="VBA_Tab1">For l = 2 To マスタ下端行</span><br />
<span class="VBA_Tab2">If dictJANYJ.Exists(Sheets(&#8220;商品マスタ&#8221;).Cells(l, 1).Text) = False Then <span class="VBA_Comment">’キーは重複できないので、存在有無を確認してから追加</span></span><br />
<span class="VBA_Tab3">dictJANYJ.Add Key:=Sheets(&#8220;商品マスタ&#8221;).Cells(l, 1).Text, Item:=Sheets(&#8220;商品マスタ&#8221;).Cells(l, 2).Text</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next l</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’dictJANYJに格納した値を参照してセルに記載</span></span><br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<span class="VBA_Tab1">Sheets(&#8220;伝票&#8221;).Cells(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">Sheets(&#8220;伝票&#8221;).Cells(i, 9) = dictJANYJ.Item(Sheets(&#8220;伝票&#8221;).Cells(i, 4).Text)</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<div class="VBACode">Sub 配列ありでDictionaryオブジェクトを用いたコード2()<br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;開始&#8221; &#038; &#8220;_&#8221; &#038; Time  <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dictionaryの利用準備</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’<span class="VBA_Comment">’参照設定をする場合</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Dim dictJANYJ As Dictionary</span></span><br />
<span class="VBA_Tab1"><span class="VBA_Comment">’Set dictJANYJ = New Dictionary</span></span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’参照設定をしない場合</span></span><br />
<span class="VBA_Tab1">Dim dictJANYJ As Object</span><br />
<span class="VBA_Tab1">Set dictJANYJ = CreateObject(&#8220;Scripting.Dictionary&#8221;)</span><br />
<br />
<span class="VBA_Tab1">Dim l</span><br />
<span class="VBA_Tab1">Dim マスタ下端行</span><br />
<span class="VBA_Tab1">マスタ下端行 = Sheets(&#8220;商品マスタ&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<span class="VBA_Tab1">Dim 参照範囲</span><br />
<span class="VBA_Tab1">参照範囲 = Range(Sheets(&#8220;商品マスタ&#8221;).Cells(1, 1), Sheets(&#8220;商品マスタ&#8221;).Cells(マスタ下端行, 4))</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’JANをKey、YJをItemに格納</span></span><br />
<span class="VBA_Tab1">For l = 2 To マスタ下端行</span><br />
<span class="VBA_Tab2">If dictJANYJ.Exists(参照範囲(l, 1)) = False Then <span class="VBA_Comment">’キーは重複できないので、存在有無を確認してから追加</span></span><br />
<span class="VBA_Tab3">dictJANYJ.Add Key:=参照範囲(l, 1), Item:=参照範囲(l, 2)</span><br />
<span class="VBA_Tab2">End If</span><br />
<span class="VBA_Tab1">Next l</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’dictJANYJに格納した値を参照してセルに記載</span></span><br />
<span class="VBA_Tab1">Dim 伝票下端行</span><br />
<span class="VBA_Tab1">伝票下端行 = Sheets(&#8220;伝票&#8221;).Cells(Rows.Count, 1).End(xlUp).Row</span><br />
<br />
<span class="VBA_Tab1"><span class="VBA_Comment">’セルに直接書き込むよりスピードアップのために配列に格納する</span></span><br />
<span class="VBA_Tab1">Dim 伝票範囲</span><br />
<span class="VBA_Tab1">伝票範囲 = Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9))</span><br />
<br />
<span class="VBA_Tab1">Dim i</span><br />
<span class="VBA_Tab1">伝票範囲(1, 9) = &#8220;YJコード&#8221;</span><br />
<span class="VBA_Tab1">For i = 2 To 伝票下端行</span><br />
<span class="VBA_Tab2">伝票範囲(i, 9) = dictJANYJ.Item(伝票範囲(i, 4))</span><br />
<span class="VBA_Tab1">Next i</span><br />
<br />
<span class="VBA_Tab1">Range(Sheets(&#8220;伝票&#8221;).Cells(1, 1), Sheets(&#8220;伝票&#8221;).Cells(伝票下端行, 9)) = 伝票範囲</span><br />
<br />
<span class="VBA_Tab1">Debug.Print &#8220;終了&#8221; &#038; &#8220;_&#8221; &#038; Time <span class="VBA_Comment">’時間の測定</span></span><br />
<br />
End Sub</div>
<h2><span id="toc6">処理速度の差について集計</span></h2>
<p>①WorkSheetFunctionでVlookUp関数を呼び出して使用する方法、②今回の連想配列（Dictionaryオブジェクト）を使用する方法を比較してみました。</p>
<p>それぞれの方法において、VBA高速化で有名なのセル値を配列に格納して抽出・書き込む方法も試しています。</p>
<p>商品マスタシート・伝票シートいずれも行数は100,000行にして測定した速度結果は下記の通りです。</p>
<table border=1>
<tr>
<td align=center></td>
<th align=center>WorkSheetFunctionの<br />
VlookUp</th>
<th align=center>Dictionary<br />
オブジェクト</th>
</tr>
<tr>
<td align=center>セルの値を配列へ<br />
格納しない</td>
<td align=center>0:11:13</td>
<td align=center>0:00:32</td>
</tr>
<tr>
<td align=center>セルの値を配列へ<br />
格納する</td>
<td align=center>2:05:08</td>
<td align=center>0:00:04</td>
</tr>
</table>
<p>WorkSheetFunctionでVlookUp関数を用いた場合、配列をセルに格納することで逆に速度が著しく低下してしまったのは驚きでした。</p>
<p>原因としては私が実験した環境において、「セルの値を配列に格納することでメモリが上限に達してしまったこと」が推測されますが、詳細は不明です。</p>
<p>セルを配列に格納した際の速度アップはセルの参照・セルへの値の書き込みの部分が高速化されることによるのですが、WrorkSheetFunctionのVlookUp関数はそのものの処理が重く、セルの値の参照やセルへの書き込みが律速ではないようです。</p>
<p>そしていずれにしても①セルの値を配列に格納して②Dictionaryを使用するという両方のステップを行えば最も高速に処理が可能になることがわかりましたので、今後はこの方法を使っていこうと思います。<br />
11分かかる処理が4秒になるのはものすごい改善だと思います。</p>
<p>今回はVlookUpの代わりになるDictionaryオブジェクトを使用する方法をご紹介しました。<br />
最初のKeyとItemの値を格納する方法を変更すればSumIfsなどの代わりとしても使えるようになりますのでその方法については別の記事で紹介します。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/dictionary-vlookup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>連想配列（Dictionaryオブジェクト）を用いたVBAの高速化</title>
		<link>https://officevba.info/dictionary-top/</link>
					<comments>https://officevba.info/dictionary-top/#respond</comments>
		
		<dc:creator><![CDATA[okumasahito]]></dc:creator>
		<pubDate>Tue, 03 Nov 2020 04:54:23 +0000</pubDate>
				<category><![CDATA[連想配列]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[高速化]]></category>
		<guid isPermaLink="false">https://officevba.info/?p=2183</guid>

					<description><![CDATA[目次 行数の多いExcelシート操作の高速化テクニック連想配列とは連想配列を使用する方法について参照設定をする方法参照設定をしない方法 行数の多いExcelシート操作の高速化テクニック 最近仕事で20数万行の商品の仕入伝 [&#8230;]]]></description>
										<content:encoded><![CDATA[
  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-20" checked><label class="toc-title" for="toc-checkbox-20">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">行数の多いExcelシート操作の高速化テクニック</a></li><li><a href="#toc2" tabindex="0">連想配列とは</a></li><li><a href="#toc3" tabindex="0">連想配列を使用する方法について</a><ol><li><a href="#toc4" tabindex="0">参照設定をする方法</a></li><li><a href="#toc5" tabindex="0">参照設定をしない方法</a></li></ol></li></ol>
    </div>
  </div>

<h2><span id="toc1">行数の多いExcelシート操作の高速化テクニック</span></h2>
<p>最近仕事で20数万行の商品の仕入伝票一覧に、4万行くらいの価格一覧表を当てはめて、平均値・最大値を算出したり、10万行の商品マスタから単価・売価などの情報を呼び出すという業務がありました。</p>
<p>その際、VBAの中でWorkSheetFunctionのVlookupを使ったり、ワークシートにVlookupを代入して計算したのですが、とてつもなく時間がかかる（全部の処理で10時間くらいかかったと思います。）ので高速化の手段を探ってみました。</p>
<p>その中で連想配列を使う手法が最も効果が高く、全体の処理が5分もかからずに完了するように高速化ができたので、今回は連想配列を使用した高速化の方法を何度かに分けてご紹介します。</p>
<p>Excelのバージョンが2016からはワークシートのVlookup関数も早くなっていますが、VBAのWorkSheetFumctionで呼び出したVlookupは速度が速くなっていないですし、Sumifs関数やCountifs関数はそもそも対応していなかったりします。<br />
一方、連想配列を使用する方法は様々な用途に利用できて処理速度も高速なため、まだまだこれからも利用価値は高いと思います。</p>
<p>今回は連想配列を使う方法の概要と事前準備についてお伝えします。</p>
<h2><span id="toc2">連想配列とは</span></h2>
<p>連想配列とは「キー」と「要素」が一つのセットになった配列のことです。</p>
<p>通常の配列はインデックスが通常0から始まる数値で割り振られますが、連想配列においては「キー」をインデックスとして「要素」を呼び出すことが可能になります。</p>
<p>連想配列の記載方法は後ほど記載しますが、イメージとしては下記のような違いになります。<br />
普通の配列の場合、配列に格納された値はインデックスの番号で呼び出すことが可能です。</p>
<div class="VBACode">Dim 食べ物<br />
食べ物=Array(ハンバーグ,チョコレート,コーヒー)<br />
Debug.Print 食べ物(0)<span class="VBA_Comment">’ハンバーグ</span><br />
Debug.Print 食べ物(1)<span class="VBA_Comment">’チョコレート</span><br />
Debug.Print 食べ物(2)<span class="VBA_Comment">’コーヒー</span>
</div>
<p>連想配列はキーを使用してItemの値を取得することができます。</p>
<div class="VBACode">Dim 食べ物<br />
Set  食べ物 = CreateObject(“Scripting.Dictionary”)<br />
食べ物.Add Key:=”メイン”, Item:=”ハンバーグ”<br />
食べ物.Add Key:=”デザート”, Item:=”チョコレート”<br />
食べ物.Add Key:=”飲み物”, Item:=”コーヒー”<br />
Debug.Print 食べ物.Item(”メイン”)<span class="VBA_Comment">’ハンバーグ</span><br />
Debug.Print 食べ物.Item(”デザート”)<span class="VBA_Comment">’チョコレート</span><br />
Debug.Print 食べ物.Item(”飲み物”)<span class="VBA_Comment">’コーヒー</span>
</div>
<p>連想配列に格納するにはキーと要素を両方記述しないといけないため、通常は繰り返しの処理を用いてセルの値などを取得します。</p>
<p>格納した連想配列から値をキーで呼び出すという手順はVlookup関数の使い方と似ているため、Vlookup関数を使ったことがあってVBAを使える人なら比較的なじみやすい気がします。</p>
<h2><span id="toc3">連想配列を使用する方法について</span></h2>
<p>VBAで連想配列を作るためにはDictionaryオブジェクトというものを使用します。<br />
Dictionaryオブジェクトを使用するためには参照設定をする方法としない方法があります。</p>
<h3><span id="toc4">参照設定をする方法</span></h3>
<p>参照設定をする方法ではVBAのウインドウでメニューの中の「ツール」→「参照設定」を選択し、「Microsoft Scripting Runtime」にチェックをつけてOKをクリックします。</p>
<p><a href="https://officevba.info/wp-content/uploads/2020/11/eba487345f79ecf628d4923e47f1a9be.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/11/eba487345f79ecf628d4923e47f1a9be.jpg" alt="ツール参照設定" width="268" height="207" class="alignnone size-full wp-image-2185" /></a></p>
<p><a href="https://officevba.info/wp-content/uploads/2020/11/78da9b7857e634c394682de1cc1b249d.jpg"><img loading="lazy" decoding="async" src="https://officevba.info/wp-content/uploads/2020/11/78da9b7857e634c394682de1cc1b249d-300x228.jpg" alt="参照設定" width="300" height="228" class="alignnone size-medium wp-image-2186" srcset="https://officevba.info/wp-content/uploads/2020/11/78da9b7857e634c394682de1cc1b249d-300x228.jpg 300w, https://officevba.info/wp-content/uploads/2020/11/78da9b7857e634c394682de1cc1b249d.jpg 522w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>連想配列を宣言して使用開始するには下記のように宣言します。</p>
<div class="VBACode">Dim 〇〇 As Dictionary<br />
Set 〇〇 = New Dictionary
</div>
<p>配列名は何でもいいのですが、一般的にDictionaryオブジェクトをわかりやすいように「Dic〇〇」や「dic〇〇」とすることが多いようです。</p>
<h3><span id="toc5">参照設定をしない方法</span></h3>
<p>参照設定をしない場合、CreateObjectで「Scripting.Dictionary」を指定します。</p>
<div class="VBACode">Dim 〇〇 （As Object）<br />
Set 〇〇 = CreateObject(“Scripting.Dictionary”)
</div>
<p>参照設定せずに使用できますが、ヒントとか予測のプロパティが表示されないため、慣れていない人は参照設定をしておく方が無難かもしれません。</p>
<p>私は慣れていないですが、ヒントを参考にすることが少ないのでこちらの方法をよく使っています。<br />
（Scripting.Dictionaryではないと思いますが、参照設定がバージョンで異なっていて動かないトラブルも避けられますし。）</p>
<p>下記のリンクから実際に使う手順についてご紹介します。</p>
<li><a href="https://officevba.info/dictionary-vlookup/" target="_blank">①Vlookup関数の代用で連想配列を使って高速化する</a></li>
<li><a href="https://officevba.info/dictionary-sumifs/" target="_blank">②SumIf関数(SumIfs関数)の代用で連想配列を使って高速化する</a></li>
<li><a href="https://officevba.info/dictionary-keys/" target="_blank">③キーの一覧を取得する方法と高速化テクニック</a></li>
<li>④Itemに複数の種類を格納する方法について</li>
]]></content:encoded>
					
					<wfw:commentRss>https://officevba.info/dictionary-top/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
