- ベストアンサー
Excel VBAの配列でデータを他のシートに書き込もうとするとエラーになる理由
- EXCEL VBAの配列でデータを他のシートに書き込もうとするとエラーになってしまいます。コードの内容は、ある範囲のある列から空白ではないセルを探し出してその行のデータを配列で取得し、他のシートに一括で書き込むというものです。
- 最後の他のシート(作業中シート)に書き込もうとするとエラーになってしまいます。”Sheets("作業中").”を抜くと同じシートに結果は返ってくるのですが…。
- 同じシートからコードを実行するとうまくいきます。なぜなのでしょうか?
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
こんばんは。 Sheets("作業中").Range(Cells(1, 1), Cells(a, 4)).Value = e と言う場合、Cellsプロパティは、マクロを実行しているActiveSheetの属性のままだから、エラーが発生します。 そのような場合は、With ステートメントでつないであげます。 Range オブジェクトは、Cells を直接、配下にはしていませんから、こういう問題が発生します。たぶん、VBAの文法を作るときには、こんなことは想定していなかったに違いありません。 こういう高級文法を用いた構文では、よほどのことがない限りは、シート Select やWorkbook の Activate は用いません。 With Sheets("作業中") .Range(.Cells(1, 1), .Cells(a, 4)).Value = e End With ところで、私個人も、同じ内容で考えてみました。配列がジャグになっていますので、少し、ややこしいです。このような場合は、Sort オブジェクトを使っても良いのですが、それも、また面倒です。 本来は、Copy メソッドとかを使い、もう少し基本的なコードで書いたほうがよいかもしれません。また、同じ局面の中で、Range 型と Cells 型の混在は、非常に読みにくいです。どちらかに統一したほうがよいです。今回のようなミスを誘発します。Range型で書く場合は、Resize を使うと便利です。 -------------------------------------- Sub 作業中2() Dim WC() As Variant Dim WCE() As Variant Dim i As Long Dim n As Long, m As Long Dim x As Long, x1 As Long Dim y As Long, y1 As Long With Range("H1").CurrentRegion For i = 1 To .Rows.Count If .Cells(i, 5).Value <> "" Then ReDim Preserve WC(n) WC(n) = .Rows(i).Resize(, 4) n = n + 1 Else ReDim Preserve WCE(m) WCE(m) = .Rows(i).Resize(, 4) m = m + 1 End If Next i End With x1 = UBound(WC()) y1 = UBound(WCE()) For x = 0 To x1 Range("O1").Offset(x).Resize(1, 4).Value = WC(x) Next x For y = 0 To y1 Range("S1").Offset(y).Resize(1, 4).Value = WCE(y) Next y Worksheets("作業中").Range("A1").Resize(x1 + 1, 4).Value = _ Range("O1").Resize(x1 + 1, 4).Value End Sub
その他の回答 (3)
- Wendy02
- ベストアンサー率57% (3570/6232)
こんばんは。 #3 の回答者です。 なお、AutoFilter メソッドは侮れません。これって、選択するのに、3万行ぐらいになっても一瞬で行ってしまうのです。1万行ぐらいですと、まあ、配列変数でこなせられるけれども、それ以上の場合は、AutoFilter を考えたほうがよいです。一応、昔のバージョンなどですと、なにやらバグの話も聞いたけれども、今は、そんなことがありませんから、見えているものだけを、Copy するというワザが使えます。 それから、 Application.ScreenUpdating =False の効用は知っていますか? Copyメソッドとかでも、結局のところ、Select や Range オブジェクトを取得するときに、画面もつられるので、重くなるのであって、画面の動きを止めると、格段にスピードが速くなります。
- bin-chan
- ベストアンサー率33% (1403/4213)
Next e = Range(Cells(1, 15), Cells(a, 18)).Value '以下の一行を追加してください。 Sheets("作業中").Select Sheets("作業中").Range(Cells(1, 1), Cells(a, 4)).Value = e End Sub
お礼
うまく動きました~!! ありがとうございます!! でも、どうして、test()のプロシージャだとうまく動くんですかね? シロウトなのでよくわかりません…。 ともかく本当にありがとうございました!!
- bin-chan
- ベストアンサー率33% (1403/4213)
どの行でどんなエラーが出るのかな? 変数宣言の次の行、 myRow = Range("H1").CurrentRegion.Rows.Count で、Range("H1")とはどのシートを指しているのでしょうか? なんだか、作業中シートとは別のシートでは?と思いますが。
補足
Sheets("作業中").Range(Cells(1, 1), Cells(a, 4)).Value = e の部分が黄色くなって、 アプリケーション定義またはオブジェクト定義エラーです といわれます。 マクロ実行時はデータが載っているシートを開いて実行します。 そのシート内だけで作業するとうまく動くのです。つまり Sheets("作業中").Range(Cells(1, 1), Cells(a, 4)).Value = e の”Sheets("作業中").”の部分だけなくすと思い通りに動いてくれるのですが、データを作業中シートに返そうとするとたちまちエラーです。(涙)
お礼
ありがとうございます! なるほど!! 配列を使うのは今回が初めてだったのでとても勉強になります!! RedimをPreserveにしてループに取り込むといいんですね。 あと、cellsプロパティの件もよーく考えてみると、はっ!!と落とし穴に気づきました。今後気をつけていきたいと思います。 なぜCOPYメソッドやら基本的なコードを使わないようにしているかといいますと、データの量が増えてきて処理に時間がかかり始めたため、なんとかいい方法がないかと模索していた矢先、配列を使うと処理が軽くなることがわかったためです。 前はForループで空白セルを見つけたら直接他のシートにその時点で書き込ませるようにしていたのですが、かなり時間がかかるため、切って貼っての処理をなるべくさせないようにしたかったのです。 本当にありがとうございました。