[[20111209105823]] 『Do〜Loop』(ちょこたん) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『Do〜Loop』(ちょこたん)

 今作っているマクロで表の中から明日の日付と一致したら
 一致した全てのデータをメッセージボックスに表示させるというものを作っています。
 ココで質問し、ぶらっと様に教えて頂いたコードを元に色々試してみました。
 そのマクロの一部なんですが、最初のコードだとうまく表示されます。
 しかし2つ目のコードだと1つしか表示されませんでした。
 1つ目でうまくいったので別にいいのですが気になってしまって・・・
 1つ目は「foundcellとfirstのセル番地が異なるまでループ処理をする」ですよね?
 2つ目はもしfoundcellとfirstのセル番地が一緒だったらループを抜ける。
 違うかったら(セル番地が異なったら) ataiにfoundcell.Offset(0, -1).Valueを格納する」
 ではないのですか?
 a=a+1としているからいけそうな気がするんですが駄目でした。
 elseの意味を間違えてる?

 どなたか何故うまくいかないのか教えて下さいませんか?
 コード載せておきます。
 全部必要なら言ってください。

 こちらがうまくいくコード↓
   Do
     Set foundcell = Range("D:D").FindNext(foundcell)
         a = a + 1
         atai(a) = foundcell.Offset(0, -1).Value
    Loop While foundcell.Address <> first.Address
 こちらが1つしか表示されないコード↓   
    Do
     Set foundcell = Range("D:D").FindNext(foundcell)
        If foundcell.Address = first.Address Then
            Exit Do
        Else
         a = a + 1
         atai(a) = foundcell.Offset(0, -1).Value
        End If
    Loop
 (ちょこたん)


 firstにはどこで何が入れられてるの?
 (春日野馨)

 同じくですがfirstにSetされているものが気になります。

 ところで、「うまくいくコード」ですが
 本当にうまくいっていますか?
 1つ目に見つかったものが表示されますか?

 先に1度めのFindで見つかったものをataiに入れていれば良いのですが
 そうじゃなければ1つ目は表示されないはずです。
 先に次の該当セルを検索してしまっていますから。

 普通なら

  Sub test()
  Dim FoundCell As Range
  Dim FirstAddress As String
  Dim atai() As Variant
  Dim i As Long

  Const FindVal As String = "test"

  With ActiveSheet.Columns("D")
    Set FoundCell = Range("D:D").Find(FindVal, .Cells(.Cells.Count), xlValues, xlWhole, xlByRows)
    If Not FoundCell Is Nothing Then
      ReDim atai(1 To Application.WorksheetFunction.CountIf(.Cells, FindVal))
      FirstAddress = FoundCell.Address
      Do
        i = i + 1
        atai(i) = FoundCell.Offset(0, -1).Value
        Set FoundCell = .FindNext(FoundCell)
      Loop While FoundCell.Address <> FirstAddress
      MsgBox Join(atai, vbLf)
    Else
      MsgBox "見つかりません"
    End If
  End With
  End Sub

 こんなロジックにすると思いますが。
 (momo)

 あ、1つ目が最後に来るのかな?
 とりあえず、読みにくいコードになってますね。
 (momo)

 どのトピだったかな?物覚えがどんどん悪くなっていて思い出せない。
URLアップしてくれればありがたいね。

 ところで、コードとしては2つとも??? ほんとに、このような構成で回答したのかなぁ?
アップされたコードの上に Set foundcell = Range("D:D").Find ・・・・ と、そのセルをfirstに格納しているところがあるとして
もし、D列に検索対象のものが「複数」あれば、2番目のコードでも、複数セルが取得されるはず。
むしろ、最初のコードは、最初の Findで見つかったところがダブって取得されてしまう。

 そもそも、Find/FindNext の構文は

 Sub 基本構文()
    Dim first As Range
    Dim foundcell As Range

    Set foundcell = Columns("D").Find ・・パラメータ・・・
    If foundcell Is Nothing Then Exit Sub
    Set first = foundcell
    Do
        '取得したfoundcellを処理
        Set foundcell = Columns("D").FindNext(foundcell)
    Loop While foundcell.Address <> first.Address

 End Sub

 アップされた2つのコード、いずれも、この基本からちょっとズレてるね。

 (ぶらっと)


