派遣事務員の迷走

派遣事務員コロ子。会社の犬。顔出しNG。常に迷走している。

モジュールレベル変数(コロ子勘違いしていた)

こんにちは。
派犬事務員のコロ子です。

前回記事で自動採番のマクロを作成。
データの保持が上手くできず、空いているセルに仮置きする方法で作成した。
何かもっと良い方法はないのかなぁ~と聞いてみたところ、空腹おやじ(id:Z1000S)さんからこんな方法が!! (素晴らしいので必見)

z1000s.hatenablog.com


コードを見て「えっつ!」と。コロ子が空いているセルに仮置きしたデータをモジュールレベル変数に入れている。???と思い実際やってみたところ、コロ子ずっと勘違いしてた事が判明。(注)低レベルです。

モジュールレベル変数

何を勘違いしていたかというと、一つのプロシージャが終了したら「全ての変数が破棄される」と思っていた。

【モジュールレベル変数】
変数をモジュールの最上部で、どこのプロシージャにも属さないで宣言すると、プロシージャ間で共有して使用できる。

Private buf As Long
’****************************************
Sub test1()

End Sub
’****************************************
Sub test2()

End Sub

この場合だと、変数「buf」はプロシージャ「test1」と「test2」で共有して使用できる。

ここまでは、コロ子も認識していた。


【コロ子の勘違い】

Private buf As Long
’****************************************
Sub test1()

buf = 1

End Sub
’****************************************
Sub test2()

Debug.Print buf

End Sub

上記の場合で
①「test1」を実行する
②「test2」を実行する
一つのプロシージャが終了したら「全ての変数が破棄される」と思っていたので、この場合「test2」の「buf」には「初期値の0」が入っていると思っていた。

実際は値は保持されていて
「test2」の「buf」には「1」が入っている。

えーーーー!そうなの?
コロ子ずーっと勘違いしてた。

そうとは知らず、今までかなりムダに苦戦してきた。
これができるとなるとコードの書き方の考え方が変わる。
目から鱗!!

ちなみに、今までどうやってモジュールレベル変数を使っていたかというと

Private buf1 As Long
Private buf2 As Long
’****************************************
Sub test1()

buf1 = 1
buf2 = 2
Call test2

End Sub
’****************************************
Sub test2()

buf1 = 10
Call test3

End Sub
’****************************************
Sub test3()

Debug.Print buf1
Debug.Print buf2

End Sub

引数を渡すのが面倒なとき。
「test2」で「buf2」は必要ないけど、「test2」で呼び出してる「test3」では「buf1」も「buf2」も必要なとき。
引数がたくさんあって、受け渡しがごちゃごちゃのときに使う物だと思っていた。
もしかしたらこの考え方も違うのかも・・・。

今回、空腹おやじさんに教えてもらわなかったら、この先もずーーーっと勘違いしたままだった。本当に、本当に感謝です。
ありがとうございました。

Worksheet_ChangeイベントとWorksheet_SelectionChangeイベント(前回の種目の自動採番コード変更しました)

こんにちは。
派犬事務員のコロ子です。

前回の記事、種目別の自動採番のコードで
空腹おやじ(id:Z1000S)さんより、

この処理だと、登録済みのA列のセルで、B列の値が最大値ではない行でF2を押して編集状態にして、そのまま何も変えないでEnter押しちゃうと、B列の値が更新されちゃいますが、大丈夫ですか?(F2、Escだと大丈夫ですが)

とご指摘頂きました。
おおっつ!ヤバイ!大丈夫じゃない!
そこまで考えが至らなかった。

下記の図のように、もうすでに値が入っているA列を編集状態にする。

f:id:SNegishi:20190814105224p:plain

    ↓

値を変更しないで確定すると最大値+1を採番してしまう。
f:id:SNegishi:20190814105258p:plain

これは良くない!!重大なバグ!
実はコロ子の会社で作ったマクロはB列でダブルクリックすると採番するよういしていて、ブログ用にちょっとアレンジしたら、この有様。
(会社で問題なく使えてるから安心して!)

更新したくないのであれば、SelectionChangeイベントで、変更前の値を保持しておいて、Changeイベントで変更されているか確認する等、なんらかの処理が必要になるかも。by空腹おやじ(id:Z1000S)さん

