Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


4509 / 13646 ツリー ←次へ | 前へ→

【56162】Index と Join の使い方 Abebobo 08/6/6(金) 17:07 質問[未読]
【56164】Re:Index と Join の使い方 kanabun 08/6/6(金) 17:57 発言[未読]
【56165】Re:Index と Join の使い方 kanabun 08/6/6(金) 18:05 発言[未読]
【56168】Re:Index と Join の使い方 Abebobo 08/6/6(金) 18:42 お礼[未読]
【56169】Re:Index と Join の使い方 n 08/6/6(金) 19:11 発言[未読]
【56170】Re:Index と Join の使い方 kanabun 08/6/6(金) 22:59 発言[未読]
【56177】Re:Index と Join の使い方 Abebobo 08/6/7(土) 13:09 お礼[未読]
【56219】Re:Index と Join の使い方 Abebobo 08/6/9(月) 10:16 お礼[未読]
【56220】Re:Index と Join の使い方 kanabun 08/6/9(月) 11:06 発言[未読]
【56222】Re:Index と Join の使い方 Abebobo 08/6/9(月) 11:58 お礼[未読]
【56227】Re:Index と Join の使い方 kanabun 08/6/9(月) 16:23 発言[未読]
【56228】Re:Index と Join の使い方 Abebobo 08/6/9(月) 17:08 お礼[未読]

【56162】Index と Join の使い方
質問  Abebobo  - 08/6/6(金) 17:07 -

引用なし
パスワード
   お願いします。
記事No.55989 で Join と Index の使い方をおぼえようと思いましたが若干 Index の使い方に自信がありません。

Sub 九九表準備()
Dim i As Long
 i = 1
 For i = 1 To 2
  With Sheets(i)
   .Range("B1:J1").FormulaR1C1 = "=Column()-1"
   .Range("A2:A10") = "=ROW()-1"
  End With
 Next i
 With Sheet2
  .Range("B2:J10").FormulaR1C1 = "=RC1*R1C"
  With .Range("A1").CurrentRegion
   .Copy
   .PasteSpecial Paste:=xlValues
   Application.CutCopyMode = False
  End With
 End With
End Sub

まず上記マクロで、Sheet2に九九表の答えをを作成します。
そして、手作業でSheet1に九九表を作ることが出来たら、テキストボックスでメッセージを2秒間出すマクロを作りました。