衝突してしまいました。
 お昼からまた検証してみます。

 春日野様、いつも早い返信ありがとうございます。
 firstですが、
 @列から明日の日付を探す = foundcell
 Aもし見つからなかったらメッセージ
 B見つかったらfirstに格納 Set first = foundcell
 という感じでBに「最初に見つかったセル」が入っています。
 で、先ほど記載したコードで foundcell には Range("D:D").FindNext(foundcell)、
 次に見つかったセルを Set していますので If foundcell.Address = first.Address Then は
 「もし次に見つかったセル番地と最初に見つかったセルが同じだったとき」としています。
 全コードはこんな感じです。
 Aのメッセージはテストメッセージなので適当です。

Sub nouki()

Dim foundcell As Range
Dim first As Range
Dim atai() As String
Dim a As Long

Set foundcell = Range("D:D").Find(what:=Format(Now() + 3, "yyyy/mm/dd"), LookIn:=xlValues)

    If foundcell Is Nothing Then
    MsgBox "馬鹿"
      Exit Sub
    Else
     Set first = foundcell
    End If
ReDim atai(1 To Range("D" & Rows.Count).End(xlUp).Row)
    Do
     Set foundcell = Range("D:D").FindNext(foundcell)
        If foundcell.Address = first.Address Then
            Exit Do
        Else
         a = a + 1
         atai(a) = foundcell.Offset(0, -1).Value
        End If
    Loop
ReDim Preserve atai(1 To a)

 MsgBox "明日" & foundcell & "納期です" & vbLf & vbLf & Join(atai, vbLf) _
& vbLf & vbLf & "出荷しますか?", vbYesNo, "出荷確認"

 End Sub
 (ちょこたん)

 すいません。
 お昼なのですが気になったのでもう一度コードを試したところ、
 最初のコードもうまくいきませんでした;
 色々いじくっているうちにぐちゃぐちゃになった模様です。
 またお昼一番に皆様のコメントを読んでじっくり考えたいと思います。
 そのときは随時経過報告させていただきますのでアドバイスの程お願いします。
 (ちょこ)

 先ほどうまくいったと載せたコードはうまくいきませんでしたので
 もう一度いじくってみたところ、
    Do
         a = a + 1
         atai(a) = foundcell.Offset(0, -2).Value & " " & foundcell.Offset(0, -1).Value
     Set foundcell = Range("D:D").FindNext(foundcell)
    Loop While foundcell.Address <> first.Address

 ↑このコードでうまくいきました。
 1つ目も一番上に来ています。でもこれだとあまり良いとはいえないないコードなんですよね?
 ぶらっと様の基本構文を理解し、momo様の提示していただいたコードをよく読み、もう少し勉強してみます。

 あと、以下が以前質問したURLです。