とアドバイスいただいたので、早速作り直さなければ!


前回書いたダメなコード(コピー不可)

Private Sub Worksheet_Change(ByVal Target As Range)

Dim buf As Range

Application.ScreenUpdating = False

'1列目(A列)に変更があった時
If Target.Column = 1 Then
     
  '初期化
    Range("B" & Target.Row).Value = ""

    'A列でフィルタ
    Range("A1").AutoFilter Field:=1, Criteria1:=Range("A" & Target.Row).Value
     
    'アクティブセル領域の可視範囲を取得
    Set buf = Range("A1").CurrentRegion.Columns(2).SpecialCells(xlCellTypeVisible)
     
    '最大値+1を取得
    Range("B" & Target.Row).Value = Application.WorksheetFunction.Max(buf) + 1
     
    'フィルター条件解除
    ActiveSheet.ShowAllData
       
End If

Application.ScreenUpdating = True

End Sub

*前回記事のコードは修正しました。


何がダメかというと、 Worksheet_Changeイベントについて分かっていない。

Worksheet_Changeイベント

イベントの名前が「Change」なのでセルに値や数式を入力したり、入力されているデータを変更した時に発生するイベントだと思っていた。

Changeイベントは、名前に騙されやすいので要注意です。
by空腹おやじ(id:Z1000S)さん

ところが入力されているデータを変更しなくても「編集状態」にして「確定」すると変更したとみなされ、イベントが発生してしまう。
知らなかった~。これ危険だわー。
*「編集状態」にしてもescキーでキャンセルすればイベントは発生しない。
また、セルに色を付けたり、書式などの変更でもイベントは発生しない。

Worksheet_SelectionChangeイベント

イベントの名前の通り、選択セルが変更されたらイベントが発生する。
引数Targetに変更後のセル(カーソルがあるセル)のRangeオブジェクトが取得できる。

考え方

ChangeイベントとSelectionChangeイベントのダブル使い。
①A列のセルにカーソルが移動したら、Worksheet_SelectionChangeイベントでカーソルのあるセルの値と、の隣のB列の値を空いてるセルに仮置きする。
A列の値をAA列、B列の値をBB列に入力。
②そのセルでChangeイベントが発生したら、採番し隣のB列に値を入れる。
③もし、Changeイベント発生時のA列の値と、保存してあるAA列の値が同じ場合は、保存しているBB列の値をB列に入れる。


【コード】Worksheet_SelectionChangeイベント

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

If Target.Column = 1 And Target.Row >= 2 Then

    'AA列、BB列に仮置き
    Range("AA:BB").Clear
    Range("BB" & Target.Row).Value = Range("B" & Target.Row).Value
    Range("AA" & Target.Row).Value = Range("A" & Target.Row).Value

End If

End Sub


【コード】Worksheet_Changeイベント

Private Sub Worksheet_Change(ByVal Target As Range)

Dim buf As Range

Application.ScreenUpdating = False
Application.EnableEvents = False 'イベント禁止

If Target.Column = 1 Then

    'オートフィルタ
    Range("A1").AutoFilter Field:=1, Criteria1:=Range("A" & Target.Row).Value
     
    'アクティブセル領域の可視範囲を取得
    Set buf = Range("A1").CurrentRegion.Columns(2).SpecialCells(xlCellTypeVisible)
     
    '最大値+1を取得
    Range("B" & Target.Row).Value = Application.WorksheetFunction.Max(buf) + 1
     
    'フィルター条件解除
    ActiveSheet.ShowAllData
    
    'A列に変更がない場合は元の値に戻す
    If Range("A" & Target.Row).Value = Range("AA" & Target.Row).Value Then
    
        Range("B" & Target.Row).Value = Range("BB" & Target.Row).Value
    
    End If
    
    'AA列、BB列に仮置き
    Range("AA:BB").Clear
    Range("BB" & Target.Row).Value = Range("B" & Target.Row).Value
    Range("AA" & Target.Row).Value = Range("A" & Target.Row).Value
       
End If

Application.ScreenUpdating = True
Application.EnableEvents = True

End Sub