Private Sub Worksheet_Change(ByVal Target As Range)
Dim i   As Long
Dim v1, v2 As Variant
Dim x1 As String, x2 As String
Const j  As Long = 9
Dim Seien As Object

 v1 = Sheet1.Range("B2").Resize(j, j).Value '手作業の結果
 v2 = Sheet2.Range("B2").Resize(j, j).Value '答え
 For i = 1 To j
  x1 = x1 & Join(Application.Index(v1, i, 0)) & " "  'JoinでString型に収める
  x2 = x2 & Join(Application.Index(v2, i, 0)) & " "
 Next
 If x1 = x2 Then  '答えがあっていれば
  Set Seien = ActiveSheet.Shapes.AddTextbox _
      (msoTextOrientationHorizontal, 216#, 175.5, 216#, 123#)
  With Seien
   .Name = "Ab"
   .TextFrame.Characters.Text = "出来上がり"
  End With
  Me.Range("A1").Select
  Application.Wait Now + TimeValue("0:00:02")
  Seien.Delete
 End If

End Sub

結果には満足していますが、なにかご指摘がありましたら(何でも良いです)ご指導をお願いします。

【56164】Re:Index と Join の使い方
発言  kanabun  - 08/6/6(金) 17:57 -

引用なし
パスワード
   ▼Abebobo さん:
こんにちは

> For i = 1 To j
>  x1 = x1 & Join(Application.Index(v1, i, 0)) & " "  'JoinでString型に収める
>  x2 = x2 & Join(Application.Index(v2, i, 0)) & " "
> Next
> If x1 = x2 Then  '答えがあっていれば

> (何でも良いです)
とのことなので、ぼくなら

(1) まず対象範囲に数値がすべて入っているか、COUNT関数で調べます。

(2) すべてのセルに数値が入っていたら、
  行単位で答えが合っているか、チェックします。
  このとき、合っていない行があれば、その後の処理はしません。

 For i = 1 To K
  x1 = Join(Application.Index(v1, i, 0))  'JoinでString型に収める
  x2 = Join(Application.Index(v2, i, 0))
  If x1 <> x2 Then Exit Sub
 Next

PS.九九が苦手なので、途中で挫折しました(ToT)
どんなテキストボックスなのか 見てみたかったけど(^^

【56165】Re:Index と Join の使い方
発言  kanabun  - 08/6/6(金) 18:05 -

引用なし
パスワード
   あと、どんなふうに入力していくかですが、
列方向にLoopしてcheck してもいいですね
(配列に入れずに Range を使ってますが。)

 Set r1 = Me.Range("B2").Resize(K, K)  '手作業の結果
 Set r2 = Sheet2.Range("B2").Resize(K, K)  '答え
 For i = 1 To K
  S1 = Join(Application.Transpose(r1.Columns(i)))  'JoinでString型に収める
  S2 = Join(Application.Transpose(r2.Columns(i)))
  If S1 <> S2 Then Exit Sub
 Next

【56168】Re:Index と Join の使い方
お礼  Abebobo  - 08/6/6(金) 18:42 -

引用なし
パスワード
   kanabun さんありがとうございます。

>(1) まず対象範囲に数値がすべて入っているか、COUNT関数で調べます。
そのとおりですね。

>(2) すべてのセルに数値が入っていたら、
>  行単位で答えが合っているか、チェックします。
>  このとき、合っていない行があれば、その後の処理はしません。
納得です。

>Transpose
使ったことなかったです。実は、『こいつ存在する意味有るのか?』とまで思っていて調べてもいませんでした。 調べてみます。

これからコードを書くときに(1)(2)等のことも考えながら組みたいと思います。

>PS. ・・・どんなテキストボックスなのか 見てみたかったけど(^^
またまたぁ〜  ←ちょっとなれなれしいですか?

【56169】Re:Index と Join の使い方
発言  n  - 08/6/6(金) 19:11 -

引用なし
パスワード
   こんにちは。
私もkanabunさんと同意見で、Worksheet_Changeイベントを使うのなら、
最初に対象範囲をチェックしたほうが良いと思います。

以下、あまり良い例ではないかもしれませんが、一案として

Option Explicit

Const j As Long = 9
Dim x(1 To j) As String

Private Sub Worksheet_Change(ByVal Target As Range)
  Dim v As Variant
  Dim n As Long
  Dim i As Long
  
  With Range("B2").Resize(j, j)
    If Intersect(Target, .Cells) Is Nothing Then Exit Sub
    n = Target.Row - 1
    x(n) = Join(Application.Index(.Value, n))
    If WorksheetFunction.CountBlank(.Cells) > 0 Then Exit Sub
    v = Sheet2.Range("B2").Resize(j, j).Value
    For i = 1 To j
      If x(i) <> Join(Application.Index(v, i, 0)) Then Exit For
    Next
  End With
  If i = j + 1 Then
    Range("A1").Select
    With Me.TextBoxes.Add(216, 175.5, 216, 123)
      .Text = "出来上がり"
      DoEvents
      Application.Wait Now + TimeValue("0:00:02")
      .Delete
    End With
  End If
End Sub

今回のような例示なら、やはりDataObjectを使って、
文字列全体でまとめてチェックすれば良いような気もしますね。

データ数が少ないなら、お遊びで
MsgBox Evaluate("AND(Sheet1!B2:J10=Sheet2!B2:J10)")

【56170】Re:Index と Join の使い方
発言  kanabun  - 08/6/6(金) 22:59 -

引用なし
パスワード
   ▼Abebobo さん:
こんばんは。

>>Transpose
>使ったことなかったです。実は、『こいつ存在する意味有るのか?』とまで思っていて調べてもいませんでした。 調べてみます。
>
セルの行データを Joinして文字列を作り出すために Application.Index、
また セルの列データを Application.Transpose を使いますが、
Indexと Transpose でどのくらいスピードがちがうのか、ためしに実験して
みました。

'ワークシートの 250*250 の範囲に 数値データを作成します
Sub 準備()
 Dim v(1 To 250, 1 To 250)
 Dim i&, j&
 
 For i = 1 To 250
  For j = 1 To 250
   v(i, j) = Int(250 * Rnd + 1)
  Next
 Next
 Worksheets("Sheet3").Range("A1").Resize(250, 250).Value = v
End Sub

'これを Index関数で行単位で Joinして時間を計ってみました。
Sub Test1()
 Dim ss$
 Dim i&
 Dim r As Range
 Dim t!
 
 t = Timer
 Set r = Worksheets("Sheet3").Range("A1").Resize(250, 250)
 For i = 1 To 250
   'ss = Join(Application.Index(r, i))
   ss = Join(Application.Index(r.Value, i))
 Next
 Debug.Print "'Range Index "; Timer - t; """"
 Beep
End Sub
'結果
'Range Index 9.640625 "
'Range Index 9.585938 "
'Range Index 9.585938 " ------------ とても時間がかかっています

遅いのは、Index は配列として処理しないといけないことも要因のようです。
Rangeに対してIndexがかけれない。

つぎに、列単位で Transposeして join してみました。
Sub Test2()
 Dim ss$
 Dim i&
 Dim r As Range
 Dim t!
 t = Timer
 Set r = Worksheets("Sheet3").Range("A1").Resize(250, 250)
 For i = 1 To 250
   ss = Join(Application.Transpose(r.Columns(i)))
 Next
 Debug.Print "'Range Transpose "; Timer - t; """"
 Beep
End Sub
'結果
'Range Transpose 0.078125 "
'Range Transpose 0.15625 "
'Range Transpose 0.09375 " '---- Transpose はIndexと比べるとかなり高速

そこで、行単位のデータをTransposeを2回使うことで1次元配列にして、
Joinしてみました。

Sub Test22()
 Dim ss$
 Dim i&
 Dim r As Range
 Dim t!
 
 t = Timer
 Set r = Worksheets("Sheet3").Range("A1").Resize(250, 250)
 With Application
   For i = 1 To 250
     ss = Join(.Transpose(.Transpose(r.Rows(i))))
   Next
 End With
 Debug.Print "'Range Transpose^2 "; Timer - t; """"
 Beep
End Sub
'結果
'Range Transpose^2 0.1796875 "
'Range Transpose^2 0.1640625 "
'Range Transpose^2 0.1484375 "

見られるように、行単位のデータをJoinするにも Index関数よりも、
Transposeを2回使ったほうがはるかに速いことが分かりました。

【56177】Re:Index と Join の使い方
お礼  Abebobo  - 08/6/7(土) 13:09 -

引用なし
パスワード
   nさん kanabun さん
ありがとうございます。

ものすごく興味深いサンプルをありがとうございます。
しっかり理解したいと思います。

【56219】Re:Index と Join の使い方
お礼  Abebobo  - 08/6/9(月) 10:16 -

引用なし
パスワード
   kanabunさん nさん おはようございます。

 nさん
>あまり良い例ではないかもしれませんが、一案として
のコード試しました。
 2の段まで入力して後はフィルコピーした時は発動しませんでしたが、なるほど!です。。恥ずかしい話ですが、ローカルで x を探しちゃいました。Meの中にあるんですね。(*^_^*)
  Evaluate なんか名前の定義みたいに使えるようで…。使っていって憶えて行こうと思います。

 Kanabunさん
ss$ = Variant   j& = Long で良いんですよね?
実は、初めてみました。Helpにも載ってない?ですよね。

Sub Test1() は 250のままだと エラー13 型が一致しません でした。
私のPC では、74が限界なのかな? (エクセル2000)ヴァージョンの問題? 256M の問題?
 
Test2() ・ Test22() はちゃんと動きました。

Transpose ものすごい勘違いしていました。4年間位!!
Vlookupを覚えた頃、検索値より表の左を求めたくて(Index&Matchのように)色々ヘルプを探していて 『セル範囲の変換を行います。』 を 『セル範囲の入れ替えを行います。』だと勘違いして Transpose とずい分格闘した記憶があります。ここで出来なかったので、Transposeにちょっとした憎悪感が生まれました。

Transpose 反復練習で使えるようにします。

お二人とも丁寧なサンプルをありがとうございました。
もっと精進します。

【56220】Re:Index と Join の使い方
発言  kanabun  - 08/6/9(月) 11:06 -

引用なし
パスワード
   ▼Abebobo さん:
おはようございます。

>ss$ = Variant   j& = Long で良いんですよね?
>実は、初めてみました。Helpにも載ってない?ですよね。

Dim ss$ は、
Dim ss As String
と同じです。
変数名のお尻の $や& は「型宣言文字」と呼ばれるもので、
ヘルプにも載ってたと思います

型宣言文字
h t t p://xls.seesaa.net/article/19546212.html

【56222】Re:Index と Join の使い方
お礼  Abebobo  - 08/6/9(月) 11:58 -

引用なし
パスワード
   kanabun さんありがとうございます。

>h t t p://xls.seesaa.net/article/19546212.html
残念ながら会社の環境からは、ブロックがかかっていいるようでした。


型宣言文字 で検索して ↓
://www.sigoto.co.jp/excel/base/hensuu04.htm
で解りました。

あれからもHelp見てるんですけど…。

【56227】Re:Index と Join の使い方
発言  kanabun  - 08/6/9(月) 16:23 -

引用なし
パスワード
   ▼Abebobo さん:
>Sub Test1() は 250のままだと エラー13 型が一致しません でした。
>私のPC では、74が限界なのかな? (エクセル2000)ヴァージョンの問題? 256M の問題?

ぼくも Excel2000と Excel2002 で Test1() やってみました。
(メモリは Abebobo さんと同じく 256M)

2002では何事もなく動きましたが(30秒強かかりました)、
Excel2000 では、"Type Mismatch"となりストップしました。

調べたら、Excel2000 までは Index 関数は
要素数が 5461 個までの配列にしか使えないと、ありました。
ですから、 74×74 = 5476 で、まだ大きいので、 73×73 にしてようやく
動きました。

【56228】Re:Index と Join の使い方
お礼  Abebobo  - 08/6/9(月) 17:08 -

引用なし
パスワード
   kanabun さん
すみません。私も74は × でした。
色々調べていただきありがとうございました。勉強になります。

>調べたら、Excel2000 までは Index 関数は
>要素数が 5461 個までの配列にしか使えないと、ありました。
私の場合、どこをどう調べたら解るのやら…。
会社では、ところどころブロックが掛かるし、家では子供たちが私をブロックするし、エクセル川柳が面白くて途中で検索忘れちゃうし…。

kanabun さん nさん 本当にありがとうございました。

4509 / 13646 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free