[[20111130110337]] 
 (ちょこたん)

 >でもこれだとあまり良いとはいえないないコードなんですよね?

 Do〜Loop内のコードだけ見るなら、なんの問題も無いと思いますよ?
 元々問題なのはFindNextの位置だけですから。

 あとはループの終わり処理やデータの整理などは
 人によって千差万別ですから、何が良いともいえないです。
 (momo)

 そうなのですね。FindNext...勉強します!
 Do〜Loop while の構文の方は FindNext の位置を変えることで問題なく作動しましたが、
 Do〜Loop 間の If foundcell.Address = first.Address でコードを書きたい場合はどういったコードになりますか?
 今作成中のマクロはDo〜Loop whileのうまくいったコードで使用しますが、
 勉強の為に知っておきたいのでどなたか教えてくれませんか?
 (ちょこたん)


 あまりそのような事はFindのループでは必要無いと思いますが
 Do
  処理
   Set ・・・・FindNext・・・
 Loop While Not foundcell.Address = first.Address 

 なのですから

 ※補足ですがこの場合
 Loop While foundcell.Address <> first.Address 
 Loop While Not foundcell.Address = first.Address 
 Loop Until foundcell.Address = first.Address
 の、どれでも同じ

 Do
   処理
   Set ・・・・FindNext・・・
   If foundcell.Address = first.Address Then
     Exit Do
   End If
 Loop

 で、同じ意味になりますね。

 普通のDo〜Loopの場合と違う所(何が普通かは置いておいて)は
 最初にFindしておいて処理と再建策がループ内にあるので
 抜けるかどうかは次の検索の後に行う事
 つまり 処理→再検索→抜け判定 という順番を変えないように
 してあげれば大丈夫です。

 逆に言うと、順番を入れ替えて処理をIfの中に入れようとすると

   Set ・・・・FindNext・・・
   If foundcell.Address = first.Address Then
     Exit Do
   Else
     処理
   End If

 は、最初の検索したものが除外されてしまうし

   If foundcell.Address = first.Address Then
     Exit Do
   Else
     処理
   End If
   Set ・・・・FindNext・・・

 は、最初から条件に一致してしまうので抜けてしまいます。
 と、余計にややこしいコードになるので素直に最初に示した
 3つのようにLoopの最後で判定してしまったほうが良いです。
 (momo)

 なるほど。
 たしかにmomo様の言うとおりですね。
 その2つのコード、実は既に実行済みでうまくいかなくてなんでだろう?と思っていたのですが、
 >最初の検索したものが除外されてしまうし、
 >最初から条件に一致してしまうので抜けてしまいます。
 この2行で納得です。
 マクロは初心者でまだまだわからないだらけですがこうして理解できることが増えてくると楽しいですね!
 あと、もうひとつ続きでお伺いしたいのですが、
 メッセージボックスで「出荷しますか?」でYESを押したとき、その日付の2つ横のセル(出荷確認セル)に
 「済」という文字を記入したいと思い、自分で考えてコードを作ってみました。
 このコードでうまくいくのですがもっとスマート?なコードとかありますか?
 先ほどのコードの続きです。

     ReDim Preserve v(1 To k)
      Button = MsgBox(FoundCell & "出荷予定は以下です" & vbLf & vbLf & Join(v, vbLf) & vbLf & vbLf & "出荷しますか?", vbYesNo, "出荷の確認")
       If Button = vbYes Then
        Do
         Set FoundCell = Cells.FindNext(FoundCell)
          If FoundCell.Address = First.Address Then
            Exit Do
          Else
            First.Offset(0, 2).Value = "済"
            FoundCell.Offset(0, 2).Value = "済"
          End If
        Loop
       End If
     Exit Sub
        Set FoundCell = Nothing
        Set First = Nothing
 End Sub

 しかしですねこのコード、シート1で使うのですが、シート1では問題なく動いたのですが試しにシート2で試してみたところ、
 メッセージが出るところまでは問題ないのですが、YESを押しても「済」が記入されませんでした。
 やはりよくないコードですか?
 (ちょこ)


 ていうかこれも
 Do〜Loop Whileの式のがいいのかな?
 (ちょこ) 

 >Do〜Loop Whileの式のがいいのかな?
 当然そうですね。
 でも他にもありそうです。
     ・Cellsを対象にFindNextしたり
     ・最後に意味の無いExitSubがあったり
   ・そもそも検索対象がなんなのか・・・

 とりあえず、プロシージャの一部分だけを抜き出して見せられても
 まったく動作がイメージできません。

 シートを指定している部分もFindメソッドの部分もありませんし。
 どんなに優秀な回答者さんでも、その部分だけでは正しく動くかどうかなんて
 わからないと思います。

 可能なら全部載せてみてください。
 (momo)

 衝突しました。
 Do〜Loop While の式にしたところシート2でも問題なく動きました!
 全コードはこちらになります↓(momo様に言われたところを一部修正しました)

 Sub TEST10()

    Dim FoundCell As Range
    Dim First As Range
    Dim v() As String
    Dim k As Long
    Dim Button As Integer

    ReDim v(1 To Range("B" & Rows.Count).End(xlUp).Row)
     Set FoundCell = Range("B:B").Find(what:=Format(Now(), "yyyy/mm/dd"), LookIn:=xlValues)
      If FoundCell Is Nothing Then
       MsgBox "見つかりません"
        Exit Sub
      End If
       Set First = FoundCell
      Do
       k = k + 1
       v(k) = FoundCell.Offset(0, 2).Value & " " & FoundCell.Offset(0, 3)
       Set FoundCell = Range("B:B").FindNext(FoundCell)
      Loop While FoundCell.Address <> First.Address

     ReDim Preserve v(1 To k)
      Button = MsgBox(FoundCell & "出荷予定は以下です" & vbLf & vbLf & Join(v, vbLf) & vbLf & vbLf & "出荷しますか?", vbYesNo, "出荷の確認")
       If Button = vbYes Then
        Do
            First.Offset(0, 4).Value = "済"
            FoundCell.Offset(0, 4).Value = "済"
            Set FoundCell = Range(B:B).FindNext(FoundCell)
        Loop While FoundCell.Address <> First.Address
       End If
        Set FoundCell = Nothing
        Set First = Nothing
 End Sub

 (ちょこ)

 その処理なら2回もFindをループしなくても

  Sub TEST10()
    Dim FoundCell As Range
    Dim First As Range
    Dim v() As String
    Dim k As Long
    Dim Button As Integer
    Dim FoundRng As Range

    ReDim v(1 To Range("B" & Rows.Count).End(xlUp).Row)
    Set FoundCell = Range("B:B").Find(what:=Format(Now(), "yyyy/mm/dd"), _
                                      After:=Range("B" & Rows.Count), _
                                      LookIn:=xlValues)
    If FoundCell Is Nothing Then
      MsgBox "見つかりません"
      Exit Sub
    End If
    Set First = FoundCell
    Do
      k = k + 1
      v(k) = FoundCell.Offset(0, 2).Value & " " & FoundCell.Offset(0, 3)
      If FoundRng Is Nothing Then
        Set FoundRng = FoundCell
      Else
        Set FoundRng = Application.Union(FoundRng, FoundCell)
      End If
      Set FoundCell = Range("B:B").FindNext(FoundCell)
    Loop While FoundCell.Address <> First.Address
    ReDim Preserve v(1 To k)
    Button = MsgBox(FoundCell & "出荷予定は以下です" & vbLf & vbLf & Join(v, vbLf) _
                    & vbLf & vbLf & "出荷しますか?", vbYesNo, "出荷の確認")
    If Button = vbYes Then
      FoundRng.Offset(, 4).Value = "済"
    End If
    Set FoundCell = Nothing
    Set First = Nothing
    Set FoundRng = Nothing
  End Sub

 こんなので出来ませんか?
 (momo)

 コードありがとうございます。
 分からないことをいくつか質問させていただきたいのですが、

 >If FoundRng Is Nothing Then
        Set FoundRng = FoundCell

 これは 「もしFoundRngがないとき、FoundCellを格納する」であってますか?
 しかしその上のコードにはFoundRngには何もいれていないから最初は空。
 なので最初のループでFoundCellが格納される。
 2回目のループでは空ではないから

 Else
        Set FoundRng = Application.Union(FoundRng, FoundCell)

 の式に行って、Application.Union(FoundRng, FoundCell)を格納する。・・・ですか?
 この Application.Union(FoundRng, FoundCell) の部分がいまいちわからないのですがこれは何を指してますか?

 もうパソコンが使えない為返信は遅くなるかもしれませんが最後までお付き合いくださいましたらうれしい限りです。
 (ちょこ)


 If分岐の考え方はそれで合っています。

 最初は何も無いので見つかっていればそれを格納
 で、2度目からはUnionで格納するわけですが
 Unionの所にカーソルを当ててF1キーを押すとヘルプが出てきます。

 ヘルプを要約すると、UnionはRangeとRangeをくっつけます。

 Set Rng = Range("A1,B2,C3")
 と
 Set Rng = Application.Union(Range("A1"), Range("B2"), Range("C3"))
 は、同じ意味になります。

 なので
 >Set FoundRng = Application.Union(FoundRng, FoundCell)
 の部分は、今までに見つかったセルと、今回見つかったセルを一纏めにしておく
 という意味です。

 見つかったセルをFoundRngという変数に一纏めにしておけば
 あとで一括で値を入れるなどの処理に使えるかな〜
 なんていう発想が出来れば使い道が色々見えてきますよね。
 (momo)

 返信が遅くなり申し訳ありません。
 Application.Unionの意味はなんとなく理解できました!
 あともうひとつお聞きしたいのですが、最初の

 Set FoundCell = Range("B:B").Find(what:=Format(Now(), "yyyy/mm/dd"),After:=Range("B" & Rows.Count),LookIn:=xlValues)
 の After:=Range("B" & Rows.Count) の部分なんですが、
 なんとなくB列のセルをカウントしているのかな?と思っていますが [After:=] が指す意味が分かりません。
 F1を押してもヘルプは出てきませんでした。
 別になくても動きましたし、そこに記述する意味、この一文の意味を教えていただけませんでしょうか?

 (ちょこ)


 またもやお邪魔虫で。

 指定領域.Find(あるいはFindNext)は、指定された値を見つけようとするんだけど、