「Worksheet_SelectionChangeイベントで変更前の値を保持しておいく」のところを静的変数Staticを使おうとしたけど、モジュール変数として使う事がでなかった。
「プローシシャーの外では使えません」のエラーメッセージが出た。
結局上手くできず、いつもの仮置き方式。
仮置き方式でないなら、どのように作るものなのでしょうか・・・?

今回は空腹おやじ(id:Z1000S)さんのアイディアをそのまま使わせて頂きました。
どうもありがとうございました!

Excel VBA 種目別に自動採番(複数の種類で連番を取る)

こんにちは。
派犬事務員のコロ子です。

種目別自動採番マクロ

コロ子の会社では毎日、品物が増えるたびに連続の番号を取る作業ある。

A列に複数の種類が入っていて、種類ごとに連番を取る。

【例】下記表で新しく種目CCCが追加になる場合。

f:id:SNegishi:20190811225617p:plain

B列に66を入力する。

f:id:SNegishi:20190811225736p:plain

もし
AAAなら:103
BBBなら:22
DDDなら:33

こんな具合に採番をする。手作業なら多分、A列の項目でフィルタをして、B列の最大値に1を足した数値を入力する。
途中で種目が変更になったり、欠番が出たり、B列の最大値が一番下に来ているとは限らない場合があるので地味に面倒な作業だったりする。

こんな作業を毎日やっているならば、マクロを作成しよう。

コピペでできるよ!

作り方

【① A列に入力規則を作成する。】
(プルダウンでデータを選択できるようにする)

A列を選択した状態で、「データ」タブ→「データの入力規則」→「データの入力規則」を選択
f:id:SNegishi:20190811231447p:plain


入力の種類:「リスト」を選択
元の値:種目を半角カンマ(,)で区切って入力

f:id:SNegishi:20190811231929p:plain



【② コードをシートモジュールに書く】
対象のシート(ここではSheet1)のシートモジュールを選択する。
オブジェクトボックスで「WorkSheet」、プロシージャーボックスで「Change」を選択する。

f:id:SNegishi:20190811232923p:plain


コードウィンドウに下記コードを書く
*Private Sub Worksheet_Change(ByVal Target As Range)はプロシージャーボックスで「Change」を選ぶと自動に表示される。
セルの中身を変更したらこのプロシージャーが呼び出される。

【コード】Worksheet_Changeイベント

Private Sub Worksheet_Change(ByVal Target As Range)

Dim buf As Range

Application.ScreenUpdating = False
Application.EnableEvents = False 'イベント禁止

If Target.Column = 1 Then

    'オートフィルタ
    Range("A1").AutoFilter Field:=1, Criteria1:=Range("A" & Target.Row).Value
     
    'アクティブセル領域の可視範囲を取得
    Set buf = Range("A1").CurrentRegion.Columns(2).SpecialCells(xlCellTypeVisible)
     
    '最大値+1を取得
    Range("B" & Target.Row).Value = Application.WorksheetFunction.Max(buf) + 1
     
    'フィルター条件解除
    ActiveSheet.ShowAllData
    
    'A列に変更がない場合は元の値に戻す
    If Range("A" & Target.Row).Value = Range("AA" & Target.Row).Value Then
    
        Range("B" & Target.Row).Value = Range("BB" & Target.Row).Value
    
    End If
    
    'AA列、BB列に仮置き
    Range("AA:BB").Clear
    Range("BB" & Target.Row).Value = Range("B" & Target.Row).Value
    Range("AA" & Target.Row).Value = Range("A" & Target.Row).Value
       
End If

Application.ScreenUpdating = True
Application.EnableEvents = True

End Sub


同様にプロシージャーボックスで「SelectionChange」を選択し、下記のコードを書く。
(登録済みのA列を編集状態にして、変更しないでEnter押した場合、値が変更されるのを防ぐため)
【コード】Worksheet_SelectionChangeイベント

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

If Target.Column = 1 And Target.Row >= 2 Then

    'AA列、BB列に仮置き
    Range("AA:BB").Clear
    Range("BB" & Target.Row).Value = Range("B" & Target.Row).Value
    Range("AA" & Target.Row).Value = Range("A" & Target.Row).Value

End If

End Sub

