[[20150702130010]] 『配列で合計を!』(タロウZ) ページの最後に飛ぶ

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

 

『配列で合計を!』(タロウZ)

 勉強のため、配列でやっていますが、
  A列の検索品番がD列になかったら空白にしたいのですが思ったようにできません。
 下記のマクロでは、検索品番の一つ前の値と同じになってしまします。
  On Error Resume Nextを除けるとマクロが動かないし・・・
  どなたか宜しくお願いします。

  A列は検索品番、B列は合計数をだしたい。
  データーは、D列に品番、E列からJ列は数値

   Sub 合計()
    On Error Resume Next
    lastRow = Range("A" & Rows.Count).End(xlUp).Row

    配列 = Range("A2:A" & lastRow)

      For i = 1 To lastRow

            ANS = WorksheetFunction.Sum(Sheets(1).Range(Range("D1:D10000").Find(What:=配列(i, 1)).Address(0, 0)).Resize(1, 7))

           配列(i, 1) = ANS

      Next

    Range("B2:B" & lastRow).Value = 配列

   End Sub

< 使用 Excel:Excel2002、使用 OS:Windows2000 >


 自分はFindをあまり使用しないのでよくわからないですが、

 >ANS = WorksheetFunction.Sum(Sheets(1).Range(Range("D1:D10000").Find(What:=配列(i, 1)).Address(0, 0)).Resize(1, 7))

 質問者さんはD1からD10000の範囲で検索品番に引っかかった品番の値をすべて合計したいのだと思うのですが、これだと見つかった行だけしか合計しないような気がします。

 もっと簡単なコードがあると思いますが、自分はこんな感じで作ってみました。

 Option Explicit

 Sub 合計()
 Dim lastRow As Long
 Dim lastRowD As Long
 Dim 配列 As Variant
 Dim i As Long, j As Long
 Dim ANS As Long
 Dim 合計 As Long
 Dim 集計 As Long
 Dim cnt As Long

    'On Error Resume Next
    lastRow = Range("A" & Rows.Count).End(xlUp).Row
    lastRowD = Range("D" & Rows.Count).End(xlUp).Row

    配列 = Range("A2:A" & lastRow)
    集計 = 0
    cnt = 0

    For j = 1 To UBound(配列)
      For i = 1 To lastRowD
        If Cells(i + 1, 4).Value = 配列(j, 1) Then
            cnt = cnt + 1
           合計 = WorksheetFunction.Sum(Cells(i + 1, 5).Resize(1, 7))
           集計 = 集計 + 合計
        End If
      Next
      If cnt = 0 Then
        配列(j, 1) = ""
      Else
        配列(j, 1) = 集計
        cnt = 0
      End If
      集計 = 0

    Next j
    Range("B2:B" & lastRow).Value = 配列
 End Sub