After:=で指定されたセルから検索開始・・・ではなく、実際は
After:=で指定されたセル「の次のセル」から検索開始する。
(After:=が省略されると、指定領域の先頭のセルが指定されたと解釈される)
だから、指定セルに、その値があっても、それはチェックされずに、「その次」に見つかったものが
「最初に見つかったもの」として扱われる。

 ↑の場合、指定領域はB列で、これをB1から検索すると、B1にその値があっても後回しにされる。
なので、Range("B" & Rows.Count) これはB列のエクセルとしての最終行、2003なら B65536。
ここをスタートにして、「その次」からチェックされる。B列におけるB65536の「次」は B1。
なので、もしB1に検索値があれば、それが最初に見つかるセルになるということ。

 (ぶらっと)

 なるほどです!
 ということはAfter:=Range("B" & Rows.Count)の記述がなければ
 最初に見つかるセルが本当は2つ目のセルってことなんですね?
 休みを挟んでしまいRows.Countの意味も忘れていました;
 そうですよね。Rows.Countは最終行って意味でした;
 基礎中の基礎を忘れてしまうようではまだまだです><
 皆様に教えて頂いたおかげですごい良いマクロが出来そうです!!!!
 今までは練習用ブックにコードを記述していましたが 
 今から練習用コードを(なるべく)見ずに本番ブックにコードを記述したいと思います!
 ここで聞いていないやりたいこともまだあるのですがとりあえず自分で
 考えてから行き詰ったら又来ます。
 そのときは今一度お付き合い下さると嬉しいです。

 (ちょこ) 


 すいません。さっそく行き詰りました(早っ)
 本番用マクロを作るにあたりアドバイスをいただきたいのですが、
 今の段階では
 @ブックを開いたときシート1なら今日の出荷予定を調べメッセージ表示。
 A今日出荷予定がなければ「ないよ」といメッセージが出る
 B今日出荷のものがあったらメッセージで出荷確認。
 COKなら出荷確認に「済」を記入。NOなら何もしない。
 というマクロです。

 そのあとに別の列に納期という項目があるのでその列から明日納期のものを探し、
 もし見つかれば上記と同じような動きをさせたいです。
 コード自体はほぼ同じなので問題ないのですが、
 出荷予定日が今日、納期が明日というデータがあった場合、
 「済」を記入するという同じ行動を阻止したいのですが

 @納期から明日の日付を探し、一致したら出荷確認に「済」がないか確認。
 「済」がなければ変数に格納。あれば除外。
 これを繰り返し。
 最後にメッセージで確認後「済」記述。

 A納期から明日の日付を探し、一致したら変数に格納。繰り返し。
 一致したデータの2つ隣の値が「済」なら何もしない。
 「済」でないならメッセージ表示
 メッセージで確認後「済」記述。

 という感じでコード記述すればいいかな?と思ったのですが、
 @とAどちらが良いと言えますでしょうか?
 また、それよりこんな風にした方がいいよというのがあれば教えて下さい。
 今回は最初は自分で頑張りたいのでコードの提示はご遠慮願いますm(__)m
 どうしてもわからなくなったときはまたくるのでそのとき協力していただきたいです。
 (↑わがまま!)
 どうかよろしくお願いします。

 (ちょこ)


 個人的に作るなら1かな〜という感じです。
 2でも出来ると思いますが、変数に入れた後に除外というのは
 メモリー的にも無駄が多いと思います。
 Findで検索するとしてFoundCellのOffsetだけで判断してからでも良さそうなので。

 そもそもなんですが、あまりにもデータが多いようなら
 配列処理なんかにしても良いですね。
 結果的にはその方が速かったりもします。

 ま、速度なんて微々たるもんですからまずは完成させて
 処理効率とか速度なんて色々覚えていく過程で徐々にやっていけば良いと思いますよ^^

 あとFindメソッドのAfterに関してですが、私は必ず指定するようにしています。
 ぶらっとさんから御指摘があった理由もそうなんですが、
 暗黙的にActiveCellのままなんて状態のままで
 A列にActiveCellがある状態でB列にFindを掛けようと思うとエラーになったりします。
 そんな不足の事態を避けるためにも、最初から順にFindする時は最終セルを
 最終行を検索するときなんかは最初のセルを指定するようにしています。

 (momo)

 >あとFindメソッドのAfterに関してですが、私は必ず指定するようにしています。

 そのほうが、より明示的でコードを読む上で、わかりやすいよね。ただ、

 >暗黙的にActiveCellのままなんて状態のままで
 >A列にActiveCellがある状態でB列にFindを掛けようと思うとエラーになったりします。

 After:=activeCell と「明示的」に記述すればそうなるし、これに限らず、「まぎれが多い」ActiveCellは、できるだけ使用を避けた方が無難だけど
 そうではなく、「省略」という意味であれば、上でも書いたけど、ActiveCellではなく領域の左上隅のセルということになり
 ヒットする順番が重要ではない要件の場合は、どちらかというと、自分が書くコードとしては「省略」が 多いかな。

 (ぶらっと)

 >ActiveCellではなく領域の左上隅のセル
 失礼しました。
 省略の場合はそうでしたね・・・ちょっと勘違いでした。
 ぶらっとさん御指摘ありがとうございます。

 いずれにしてもFindメソッドの引数は直前の検索時のパラメータを引き継ぎます。
 Afterは手作業の引数にはありませんが、手作業で検索した時の引数も引き継いでしまうので
 全て明示的にするのが後々不要な事で悩まずに済むかと思います。

 (momo)

 お返事ありがとうございます。
 やはり@の方がよさそうですね^^
 @の方で頑張ってみます☆
 FindメソッドのAfterに関しても詳しく教えていただきありがとうございます!
 初心者なのでまずは省略などせずにmomo様のようにきちんと記述していきたいと思います。
 さっそくマクロを作りたいのですが違う仕事が入ってしまって
 しばらく触れそうにありません。
 なので一旦終わりにさせてください..m(__)m
 こんなにも親身に付き合っていただいてありがとうございました。
 またこのマクロを作る時、行き詰ってしまったらこちらに新しく質問しますので
 そのときはどうかよろしくお願いします。

 (ちょこ)

 お久しぶりです。
 行き詰ってしまいました。
 明日納期で「済」の入っていないデータを探すというコードなのですが
 Findで検索できるのは1つだけですよね?
 Findで日付を検索したとしてどうやって「済」が入っているセルを探せばいいでしょうか?
 一応メッセージボックスに表示させないようにはできたのですが
 このコードだとまだ変数の中に「済」が入ったデータ・・・ありますよね?
 変数に格納する前に除外するにはどうしたらいいでしょうか?
 コード載せておきます

    Dim FoundCell As Range
    Dim First As Range
    Dim v() As String
    Dim k As Long
    Dim Button As Integer
    Dim Foundrng As Range

    Set FoundCell = Range("D:D").Find(What:=Format(Now() + 1, "yyyy/mm/dd"),After:=Range("D" & Rows.Count), LookIn:=xlValues)
     If FoundCell Is Nothing Then
        Exit Sub
     End If
     Set First = FoundCell
    ReDim v(1 To Range("D" & Rows.Count).End(xlUp).Row)
     Do
       If FoundCell.Offset(, 2).Value = "" Then '←追加コードです
        k = k + 1
        v(k) = FoundCell.Offset(0, -2).Value & " " & FoundCell.Offset(0, -1)
       End If
        If Foundrng Is Nothing Then
            Set Foundrng = FoundCell
        Else
            Set Foundrng = Application.Union(Foundrng, FoundCell)
        End If
        Set FoundCell = Range("D:D").FindNext(FoundCell)
      Loop While FoundCell.Address <> First.Address

      ReDim Preserve v(1 To k)
      Button = MsgBox(FoundCell & "、明日納期です" & vbLf & vbLf & Join(v, vbLf) & vbLf & vbLf & "出荷しますか?", vbYesNo, "出荷の確認")
       If Button = vbYes Then
            Foundrng.Offset(, 2).Value = "済"
       End If
        Set FoundCell = Nothing
        Set First = Nothing
        Set Foundrng = Nothing

 End Sub

 (ちょこ)


 最後の確認メッセージと処理のタイミングが意図したものになっているかどうか不安だけど。