空腹おやじ(id:Z1000S)さんよりご指摘頂いて修正しました。
詳しくは次の記事で。


完成!

例えば、A列に日付、B列に種目、C列に番号の表の場合、コードの

”A”→”B”
”B”→”C”
”A1”→”B1”
4行目のTarget.Column = 1 → Target.Column = 2(数字は列の番号)

に変更すればOK!
こんな作業のある人は自分の表に合わせて作ってみよう!


このマクロ、VBAを始めたての頃に「マクロの自動記録」で作成した。
もちろんそのままでは使えないから、上手くいかないところを無理やり手で直して、何がなんだか分からないけど、とりあえず希望の結果になるからOKという作り方だった。
それから毎日このマクロを使っていて、特に問題ないからずっとそのままにしてたけど、最近見直したら、あまりにもヒドイ驚きのコードだったので作り直すことにした。マクロの自動記録をベースにしてるからコードがやたらと長くて意味不明の個所がいっぱいあった。

そんなデタラメでも、出来た時は感動したし、すごく便利で重宝しているから、下手でも力ずくでも作って良かっな、って思う。そして、その時の感動が「VBAでもっといろいろできるようになりたい!」という気持ちの源だったんだな、と実感。

マンゴー杯!

こんにちは。
派犬事務員のコロ子です。

マンゴー杯 結果報告

前回、あみだくじや抽選のコードを「自分ならこう書く!」を募集したところ、素晴らしいアイディアの応募がありました!どうもありがとうございます!!
みんな演出が凝っていて高度な技が満載!

エントリーNo.1

セラエノ (id:celaeno42)さん
celaeno42.hatenablog.com

エントリーNo.2

id:Infomentさん
infoment.hatenablog.com

さらに進化版。あみだくじの格子部分もマクロで作る↓
infoment.hatenablog.com

infoment.hatenablog.com

すごい!!!
あみだくじの完成形。必見↓

infoment.hatenablog.com

あみだくじファイナル↓
infoment.hatenablog.com


エントリーNo.3

Kou (id:ExcelLover)さん
www.excellovers.com

エントリーNo.4

コロ子
ちょっとみんな凄すぎて実力の差がありすぎる・・・。
こうなったらコロ子が絶対当たるマクロを作ろう。

【コロ子が絶対当たる抽選】
マンゴーの数を入力して「抽選」ボタンを押すと、「当たり」「はずれ」が表示される。コロ子は絶対「当たり」が出る。
f:id:SNegishi:20190804231401p:plain


【考え方】
①「当たり・はずれの要素」「乱数を入れる要素」を加者数-1(コロ子を抜かす)の要素数の2次元配列を用意する。
②「当たり・はずれの要素」の先頭から、マンゴーの数-1個の当たり、後ははずれを入れる。
③「乱数を入れる要素」に整数1~参加者数を重複しないでランダムに入れる。

例:マンゴーの数3個、コロ子以外の参加者7人の場合の配列のイメージ

f:id:SNegishi:20190804215251p:plain
配列のイメージ

④「乱数を入れる要素」で昇順にソートする。

⑤結果セルに「乱数を入れる要素」を順に入れていく。
このとき参加者が「コロ子」だったら当たりを入れスキップし、次のセルへ。

【コード】
抽選ボタン

Sub Absolutely_win()

Dim i As Long, j As Long
Dim StartCell As Long
Dim Cellcount As Long
Dim Arr() As Variant

Application.ScreenUpdating = False

StartCell = 5
Cellcount = Cells(Rows.Count, 1).End(xlUp).Row - StartCell

ReDim Arr(1 To Cellcount, 1)

For i = 1 To Cellcount

    If i < Range("B2").Value Then
        Arr(i, 0) = "当たり"
    Else
        Arr(i, 0) = "はずれ"
    End If

    Dim Flg() As Boolean
    ReDim Flg(1 To Cellcount)
    Dim Num As Long
    
    '整数1~参加者数を重複しないでランダムに発生させる
    Randomize
    Do
        Num = Int((Cellcount - 1 + 1) * Rnd + 1)
    Loop Until Flg(Num) = False
    Arr(i, 1) = Num
    Flg(Num) = True
    
Next i

'ソート
Call ArrSort(Arr)