(KUKI) 2015/07/02(木) 14:47


 まだ、コードはほとんど読んでいません。
 ざっと眺めた感じだけで、コメントします。

 1.On Error Resume Nextを除けるとマクロが動かないし・・

  そもそも、これが間違っています。エラーになるということは原因があるわけです。
  その原因を究明して、ロジックの不備を直すということが、最初の一歩です。

 2.Findメソッドでは、見つからなかったときに Nothing がかえります。
  その時、Find(What:=配列(i, 1)).Address(0, 0) は Nothing.Address(0,0) になりますのでエラーになります。
  Find を単独で実行して、その結果を取得し、Nothing であればスキップ等の措置が必須です。

 3.同じく Findメソッドの重要な引数、LookAt (部分一致か完全一致)は、指定がない場合、
   直前のエクセル上の置換、検索(手作業、あるいはVBA処理)の条件を引き継ぎます。
   自分がどちらでやりたいのか、それを明記することが必要です。

 4.Sheets(1).Range(Range("D1:D10000").Find

   領域の規定に重大な欠陥があります。(状況によっては結果オーライですが)
   Sheets(1).Range ですから、Sheets(1)上の領域ですね。なのに、その中身として、Range("D1:D10000")
   これは、たままたま、その時にアクティブになっていたシートの D1:D10000 です。
   もし、実行時に Sheets(1) がアクティブになっていなければ、1004(かな?)のエラーになります。

   なぜ、。ふつうに Sheets(1).Range("D1:D10000").Find と記述しないのですか?

 ところで、この D10000 ですが、本当に、固定で 10000行までですか? それとも D列のデータ最終行までという意味ですか?
 後者なら、アップされたコードの別のところで最終行の取得をしていますよね。
 ここでも、最終行を取得して領域を指定すべきですね。

 ★追加です。変数は必ずすべて宣言しましょう。モジュールの先頭に Option Explicit を記述してください。
  (VBE画面のツール->オプション の編集タブの変数の宣言を強制する にチェックしておくと自動的に記述されます)

(β) 2015/07/02(木) 15:14


 とりあえず、アップされたコードの処理方式でコードを整理しますと以下になりますね。
 なお、関連データはすべて Sheets(1) にあるという前提です。

 Sub 合計2()
    Dim 配列 As Variant
    Dim i As Long
    Dim c As Range

    With Sheets(1)
        配列 = .Range("A2", .Range("A" & Rows.Count).End(xlUp)).Resize(, 2).Value
        For i = 1 To UBound(配列, 1)
            配列(i, 2) = Empty
            Set c = .Range("D1", .Range("D" & Rows.Count).End(xlUp)).Find(What:=配列(i, 1), LookAt:=xlWhole)
            If Not c Is Nothing Then 配列(i, 2) = WorksheetFunction.Sum(c.Offset(, 1).Resize(, 7))
        Next
        .Range("A2").Resize(UBound(配列, 1), 2).Value = 配列
    End With

 End Sub

(β) 2015/07/02(木) 15:29


  鋭い指摘有難うございます。みなさんの指摘どおりです。
  データーは簡単なほうが良いと思いまして・・・

  (KUKI)さん とりあえずD列に品番は1つだけです。

  (β)さん 最終的に別ブックのシート1を参照する予定でした。
      D10000 は、暫定範囲です。最終行を取得して領域を指定します。

    無事に目的としたことが出来ました。
    みなさん、 いろいろお世話になりました。

 追加になるのですが、A列に空白があるのですが、
 A列が空白の場合B列も空白にしたいのですがどのように
 変更したらいいでしょうか?宜しくお願いします。

(タロウZ)


 Sub 合計3()
    Dim 配列 As Variant
    Dim i As Long
    Dim c As Range

    With Sheets(1)
        配列 = .Range("A2", .Range("A" & Rows.Count).End(xlUp)).Resize(, 2).Value
        For i = 1 To UBound(配列, 1)
            配列(i, 2) = Empty
            If Not IsEmpty(配列(i, 1)) Then
                Set c = .Range("D1", .Range("D" & Rows.Count).End(xlUp)).Find(What:=配列(i, 1), LookAt:=xlWhole)
                If Not c Is Nothing Then 配列(i, 2) = WorksheetFunction.Sum(c.Offset(, 1).Resize(, 7))
            End If
        Next
        .Range("A2").Resize(UBound(配列, 1), 2).Value = 配列
    End With

 End Sub

(β) 2015/07/02(木) 17:13


 これでチェック時に大幅に楽になりました。
 みなさん有難うございました。

(タロウZ)


  (β)さんの上記の 合計3 のマクロで下記記述がありますが

         配列(i, 2) = Empty

   理解に苦しんでいます。なぜ必要なのか・・・
   申し訳ありませんが教えてください。

(タロウZ)


 コード処理方法として、B列用の空っぽの配列を別に用意し、マッチした場合に、そこに相当する場所に
 値を入れて、最後に、そこからB列に落とし込むというやりかたもあります。
 アップされたそちらのコードではそれに近いですね。(もっとも ?? のところもあります。後述)

 今回、アップしたコードでは、Resize(,2) で2列の配列(A,B列)にしています。

    配列 = .Range("A2", .Range("A" & Rows.Count).End(xlUp)).Resize(, 2).Value

 ということは、2列目は、もともとB列に入っていた値ですから、もしかしたら空白かもしれないし
 もしかしたら何か値が入っているかもしれませんね。

 なので、まず、空白にした上でマッチしたら値をセット。こうしておいて最後に

   .Range("A2").Resize(UBound(配列, 1), 2).Value = 配列

 こうして、2列の領域(A,B列)に落とし込んでます。

 (KUKI)さんも、同じように、以下の部分で、マッチすればそれを、マッチしなければ空白をセットしておられます。

      If cnt = 0 Then
        配列(j, 1) = ""
      Else
        配列(j, 1) = 集計
        cnt = 0
      End If

 ところで、そちらのコードですが

 配列 = Range("A2:A" & lastRow)

 すべての配列の要素としてA列の品番が格納されますね。
 で、そこに結果をいれていって、B列に落とし込んでいます。
 すべての要素が、集計結果で置き換わればそれでもいいのですが、マッチしなかった場合もありますよね。
 (そちらでアップされたコードでは、逆に、その手当てが抜けていましたが)
 その場合、なにもしなければ、B列に品番が書きこまれる結果になりますね。

(β) 2015/07/03(金) 16:35


 かえって混乱するかもしれませんが、コメントした「B列用の空っぽの配列を別に用意し」の場合のコードです。
 (テストしていないので、バグあればご容赦)

 Sub 合計4()
    Dim 配列 As Variant
    Dim 結果 As Variant
    Dim i As Long
    Dim c As Range

    With Sheets(1)

        配列 = .Range("A2", .Range("A" & Rows.Count).End(xlUp)).Value
        ReDim 結果(1 To UBound(配列, 1), 1 To 1)

        For i = 1 To UBound(配列, 1)
            If Not IsEmpty(配列(i, 1)) Then
                Set c = .Range("D1", .Range("D" & Rows.Count).End(xlUp)).Find(What:=配列(i, 1), LookAt:=xlWhole)
                If Not c Is Nothing Then 結果(i, 1) = WorksheetFunction.Sum(c.Offset(, 1).Resize(, 7))
            End If
        Next

        .Range("B2").Resize(UBound(結果, 1)).Value = 結果

    End With

 End Sub

(β) 2015/07/03(金) 17:32


 多少の応用はできますが、マクロ自体を最初から書くのはまだまだです。手当てまでは理解できていません。
 詳しく解説していただき有難うございます。

 なにもしなければ、B列に品番が書きこまれる結果になりますね。−−−そのとおりです。対処していただき有難うございます。

 バグ ありません。完璧です。 

(タロウZ)

追記:私が書いたマクロより (β)さんのマクロは格段に早いです。
有難うございます!


コメント返信:

[ 一覧(最新更新順) ]


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