対象のものを表示し、OKなら一括で"済"を記入するコード。

 Sub Sample()
    Dim FoundCell As Range
    Dim First As Range
    Dim v() As Range
    Dim v2() As String
    Dim k As Long
    Dim Button As Integer
    Dim Foundrng As Variant

    ReDim v(1 To Range("D" & Rows.Count).End(xlUp).Row)
    ReDim v2(1 To UBound(v))

    Set FoundCell = Range("D:D").Find(What:=Format(Now() + 1, "yyyy/mm/dd"), After:=Range("D" & Rows.Count), LookIn:=xlValues)
    If FoundCell Is Nothing Then Exit Sub

    Set First = FoundCell

    Do
        If FoundCell.Offset(, 2).Value = "" Then '←追加コードです
            k = k + 1
            Set v(k) = FoundCell
            v2(k) = FoundCell.Offset(0, -2).Value & " " & FoundCell.Offset(0, -1)
        End If
        Set FoundCell = Range("D:D").FindNext(FoundCell)
    Loop While FoundCell.Address <> First.Address

    If k > 0 Then
        ReDim Preserve v(1 To k)
        ReDim Preserve v2(1 To k)

        If MsgBox("以下が明日納期です" & vbLf & vbLf & Join(v2, vbLf) & _
                vbLf & vbLf & "出荷しますか?", vbYesNo, "出荷の確認") = vbYes Then
            For Each Foundrng In v
                    Foundrng.Offset(, 2).Value = "済"
            Next
        End If
    Else
        MsgBox "明日納期のもので未処理のものはありませんでした"
    End If

    Erase v
    Set FoundCell = Nothing
    Set First = Nothing

 End Sub

 さっそくありがとうございます。ぶらっと様でしょうか?
 動作はまったく問題ありませんでした!
 ですが勉強の為にいくつか質問させていただきたいのですが

 ReDim v2(1 To UBound(v)) はどういった意味なんでしょう?

 配列が1〜v()で取得した値・・ということでしょうか?
 調べたら UBound 関数は配列のサイズを調べるために使います。
 インデックス番号の最小値を調べるには、LBound 関数を使ってください。とありましたがいまいち分かりません。 
 あと
     If k > 0 Then
        ReDim Preserve v(1 To k)
 と
 For Each Foundrng In v
 この二文がなにをしているか知りたいです。

 もしkが0より大きいときvは1〜k・・ですよね?
 ん〜この二文がなにをしているか知りたいというよりvが何を表しているのか知りたい?
 ごめんなさい。まだまだ勉強が足りないですね。。。

 (ちょこ)


 はい、ぶらっとでございます。(HN書くのを忘れた)

 >ReDim v2(1 To UBound(v)) 

 V2という配列の大きさをVとおなじ大きさにしたい。

 ReDim v(1 To Range("D" & Rows.Count).End(xlUp).Row) これはわかるよね。
 v(1 to 最終行番号) ということだね。
 なので、ReDim v2(1 To Range("D" & Rows.Count).End(xlUp).Row) でもいいんだけど
 仮に変更があれば2箇所の変更になる。
 なので To 側の数字を Ubound(v) つまりvの行数という指定をしてみた。

 >If k > 0 Then
 >       ReDim Preserve v(1 To k)

 アップされたコードに ReDim Preserve v(1 To k) こんな記述があるので Redim Preserve はOKだよね。
 で、既に済のものは対象外だよね。結果、同じ日付が合っても全て済なら該当がなかったということ。
 この状態を k がゼロかどうかで判定。(もし、あれば k は少なくとも 1 になってるよね)

 >For Each Foundrng In v

 こちらでアップしたコードが、
 Dim v() As Range
 Set v(k) = FoundCell
 こんなふうに記述してる。
 配列 v の各要素が Range型のオブジェクトということで、そこに見つかったFoundCellという
 オブジェクトを格納している。

 たとえば Each Range型変数 In Range("A1:A10") なんてやると、A1からA10までのセルが
 オブジェクトとしてRange型変数に格納されるね。これと一緒で、FoundRngというRange型変数に
 該当のD列の日付のセルのオブジェクトを取り出している。

 (ぶらっと)


 >To 側の数字を Ubound(v) つまりvの行数という指定をしてみた。

 なるほどですね!なんか私大きな間違いをしてたみたいです。
 vの値をv2に入れるという感覚でした;配列だから値はひとつじゃないですもんね。
 配列の範囲ですね。あれ?違う?

 >Redim Preserve はOKだよね。

 たしか Redim Preserve は格納した値を記憶したまま配列の範囲を変える・・だったような。
 kは If FoundCell.Offset(, 2).Value = "" Then のときに k = k + 1 の処理をするから
 「済」がある場合は0になる。
 「済」があった場合、つまり0のときの処理が

 >MsgBox "明日納期のもので未処理のものはありませんでした"

 になるんですね!

 >FoundRngというRange型変数に
 該当のD列の日付のセルのオブジェクトを取り出している。

 ここがまだいまいち分からないのですが
 vの配列の中に今まで見つかった同じ日付セルがセットされていて
 その見つかったセルを一つずつ FoundRng に取り出しているってことですか?
 でもそれならそのままv.Offset(, 2).Value = "済"じゃだめなんですか?
 そもそも配列変数は無理なのか?1つの大きい箱って考えたらそうか・・なんかだめそうですね。
 うーん・・まだまだ基礎が足りてないです。もっと勉強します。
 ぶらっと様、長い期間お付き合いくださいましてありがとうございます^^
 どうか、どうかあと少しお付き合いください。お願いしますm(__)m

 (ちょこ)


 >でもそれならそのままv.Offset(, 2).Value = "済"じゃだめなんですか?

 いやいや、だめじゃない。( v. はだめだけど)

 新規ブックに以下を貼り付けて実行してみて。
 すべて同じ結果になる。コードってのはいろんな書き方があるね。

 Sub Test1()
    Dim v(1 To 3) As Range

    Range("D2").Value = "ABC"
    Range("D3").Value = "DEF"
    Range("D4").Value = "HIJ"

    Set v(1) = Range("B2")
    Set v(2) = Range("B3")
    Set v(3) = Range("B4")

    MsgBox v(1).Address & "/" & v(1).Offset(, 2).Value
    MsgBox v(2).Address & "/" & v(2).Offset(, 2).Value
    MsgBox v(3).Address & "/" & v(2).Offset(, 2).Value

 End Sub

 Sub Test2()
    Dim v(1 To 3) As Range
    Dim c As Variant

    Range("D2").Value = "ABC"
    Range("D3").Value = "DEF"
    Range("D4").Value = "HIJ"

    Set v(1) = Range("B2")
    Set v(2) = Range("B3")
    Set v(3) = Range("B4")

    For Each c In v
        MsgBox c.Address & "/" & c.Offset(, 2).Value
    Next

 End Sub

 Sub Test3()
    Dim v(1 To 3) As Range
    Dim i As Long

    Range("D2").Value = "ABC"
    Range("D3").Value = "DEF"
    Range("D4").Value = "HIJ"

    Set v(1) = Range("B2")
    Set v(2) = Range("B3")
    Set v(3) = Range("B4")

    For i = 1 To 3
        MsgBox v(i).Address & "/" & v(i).Offset(, 2).Value
    Next

 End Sub

 (ぶらっと)

コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.