'セルに配列を入れる(コロ子の時は当たりを入れる)
For i = 1 To Cellcount

    Cells(StartCell, 2).Value = Arr(i, 0)
    
    If Cells(StartCell, 1).Value = "コロ子" Then  
        Cells(StartCell, 2).Value = "当たり"
        i = i - 1
    End If

    StartCell = StartCell + 1
    
Next i

Application.ScreenUpdating = True

End Sub

※範囲を指定して乱数を発生させる
乱数=Int((最大値 - 最小値 +1 ) * Rnd + 最小値)


配列をソートする

Sub ArrSort(Arr As Variant)

Dim bufL As Long
Dim bufS As String
Dim i As Double
Dim j As Double

For i = UBound(Arr, 1) To LBound(Arr, 1) Step -1

    For j = LBound(Arr, 1) To i - 1
    
        If Arr(j, 1) > Arr(j + 1, 1) Then
        
                bufS = Arr(j, 0)
                bufL = Arr(j, 1)
                
                Arr(j, 0) = Arr(j + 1, 0)
                Arr(j, 1) = Arr(j + 1, 1)
                
                Arr(j + 1, 0) = bufS
                Arr(j + 1, 1) = bufL
                                
        End If
        
    Next j
    
Next i

やったー!コロ子マンゴー当たったー!!
セコイ!!
こーゆー不正はダメですよ!



【今回のポイント】
整数1~参加者数を重複しないでランダムに発生させる

For i = 1 To Cellcount
・
・
・
    Dim Flg() As Boolean
    ReDim Flg(1 To Cellcount)
    Dim Num As Long

    Randomize
    Do
        Num = Int((Cellcount - 1 + 1) * Rnd + 1)
    Loop Until Flg(Num) = False

    Arr(i, 1) = Num
    Flg(Num) = True

Next i

Boolean型の初期値はFalse

範囲を指定して乱数を発生させてみたら、意外と多く重複していた。
重複しないように作ろうとしたところ、なかなか上手かず、ネットで調べたら
Boolean型の配列を使うというのがあった。
最初Do~Loopのところだけを見て?って思ったけど、その下の
Arr(i, 1) = Num
Flg(Num) = True
を見て納得!Boolean型の初期値はFalse。Numを配列に格納したらFlgの要素数Num番目をTrueにすれば、ループでフラグの要素数Num番目がTrueだったら重複しているので、重複しなくなるまで乱数を発生させる。
こんなフラグの使い方あるんだ!

参考サイト
www.moug.net

マクロを作ると本質が見える?

こんにちは。
派犬事務員のコロ子です。

前回からExcel VBAであみだくじを作成中。
【前回の記事】
派遣事務 特殊スキルは評価されにくい - 派遣事務員の迷走
VBAで「あみだくじ」を作ろう。 - 派遣事務員の迷走
使えないマクロにならないために - 派遣事務員の迷走


前回、あみだくじの結果が一瞬で分かるコードを書いていて思ったんだけど・・・。
ここまで来ると、そもそも「あみだくじ」の必要があるのかどうか。

「じゃんけん」でも、「あたり・はずれを書いた紙を箱に入れてくじ引きする」、でも何でも良かったと思う。

30人中、17人にマンゴーが当たる。

これを満たせば何でも良かった。

ただ、「じゃんけん」だとなかなか勝負がつかないし、「くじ引き」だと、くじを引く順番で公平さが損なわれる。
だから手っ取り早く「あみだくじ」にしただけであって、何でも良かった。

と言うことは、こんな感じで良くない?

f:id:SNegishi:20190727160634p:plain

マンゴーの数を入力して
「抽選ボタン」を押す。
  ↓
f:id:SNegishi:20190727161540p:plain

マンゴーの数だけ当たりがランダムにでる。
乱数を使えば公平だよね?


【考え方】
マンゴーの数3個でやってみる。
①まず、B列に上からマンゴーの数だけ「当たり」を入れ、それ以降には「はずれ」を入れる。
②乱数を発生させC列に仮置きする。

f:id:SNegishi:20190727164347p:plain


B列とC列を対象にC列で昇順にソートする。

f:id:SNegishi:20190727164449p:plain

仮置きしていたC列を削除すれば、ランダムに当たりが出た事になる。

コード

Sub Lottery()

Dim i As Long
Dim StartCell As Long
Dim EndCell As Long

Application.ScreenUpdating = False

StartCell = 5
EndCell = Cells(Rows.Count, 1).End(xlUp).Row

For i = StartCell To EndCell

    If i - StartCell < Range("B2").Value Then
        Cells(i, "B").Value = "当たり"
    Else
        Cells(i, "B").Value = "はずれ"
    End If
    
    '乱数を発生させ、C列に仮置き
    Randomize
    Cells(i, "C").Value = Rnd

Next i

'乱数入れたC列でソートする
Range("B" & StartCell & ":C" & EndCell).Sort Key1:=Range("C4"), Order1:=xlAscending

'C列をクリア
Range("C" & StartCell & ":C" & EndCell).Clear

Application.ScreenUpdating = True

End Sub

感想

今回VBAであみだくじを作っていたら、「そもそもあみだくじにする意味あるのかな?公平なら何でも良くない?」と疑問が沸いた。
業務でも、こういうのいっぱいあると思う。
そもそも、何の為にそれをやるのか。
今までは手作業だったからそのやり方が最善だったけど、本当は何がしたいのか。
マクロを作っていると、本当はどうなんだろう?って思ってくる。

例えば、マンゴーを貰える人を選出する業務があるとして(何のこっちゃ)、毎日あみだくじをやっていたとする。
派犬のコロ子が「あみだくじの意味はありますか?抽選で良くないですか?」なんて言ったところで、「業務規定ではあみだくじで選出することになっています。なので簡単にあみだくじができるツールを作成してください。」と言われてしまう。
働き方改革、業務改善、本当に見直さなければいけないところはどこなんだろう・・・?
社内規定、業務規定の前にして派犬の無力さ(限界)を感じる・・・

お知らせ

今回の抽選のマクロ、コロ子は間借り方式で作ったけど、こういうのの考え方は人それぞれ。
「自分ならこうする!」と言うのがあれば、ぜひコメントください!
ブログ、Twitter@2a9SouLnhptnIqGで随時募集してます。
もちろん「あみだくじ」の書き方も募集してます!
自分が絶対当たるようにしちゃうとか(不正)もありです。

セラエノ (id:celaeno42)さんのあみだくじ。説明が分かりやすい!
動くのが見えるから楽しい!
celaeno42.hatenablog.com

使えないマクロにならないために

こんにちは。
派犬事務員のコロ子です。

前回からExcel VBAであみだくじを作成中。

【前回の記事】
派遣事務 特殊スキルは評価されにくい - 派遣事務員の迷走
VBAで「あみだくじ」を作ろう。 - 派遣事務員の迷走

紙に書いたあみだくじは、「あみだくじ~」って歌いながらやるから楽しいけど、マクロで動かすと、とたんにつまらなくなる。
紙のあみだくじより速いし手間もかからないのに、人数分の回数(30回)スタートボタンとクリアボタン交互に押すのは超面倒と感じるようになる。

おそらく、業務でも同じような事は多々あると思う。
手でやるより早いのに「何か面倒、使えないマクロ」とか言わる事はないだろうか・・・?

そこで、使えない、と思われないためにこんなのはどうだろう?

スタートボタンを押す
    ↓
結果が表示される

f:id:SNegishi:20190727121857p:plain

どうせつまらないなら早く結果を知りたいよね?それが人(犬)。

30人用のあみだくじだと、30行以上ないと公平性が保てない気がするので、何人でも、何行でもOKのコードに変更してみた。縦線・横線を自由に増やしてOK。

コード

スタートボタン
結果をあみだくじ最終行の下に書き込みながら人数分ループする。

Dim result As String
Dim LastRow As Long

Sub Start()

Dim c As Long
Dim ran As Range

Application.ScreenUpdating = False

Call Reset  '結果のクリア&最終行取得

For c = 1 To Cells(3, Columns.Count).End(xlToLeft).Column

    Set ran = Cells(3, c).Offset(1, 0)
    Call Rigth_Cell(ran)
    
    Cells(LastRow + 2, c).Value = Cells(3, c).Value
    Cells(LastRow + 3, c).Value = result
    
Next c

Set ran = Nothing

Call undo

Application.ScreenUpdating = True

End Sub


セルの右側をたどる
最後まで行ったら結果を取得

Sub Rigth_Cell(ran As Range)

While ran.Row < LastRow

    '①-1 左下に線がある場合
    If ran.Borders(xlEdgeBottom).LineStyle = xlContinuous Then
    
        ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0)
        ran.Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        Call Left_Cell(ran)
        
    '①-2 右下に線がある場合
    ElseIf ran.Offset(0, 1).Borders(xlEdgeBottom).LineStyle = xlContinuous Then
    
        ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0)
        ran.Offset(0, 1).Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 1)
        
        
    '①-3 下線がない場合
    Else

        ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        
    End If
 
 '結果を取得する(★ここを追加した)
    If ran.Row = LastRow - 1 Then
        result = ran.Offset(1, 0).Value
    End If

Wend

End Sub


セルの左側をたどる
最後まで行ったら結果を取得

Sub Left_Cell(ran As Range)
'セルの左側をたどる

While ran.Row < LastRow

    '②-1 右下に線がある場合
    If ran.Borders(xlEdgeBottom).LineStyle = xlContinuous Then
    
        ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0)
        ran.Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        Call Rigth_Cell(ran)
    
    '②-2 左下に線がある場合
    ElseIf ran.Offset(0, -1).Borders(xlEdgeBottom).LineStyle = xlContinuous Then
        
        ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0)
        ran.Offset(0, -1).Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, -1)
        
    '②-3 下線がない場合
    Else
    
        ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        
    End If
    
    '結果を取得する(★ここを追加した)
    If ran.Row = LastRow - 1 Then
        result = ran.Offset(1, -1).Value
    End If

Wend

End Sub


クリアボタン(結果を削除する)
クリアボタンを押したときと、マクロの最初に呼び出して、クリア&最終行取得

Sub Reset()

LastRow = Cells(Rows.Count, 1).End(xlUp).Row

With Cells(LastRow - 1, 1)

    If .Borders(xlEdgeLeft).LineStyle <> xlContinuous And _
       .Borders(xlEdgeRight).LineStyle <> xlContinuous Then
       
       Range(LastRow - 1 & ":" & LastRow).Delete
       
       LastRow = LastRow - 3
       
    End If

End With

End Sub


赤線を黒に戻す(前回のまま)
前回はクリアボタンに充てていたけど、今回はマクロの最後に1回呼び出せばOK。

Sub undo()

Dim ran As Range

For Each ran In ActiveSheet.UsedRange '.CurrentRegion

    If ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0) Then
        ran.Borders(xlEdgeRight).Color = RGB(0, 0, 0)
    End If
    
    If ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0) Then
        ran.Borders(xlEdgeLeft).Color = RGB(0, 0, 0)
    End If

    If ran.Borders(xlEdgeBottom).Color = RGB(255, 0, 0) Then
        ran.Borders(xlEdgeBottom).Color = RGB(0, 0, 0)
    End If
    
Next ran
    
End Sub

どんなに大きなあみだくじでもスタートボタンを押すだけで一瞬で結果が分かる。
あみだくじの楽しさはゼロだけど、一瞬だから逆に楽しい。
業務でもこれくらい一瞬なら使った人に「楽しい!」って思われる。
もう使えないマクロとは言わせない!
でも、マクロを作ってる時に思った事があるんだけど・・・。

つづく。

【次の記事】
マクロを作ると本質が見える? - 派遣事務員の迷走

VBAで「あみだくじ」を作ろう。

こんにちは。
派犬事務員のコロ子です。

前回、マンゴー争奪戦を行った「あみだくじ」をExcel VBAで作ってみた。
得意な事は面倒とか大変とか思わないはず!
得意と言えるレベルではないけど、面倒じゃなかった。(ギリギリ)

【前回記事】
派遣事務 特殊スキルは評価されにくい - 派遣事務員の迷走

あみだくじを作ろう

まずは罫線を使ってあみだくじを作る。
縦の罫線を引いて、適当に横の罫線を引く。


f:id:SNegishi:20190726062656p:plain

くじをやりたい人にカーソルを置いて「スタート」ボタンを押す。
          

f:id:SNegishi:20190724193809p:plain


考え方

たどる線の分岐のパターンを分ける。

【①セルの右側の縦線をたどる場合】

①-1 左下に線がある場合
右線と下線を赤色に変更して、下に1つセルを移動する → 左側の縦線をたどる(②へ)
f:id:SNegishi:20190724223019p:plain

①-2 右下に線がある場合
右線と右隣セルの下線を赤色に変更して、右に1つ、下に1つセルを移動する
f:id:SNegishi:20190724222955p:plain

①-3 下線がない場合
右線を赤色に変更して下に1つセルを移動する。
f:id:SNegishi:20190724225532p:plain



【②セルの左側の縦線をたどる場合】

②-1 右下に線がある場合
左線と下線を赤色に変更して、下に1つセルを移動する → 右側の縦線をたどる(①へ)
f:id:SNegishi:20190724225935p:plain

②-2 左下に線がある場合
左線と左隣セルの下線を赤色に変更して、左に1つ、下に1つセルを移動する
f:id:SNegishi:20190724230117p:plain

②-3 下線がない場合
左線を赤色に変更して下に1つセルを移動する。
f:id:SNegishi:20190724230228p:plain

分岐は左右、3パターンずつ。
左右それぞれのプロシージャーを作成する。
たどる線がセルの反対側に移ったときに反対側のプロシージャーを呼び出す。
これをあみだくじの最後の行までループする。

【コード】

スタートボタン
カーソルを置いたセルの1つ下のセルの右側の線からスタート

Const LASTROW = 12

Sub Start()

Dim ran As Range

Set ran = ActiveCell.Offset(1, 0)
Call Rigth_Cell(ran)
Set ran = Nothing

End Sub


セルの右側をたどる

Sub Rigth_Cell(ran As Range)

While ran.Row < LASTROW

    '①-1 左下に線がある場合
    If ran.Borders(xlEdgeBottom).LineStyle = xlContinuous Then
    
        ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0)
        ran.Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        Call Left_Cell(ran) 'だどる線をのセル左側に変更する
        
    '①-2 右下に線がある場合
    ElseIf ran.Offset(0, 1).Borders(xlEdgeBottom).LineStyle = xlContinuous Then
    
        ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0)
        ran.Offset(0, 1).Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 1)

    '①-3 下線がない場合
    Else

        ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        
    End If

Wend

End Sub


セルの左側をたどる

Sub Left_Cell(ran As Range)

While ran.Row < LASTROW

    '②-1 右下に線がある場合
    If ran.Borders(xlEdgeBottom).LineStyle = xlContinuous Then
    
        ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0)
        ran.Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        Call Rigth_Cell(ran)    ’だどる線をのセル右側に変更する
    
    '②-2 左下に線がある場合
    ElseIf ran.Offset(0, -1).Borders(xlEdgeBottom).LineStyle = xlContinuous Then
        
        ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0)
        ran.Offset(0, -1).Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, -1)
        
    '②-3 下線がない場合
    Else
        ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0)
        Set ran = ran.Offset(1, 0)
        
    End If
    
Wend

End Sub


クリアボタン
赤線を黒線に戻す

Sub undo()

Dim ran As Range

For Each ran In ActiveSheet.UsedRange 

    If ran.Borders(xlEdgeRight).Color = RGB(255, 0, 0) Then
        ran.Borders(xlEdgeRight).Color = RGB(0, 0, 0)
    End If

    If ran.Borders(xlEdgeLeft).Color = RGB(255, 0, 0) Then
        ran.Borders(xlEdgeLeft).Color = RGB(0, 0, 0)
    End If

    If ran.Borders(xlEdgeBottom).Color = RGB(255, 0, 0) Then
        ran.Borders(xlEdgeBottom).Color = RGB(0, 0, 0)
    End If
    
Next ran
    
End Sub

できた!

でも、紙に書いたあみだくじは、「あみだくじ~」って歌いながらやるから楽しいけど、これだと何かつまんない。
つまんない上に人数分の回数(30回)スタートボタンとクリアボタン交互に押すの面倒・・・。

つづく

【次の記事】
使えないマクロにならないために - 派遣事務員の迷走