[[20170722103510]] 『vba Findの使い方 見つからなかったときの返り値』(tata) ページの最後に飛ぶ

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

 

『vba Findの使い方 見つからなかったときの返り値』(tata)

お世話になります。

Findメソッドの正しい使い方を教えていただけませんでしょうか。

以下のような元データがあり、
F列に、 特定の名前「い」が有るかどうかを調べるため

 Sub 個別テスト()
 Dim myRng As Range
 Set myRng = Range("F:F").Find("い")
 MsgBox (myRng)
 End Sub

という形を考えてみました。

見つからなかった場合、Nothing が返ると思っていたのですが、
"個人名(F1セル)"が返っているようです。

どういう間違いから来るものでしょうか?
正しく記述するにはどのようにすれば良いのでしょうか?

よろしくお願いします。

    |[A]       |[B] |[C] |[D]|[E]|[F]   |[G] |[H] |[I] |[J] |[K] |[L] |[M] 
 [1]|名前入力欄|月  |件数|   |   |個人名|1月|2月|3月|4月|5月|6月|7月
 [2]|い        |3月|   4|   |   |あ    |    |    |    |    |    |    |    
 [3]|あ        |3月|   6|   |   |      |    |    |    |    |    |    |    
 [4]|う        |3月|   8|   |   |      |    |    |    |    |    |    |    
 [5]|あ        |4月|   2|   |   |      |    |    |    |    |    |    |    
 [6]|い        |4月|   9|   |   |      |    |    |    |    |    |    |    
 [7]|か        |4月|   7|   |   |      |    |    |    |    |    |    |    
 [8]|う        |7月|   3|   |   |      |    |    |    |    |    |    |    
 [9]|あ        |7月|   5|   |   |      |    |    |    |    |    |    |    

以下、個別の話ではなく、実際にやりたいこと全体
A列からC列の元データについて
F列に名前があればその行に
F列に名前がなければ一番下の行に
その月の件数を転記

 Sub Macro1()
 Dim i As Long '書き込み先行
 Dim j As Long '書き込み先列
 Dim k As Long '読み込み元行
 Dim myRng As Range  '名前検索結果格納用

 For k = 2 To Cells(Rows.Count, 1).End(xlUp).Row         '2行目からA列の最終行まで繰り返し

    j = Range("G1:Q1").Find(Cells(k, 2).Value).Column   'k行目2列(B列)セルと一致する月は何行目か調べる

    '----ここがおかしい?----
    Set myRng = Range("F:F").Find(Cells(k, 1).Value)    'K行目1列(A列)セルと一致する名前が
    '----A2セルの"い"をF列で検索して,返る結果がNothingではなく"個人名"になる"----

    If myRng Is Nothing Then                            '存在しなければ
        i = Cells(Rows.Count, 6).End(xlUp).Row + 1      '6列目(F列)の一番下の行をiとする
        Cells(i, 6).Value = Cells(k, 1).Value           'i行6列(F列)目に、k行目1列(Akセル)の値、件数を書き込む
        Cells(i, j).Value = Cells(k, 3).Value           'i行j列目に、k行目3列(Ckセル)の値、件数を書き込む
    Else                                                '存在すれば
        i = myRng.Row                                   '一致した行に
        Cells(i, j).Value = Cells(k, 3)                 'k行目3列の件数を書き込む
    End If
 Next                                                    '次の書き込み元列へ
 End Sub

投稿
[[20170716125509]] 『毎月の実績を一つの入力欄から自動入力』(初心者gk) 
について...
からの延長自習

< 使用 Excel:Excel2007、使用 OS:Windows10 >


 >   '----ここがおかしい?----
 >   Set myRng = Range("F:F").Find(Cells(k, 1).Value)    'K行目1列(A列)セルと一致する名前が
 >   '----A2セルの"い"をF列で検索して,返る結果がNothingではなく"個人名"になる"----

 マクロ記録したほうがいいと思いますよ。
 Find の書き方を誰に教わったのか知らないけど、格好つけない方が良いよ。
 ヘルプをもっと読みましょう。
(BJ) 2017/07/22(土) 11:14

わかりました。
マクロ記録試してみます。
(tata) 2017/07/22(土) 11:17

 ああ〜。漢字かぁ。

 すみません。
 漢字の読みを検索しちゃうのは、ちょっと不明で・・・・。
 対処法は、今のところよく解りません。
 見つかったセルの内容と、ひらがなを再比較するぐらいしか。
 2007以降では、どうなっているのか解りません。
(BJ) 2017/07/22(土) 11:45

 私は再現できませんでした。(2013)

 >漢字の読みを検索しちゃうのは
 これ初めて知りました

 Findメソッドは引数を明確にしないと前回検索時の引数を受け継ぐと思いましたので、
 引数はすべて明確にしておいたほうがいいとは思います。

 あとはCells(k, 1)とありますが、コメントでわざわざ(A列)とか説明されるなら
 Cells(k, "A")と書いたほうが可読性が良いかと!!

(稲葉) 2017/07/22(土) 12:00


マクロ記録結果
 Sub 検索2()
    Columns("F:F").Select
    Selection.Find(What:="い", After:=ActiveCell, LookIn:=xlFormulas, LookAt _
        :=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
        True, SearchFormat:=False).Activate
 End Sub

このマクロを実行すると、
F列に「い」が存在する時は、そのセルがアクティブに。

F列に「い」が存在しない時は、そのセルがアクティブに。
オブジェクト変数または With ブロック変数が設定されていません。(Error 91)
が発生しました。

このエラーは、Nothing に設定されているオブジェクト変数を使用しようとした から発生したと考えれば良いんでしょうか?

 Find 結果の Nothing を.Avtivateしようとした?

ということで、
以下のコレで間違ってないでしょうか。
Sub 検索4()
Dim myRng As Range

Set myRng = Columns("F:F").Find(What:="い", After:=ActiveCell, LookIn:=xlValues, LookAt:= _

        xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True _
        , SearchFormat:=False)

    If myRng Is Nothing Then
    MsgBox ("Nothingになりました")
    Else
    MsgBox (myRng)
    End If
End Sub

(tata) 2017/07/22(土) 12:07


BJさん
 >漢字の読みを検索しちゃう
 なるほど、そういうことも有るんですね。

稲葉さん

 >Findメソッドは引数を明確にしないと前回検索時の引数を受け継ぐと思いましたので、
 >引数はすべて明確にしておいたほうがいいとは思います。
 心がけます。

 >あとはCells(k, 1)とありますが、コメントでわざわざ(A列)とか説明されるなら
 >Cells(k, "A")と書いたほうが可読性が良いかと!!
 読みにくくてごめんなさい!
 そういう書き方出来たんですね。
(tata) 2017/07/22(土) 12:15

 >(tata) 2017/07/22(土) 12:07
 それだと、F列以外を選択状態で実行するとエラーになりません?

 AfterのところをF列の中に収めてあげるか、
 最初にF列をActivateするか
 引数を省略するかしたほうがいいかもしれません。

(稲葉) 2017/07/22(土) 12:17


 >それだと、F列以外を選択状態で実行するとエラーになりません?
 はい、なりました。

 >AfterのところをF列の中に収めてあげる
 こういうことでしょうか。

 Sub 検索5()
 Dim myRng As Range

 Set myRng = Columns("F:F").Find(What:="い", After:=Range("F1"), LookIn:=xlValues, LookAt:= _
        xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True _
        , SearchFormat:=False)

    If myRng Is Nothing Then
    MsgBox ("Nothingになりました")
    Else
    MsgBox (myRng)
    End If
 End Sub
(tata) 2017/07/22(土) 12:30

お教えいただいたことを元に、以下のような形で、想定した物ができました。
ありがとうございました。

 Sub Macro1()

 Dim i As Long '書き込み先行
 Dim j As Long '書き込み先列
 Dim k As Long '読み込み元行
 Dim myRng As Range '名前検索結果格納用

 For k = 2 To Cells(Rows.Count, "A").End(xlUp).Row  '2行目からA列の最終行まで繰り返し
                                                      'k行目B列セルと一致する月は何行目か調べる
    j = Range("G1:Q1").Find(What:=Cells(k, "B").Value, After:=Range("G1"), LookIn:=xlValues, LookAt:= _
        xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True _
        , SearchFormat:=False).Column
                                                        'k行目A列セルと一致する名前がF列に有るか調べる
    Set myRng = Columns("F:F").Find(What:=Cells(k, "A").Value, After:=Range("F1"), LookIn:=xlValues, LookAt:= _
        xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True _
        , SearchFormat:=False)

    If myRng Is Nothing Then                            '存在しなければ
        i = Cells(Rows.Count, "F").End(xlUp).Row + 1        'F列の一番下の行をiとする
        Cells(i, "F").Value = Cells(k, "A").Value           'i行F列目に、k行目A列の値、件数を書き込む
        Cells(i, j).Value = Cells(k, "C").Value             'i行j列目に、k行目C列の値、件数を書き込む
    Else                                                '存在すれば
        i = myRng.Row                                       '一致した行に
        Cells(i, j).Value = Cells(k, "C")                   'k行目C列の件数を書き込む
    End If
 Next                                               '次の書き込み元列へ
 End Sub
(tata) 2017/07/22(土) 12:36

 できたみたいで良かったです。

 説明用に作りかけたもの置いておきますね。
    Sub tata1()
        Range("F2:M" & Rows.Count).ClearContents                        '値の削除
        Range("A2", Cells(Rows.Count, "A").End(xlUp)).Copy Range("F2")  'A2以降の名前をF2以降にコピー
        Range("F:F").RemoveDuplicates Columns:=1, Header:=xlYes         '重複の削除
        Dim F1 As String
        Dim Fn As Range
        Dim r As Range
        Dim WSF As WorksheetFunction
        Set WSF = Application.WorksheetFunction
        For Each r In Range("F2", Cells(Rows.Count, "F").End(xlUp))
            With Range("A1", Cells(Rows.Count, "A").End(xlUp))
                Set Fn = .Find(what:=r.Value, LookIn:=xlValues, Lookat:=xlWhole)
                If Not Fn Is Nothing Then
                    F1 = Fn.Address
                    Do
                        r.Offset(, WSF.Match(Fn.Offset(, 1).Value, Range("F1:M1"), False)).Value = Fn.Offset(0, 2).Value
                        Set Fn = .FindNext(Fn)
                        If Fn Is Nothing Then Exit Do
                        If Fn.Address = F1 Then Exit Do
                    Loop
                End If
            End With
        Next r
    End Sub
(稲葉) 2017/07/22(土) 12:53

 おまけ
http://www.relief.jp/docs/excel-vba-msgbox-function-parenthesis.html
(BJ) 2017/07/22(土) 13:14

稲葉さん

ありがとうございます。

For Each 〜 Nextで、F列について順に処理していく

A列の範囲について検索、F列と一致した人にについてセル番地(A4とか)を取得、Fn格納

Match関数でFnの1個右のセル番地、つまり○月と一致するのをF1〜M1で検索。
△列目、の情報が返るので、F列+△列右にOffsetでずらした位置に、件数を代入

FindNextで、次の一致を探し、処理

A列を順番にF列検索処理していくのではなく、
F列を順番にA列検索処理しているんですね。

BJさん
構文の原則のお話、ありがとうございます。

 戻り値を使う場合、カッコが必要
 戻り値を使わない場合、カッコは不要
なのですね。
(tata) 2017/07/22(土) 13:35

 解決済みですが、引用元が気になりまして。
 質問者 >記入しわすれてましたがおおよその件数は300〜350程度です。 
 tataさん>月あたり300件分を私の提示したマクロで実施するなんて、本当に「おすすめしない」ですよ?
 とそれぞれ回答ありましたが、tataさんとしてはFindメソッドで「完成」なんですか?

 私が提示したものもそうですが、月300件×12ヶ月を一括で処理すると、だいぶ時間掛かりますよね。

 すでにご存知ならスルーしていただいてよいのですが、今回のケースは
 1)配列で一括出力
 2)連想配列で重複チェック
 を使うと、格段に早くなると思います。

 よい機会ですので、一緒にやってみませんか?
(稲葉) 2017/07/22(土) 16:15

 >Findメソッドで「完成」なんですか?
とりあえ使い方の勉強だったのですが、さらに勉強付き合っていただけるんですか。ありがたいです。

 >すでにご存知なら
  >配列で一括
  →「配列」を実際に自分で活用したことはありません

  >連想配列で重複チェック
  →連想配列、という単語すら知らなかった

  という知識量です。

 お付き合いのほどよろしくお願いします。

 しばらく連想配列について検索しながら返事お待ちしています。
(tata) 2017/07/22(土) 16:28

 配列、の使い方ってこんな感じでしょうか?

 Sub 演習_配列を作る()

 Dim myArray(400) As Variant   '大きさ400の配列を宣言

 myArray(1) = Cells(2, "A").Value
 myArray(2) = Cells(3, "A").Value
 myArray(3) = Cells(4, "A").Value

 MsgBox myArray(1)
 MsgBox myArray(3)
 MsgBox myArray(2)

 End Sub

(tata) 2017/07/22(土) 16:43


 私もここで教えてもらったので、いつかやる気のある人へ恩返しがしたいと思っていました。

 本題ですが、まずは下記のコードをそれぞれ実行して、違いを実感してみてください。

    Sub test1()
        Dim i As Long
        For i = 1 To 10000
            Cells(i, "A").Value = i
        Next i
        MsgBox "完了"
    End Sub

    Sub test2()
        Dim 配列 As Variant
        Dim i    As Long
        ReDim 配列(1 To 10000, 1 To 1)
        For i = 1 To 10000
            配列(i, 1) = i
        Next i
        Range("A1:A10000") = 配列
        MsgBox "完了"
    End Sub

 >Range("A1:A10000") = 配列
 配列にして一気に出力すると早いことが分かると思います。

 ですので、計算結果を直にセルへ入れるのではなく、一度配列に取り込んでから吐き出す形を
 イメージしてみましょう。

 次に、配列はシートからまとめて取得することもできます。
    Sub test3()
        Dim i As Long
        Dim n As Long
        For i = 1 To 10000
            n = n + Cells(i, "A")
        Next i
        MsgBox n
    End Sub
    Sub test4()
        Dim i As Long
        Dim n As Long
        Dim 配列 As Variant
        配列 = Range("A1:A10000").Value
        For i = 1 To 10000
            n = n + 配列(i, 1)
        Next i
        MsgBox n
    End Sub

 これは違いをほとんど感じられないと思いますが、シートを配列に取り込むと、Cellsのように使える
 という説明です。
 Cellsと違い、Cells(1, "A")など列を文字で指定することはできません。
 また取り込んだ範囲の左上が(1, 1)になるので、(何も修飾しない)Cellsと結果が異なります。
 イミディエイトウィンドウを開いて、ステップ実行してください。
    Sub test5()
        Range("B2:F6").Formula = "=ADDRESS(COLUMN(B2),ROW(B2),4)"
        Dim 配列 As Variant
        Dim i    As Long
        Dim j    As Long
        配列 = Range("B2:F6").Value
        Stop
        For i = 1 To 5
            For j = 1 To 5
                Debug.Print "配列=" & 配列(i, j) & "セル=" & Cells(i, j).Address(0, 0)
            Next j
        Next i
    End Sub

 Cellsも以下のような使い方をすると、範囲の左上のセルが(1, 1)になりますが、とりあえず置いておきましょう。
    Sub test6()
        MsgBox Range("B2:F6").Cells(1, 1).Address(0, 0)
    End Sub

 まとめると、今回の目標は
    Sub 目標()
        Dim 配列 As Variant
        '色々処理して
        Range("F2:R2").Resize(X).Value = 配列 '出力する
    End Sub

 の形を一緒に作っていきましょう。
 次回連想配列について説明します。

(稲葉) 2017/07/22(土) 17:26


 >1)配列で一括出力
 >を使うと、格段に早くなる

 配列に個々の値を格納してから、
 一回で貼り付け、ということでしょうか。こんな感じでしょうか?

Sub 演習2_2次元配列一括貼り付け()

 Dim myArray(99, 12) As Variant  '大きさ100行13列の配列を宣言

 myArray(0, 0) = Cells(2, "A").Value
 myArray(1, 0) = Cells(3, "A").Value
 myArray(2, 0) = Cells(4, "A").Value

 myArray(0, 3) = Cells(2, "C").Value
 myArray(1, 3) = Cells(3, "C").Value
 myArray(2, 3) = Cells(4, "C").Value

 Cells(2, "F").Resize(99, 12).Value = myArray

 End Sub

(tata) 2017/07/22(土) 17:32


書いてる間にお返事いただいていました。
提示いただいたもの見てみます。
(tata) 2017/07/22(土) 17:33

 2次元配列の感覚がなんとなくわかりました。

    Sub test5の2()
        Range("B2:F6").Formula = "=ADDRESS(ROW(B2),COLUMN(B2),4)" '←セル番地とセル内値一致するよう変更してみました
        Dim 配列 As Variant
        Dim i    As Long
        Dim j    As Long
        配列 = Range("B2:F6").Value
        Stop
        For i = 1 To 5
            For j = 1 To 5
                Debug.Print "2次元配列の" & i & "行" & j & "列に入っている値は = " & 配列(i, j) & "; セルのi,j番地のように使えるが、取り込んだ始点B2が(1,1)になるので、Cells(" & i & "," & j & ")とは違う。Cells(" & i & "," & j & ") = " & Cells(i, j).Address(0, 0); ""
            Next j
        Next i
    End Sub

 以下のモノは
 Dim 配列 As Variant '←とりあえず配列の大きさは宣言しなくても良い
 配列 = Range("B2:F6").Value 'こういう代入をすると、(1,1)から値が入っていく。(0,0)からではない。
 という理解であっていますでしょうか?

(tata) 2017/07/22(土) 18:22


 大雑把に説明してるので、感覚で平気です。
 今動的配列とか静的配列とか言われてもわからないと思うので
 トライアンドエラーでいきましょう

 ここまで説明した内容で、処理するデータの取り込みと、
 出力用の配列用の器を作ってみてください
(稲葉) 2017/07/22(土) 18:38

 >説明した内容で、処理するデータの取り込みと
 いきなり、出力する形の配列に取り込んでいく、というわけではなく、
 単純にA列からC列の元データを取り込む、という意味でしょうか?

 >出力用の配列用の器を作ってみてください
 つまり、取り込んだ配列と、出力用配列とを、2つ作る、と?

     Sub test101()
        Dim 元データ配列 As Variant
        Dim 出力配列 As Variant

        元データ配列 = Range("A2", Cells(Rows.Count, "C").End(xlUp)).Value

        '格納できてるかF10セルに書き出しチェック
        'Cells(10, "F").Resize(15, 3).Value = 元データ配列
     End Sub

こうでしょうか?
(tata) 2017/07/22(土) 19:15


 そうですね。
 器のサイズは後で決めるので、とりあえずこのままでいきましょう。

 次に連想配列について説明します。
 配列は 配列(1) 配列(2) のようにインデックスでしかアクセスできませんが、
 連想配列はキーとアイテムをそれぞれ登録して、キーを渡すことでアイテムを取り出す配列です。
 私が知っている中ではDictionaryやArrayListがよく使われますので、Dictionaryについて説明します。

 例えば キー=田中さん に アイテム=20歳 と登録し、呼び出すときに
 田中さんと指定すれば、20歳と答えてくれるのが連想配列の一般的な使い方です。
 また、キーの重複が許されていません。
 test7を実行すると、途中でエラーになります。
 test8は重複を事前に調べてから処理を分岐しています。

    Sub test7()
        Dim dic As Object
        Dim r As Range
        Set dic = CreateObject("Scripting.Dictionary")
        For Each r In Range("A2:A9")
            dic.Add Key:=r.Value, Item:=r.Row
        Next r
        Set dic = Nothing
    End Sub
    Sub test8()
        Dim dic As Object
        Dim r As Range
        Set dic = CreateObject("Scripting.DIctionary")
        For Each r In Range("A2:A9")
            If dic.exists(r.Value) Then
                'すでに登録されていた場合の処理
            Else
                dic.Add Key:=r.Value, Item:=r.Row
            End If
        Next r
        MsgBox "「う」が最初に出現した行は" & dic.Item("う") & "行目です"
        Set dic = Nothing
    End Sub

 あえてAddメソッドと名前付き引数を使用していますが、それぞれ省略することができます。

 では今回の元データ配列をループ処理して、「か」が最初に出現した行をmsgboxで表示させてください。
 次回、省略の仕方、出力配列の器のサイズ指定を説明します。

 次は月曜日になるかも?
(稲葉) 2017/07/22(土) 22:27

ども。横入りすみません。

遅ればせながら考えてみたので書きます。
今回の場合はFindでどうのこうのしなくても、
「フィルターオプション」の機能で表側の項目は容易に作れると思います。
(フィルターオプションの機能は、重複の削除+コピペの機能がついてます。)
表頭はオートフィルで容易に作れますよね?

あとは、手動でやる流れで、
行毎にみて、表側・表頭の項目をみて書き込むセルを確定させてやればOKですよね?
その時はワークシートの関数のMatch関数で位置を検索してやります。

せっかくエクセルを使っているのだから、エクセルに任せられるところは任せて、
マクロ化(作業の自動化)を行うと、比較的少ない勉強量で、開発が楽にできると思います。

フィルターオプションやオートフィルターは、マクロの記録で、
コードを探れますよね?

Sub test()

    Dim rngList As Range    'リストのセル範囲
    Dim rngTable As Range   'クロス集計表のセル範囲
    Dim ixRow As Long       '行番号(相対位置)
    Dim ixCol As Long       '列番号(相対位置)
    Dim c As Range          'リストの見ていく列の各セル

    '各表のセルの位置の定義
    Set rngList = Range("A1").CurrentRegion
    Set rngTable = Range("F1")

    'フィルターオプションで表側を用意
    rngList.Columns(1).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=rngTable, Unique:=True
    'オートフィルターで表頭を用意
    With rngTable.Resize(, 12).Offset(, 1)
        .Cells(1).Value = "1月"
        .Cells(1).AutoFill Destination:=.Cells, Type:=xlFillMonths
    End With
    'クロス集計表のセル範囲を再定義
    With rngTable.CurrentRegion
        Set rngTable = Intersect(.Cells, .Offset(1, 1))
    End With

    'リストを行毎にみていって(1行目を除く)
    For Each c In Intersect(rngList, rngList.Offset(1)).Rows
        '書き込み位置をMatch関数で検索
        With WorksheetFunction
            ixRow = .Match(c.Cells(1).Value, rngTable.Columns(0), 0)
            ixCol = .Match(c.Cells(2).Value, rngTable.Rows(0), 0)
        End With
        'クロス集計表に転記
        rngTable(ixRow, ixCol).Value = c.Cells(3).Value
    Next
End Sub

どうやってセル範囲の位置を表現するかを、
とりあえず一番最初に勉強しないとダメなんじゃないですかね。。。
(まっつわん) 2017/07/23(日) 00:31


編集の機能どこいったんですかね〜。。。

ま、いっか。

訂正>>
>どうやってセル範囲の位置を表現するかを、
>とりあえず一番最初に勉強しないとダメなんじゃないですかね。。。
どうやってセル範囲の位置を表現するかを、
とりあえず一番最初に勉強するといいと思います。
(まっつわん) 2017/07/23(日) 00:35


[[20170630183428]] 『[談]編集ボタンの使用について』(稲葉)

(稲葉) 2017/07/23(日) 05:53


すみません、重複の確認の処理でつまづきました。

            If dic.exists(r.Value) Then
 を、配列で行う正しい方法がわかりません。
 ご教示願います。

    Sub test8例示からほとんど変えず試行()
        Dim 元データ配列 As Variant

        Dim dic As Object
        Dim r As Variant    '各要素を受け取るデータ型はvariant?
        Set dic = CreateObject("Scripting.DIctionary")

        元データ配列 = Range("A2", Cells(Rows.Count, "C").End(xlUp)).Value

        For Each r In 元データ配列 'とすると、B列、C列の要素も処理になってしまう?

        '---ここからわからない---
            If dic.exists(r.Value) Then 'このままだと「オブジェクトが必要です424」エラー。オブジェクト修飾子?
             'オブジェクトとは、vbaで何か操作をしようとする対象http://www.vba-ie.net/object/index.html
            'Dictionaryオブジェクトを操作しようとしている?
            'If Range.dic.exists(r.Value) Then '配列の「範囲」が対象?違うよな…?引数足りない言われるし
        '---根本的に間違ってる?For Each Next で回さず、For Nextで回す?---

                'すでに登録されていた場合の処理
            Else
                dic.Add Key:=r.Value, Item:=r.Row
            End If
        Next r
        MsgBox "「か」が最初に出現した行は" & dic.Item("か") & "行目です"
        Set dic = Nothing
    End Sub

    Sub test8例示から繰り返し処理を変更()
        Dim 元データ配列 As Variant

        Dim 出力配列 As Object
        Set 出力配列 = CreateObject("Scripting.Dictionary")

        Dim k As Long '元データ配列の行
        Dim m As Long '元データ配列の末尾行数を数える

        元データ配列 = Range("A2", Cells(Rows.Count, "C").End(xlUp)).Value
        m = UBound(元データ配列, 1)

        For k = 1 To m  '元データ1行から末尾まで処理

            '---結局ここで同じように躓く
            If 出力配列.exists(元データ配列(k, 1).Value) Then
            '------

                            'すでに登録されていた場合の処理
            Else
                出力配列.Add Key:=元データ配列(k, 1).Value, Item:=元データ配列(k, 1).Row
            End If
        MsgBox "「か」が最初に出現した行は" & 出力配列.Item("か") & "行目です"
        Set 出力配列 = Nothing

        Next k
    End Sub
(tata) 2017/07/23(日) 08:25

まっつわん さん コメントありがとうございます
ゆっくり見させていただきます。
(tata) 2017/07/23(日) 08:27

 私の説明が悪かったですね。
 最終的に、1)元データ配列
      2)Dictionary
      3)出力配列
 の3つで説明していこうと考えていました。
 混乱させてしまい、すみません。

 >    Sub test8例示からほとんど変えず試行()
 で説明でします。

 >If dic.exists(r.Value) Then 'このままだと「オブジェクトが必要です424」エラー。オブジェクト修飾子?
 このエラーは、rの値を見てください。
 表示>ローカルウィンドウを開いてステップ実行を行い、エラーの時 r の値がどうなっているか確認してください。
 "い" Varinat/Stringとなっているはずです。

 配列に渡しているのは「値だけ」なので、 い.Value って言われても、"オブジェクトが必要です"って怒られるわけです。

 >If 出力配列.exists(元データ配列(k, 1).Value) Then
 ここも同じです。
 配列に渡しているのはValueなので、Value.Valueはおかしいですよね。
 今回の配列の場合は、Valueプロパティは必要ありません。

 おいおい分かっていくと思うので、形を作ってあとで覚えていきましょう!

 行番号と言いましたが、難しそうなので、最初に出現した月で行きましょう!

(稲葉) 2017/07/23(日) 08:51


稲葉さんへ>>

ども^^
ちらっと見ただけです。

If dic.exists(r.Value) Then 'このままだと「オブジェクトが必要です424」エラー。オブジェクト修飾子?

rはRangeオブジェクトでなくただのVariant型の値だから、
Valueプロパティは使えません。(エラーの意味はValueプロパティを使うなら
プロパティを持っているオブジェクトが必要ですの意)

If dic.exists(r) Then

かな?ディクショナリーは意味わからんから使ってないので自信なしですです。
(すごく便利なのは解ってますが、敢えて標準外のプログラムを使う必要に出会わないので^^;)

というか、質問と関係ない、個人の質問は別スレ立てるべきでは?
書き込みが長くなり何が「今」困ってらっしゃるかわかりにくくなります。
別スレで勉強して、その成果をここに書かれたらいいかなと、
質問の仕方、試行錯誤をされてる姿をリンクで示されたら、
それはそれで見せてもらうのも勉強になるかなと思います。
あと、2次元配列も、シートそのものが2次元配列を視覚化したものになってます。
まずは、シート上で操作がうまくできるようになれば自然に2次元配列は扱えるようになると思います。

[[20170630183428]] 『[談]編集ボタンの使用について』(稲葉)
ちらっと見ましたが意味が分かりません。
これも質問と関係ないのでこれくらいで、終わりにしましょう。
(無機質な回答以外にちょいちょい愚痴とか入れたら少しは場が和らぐかなと思って書いてるだけですで、
特に反応しなくても、「そう思う人もいる」という認識ぐらいでお願いします。)

(まっつわん) 2017/07/23(日) 08:52


 >今回の場合はFindでどうのこうのしなくても、 
 >「フィルターオプション」の機能で表側の項目は容易に作れると思います。 
 >(フィルターオプションの機能は、重複の削除+コピペの機能がついてます。) 
 >表頭はオートフィルで容易に作れますよね? 
 なるほど、フィルターオプション機能は忘れがちです。留意します。

 今回の大本の質問者さん、
  7月までのデータはクロス集計表に手作業で入力済み
  7月までの元データは存在せず、8月からのデータを楽に処理したい
 だったため、フィルターオプションで重複削除すると、入力済みのものが
 上書きされてしまうので、上手いやり方が思いつきませんでした。
 何かそういった場合の良いやり方ありますでしょうか?

 >その時はワークシートの関数のMatch関数で位置を検索してやります。 
 >エクセルに任せられるところは任せて
 (稲葉)さん 2017/07/22(土) 12:53 例示でも使われてますが、
 お恥ずかしながら、全く思いつきませんでした。
 ワークシート関数使う方が楽、速い、ですね…

 >どうやってセル範囲の位置を表現するかを、 
 >とりあえず一番最初に勉強するといいと思います。
 リスト範囲と相対位置、offset、resizeの活用、でしょうか。
 頑張ってみます。

すみません、以下の記述がよくわからなかったのですが、ご解説いただけませんか。

        Set rngTable = Intersect(.Cells, .Offset(1, 1)) '式.Intersect 複数のセル範囲の共有セル範囲を表す Range オブジェクトを返す。

(tata) 2017/07/23(日) 09:09


 tataさん
 別にスレッド設けろとのことですので、もし配列について続けていただけるのでしたら、
 新しくスレッド立てていただいてもよろしいですか?

 もう不要でしたら結構です。
 同じ題材でFind以外でもできると説明したかったのですが、気に入らない方がいらっしゃるので。
(稲葉) 2017/07/23(日) 09:20

すみません、

投稿
[[20170723091359]] 『配列 の使い方、Dictionaryの使い方』(tata) 
について...

に移動します。
(tata) 2017/07/23(日) 09:26


 > 今回の大本の質問者さん、
 >  7月までのデータはクロス集計表に手作業で入力済み
 >  7月までの元データは存在せず、8月からのデータを楽に処理したい
 > だったため、フィルターオプションで重複削除すると、入力済みのものが
 > 上書きされてしまうので、上手いやり方が思いつきませんでした。
 > 何かそういった場合の良いやり方ありますでしょうか?
元の質問はみてませんが、
手動でやる場合は、表を先に作っておいて、追記とすると思いますが、
マクロで処理するならいつでも瞬時に結果が得られますので、
クロス集計表のほうは、どこかのシートに置いておく必要はありません。
「必要な時にボタン一つで必要な結果を表示する。」
これがマクロ化する一番の目的であり利点であると思います。
保存しておくのは「データベース」です。計算や多少の作業で得られる結果は、
データベースにする必要がありません。
なので、見たり、名前を付けて保存して他人に渡したり、印刷したりしたら、
破棄すると難しいことを一切考えなくていいです。
必要になればまた、ボタン一つ押下で得られるのですから。

 >すみません、以下の記述がよくわからなかったのですが、ご解説いただけませんか。 
 >        Set rngTable = Intersect(.Cells, .Offset(1, 1)) 
 >'式.Intersect 複数のセル範囲の共有セル範囲を表す Range オブジェクトを返す。
少し数学的な話になります。
セル範囲は各セルの集合体です。(シートもセルの集合で構成されています。)
で、Intersect関数はあるセル範囲と、もう一つ別のセル範囲(3つでも4つでもいいです。要ヘルプ確認)の
「積集合」(集まりの重なった部分のみ)を返します。
元のセル範囲とそれを1行下1行右にずらした範囲つまり表体のセル範囲を求めています。
クロス集計表を扱う場合、この表体のセル範囲を求めておくと、
そのあとの作業が非常にい楽になります。
以下のサンプルを、「ステップ実行」してみてください。

サンプル>>
Sub test001()

    Dim rngDataBody As Range

    With Range("F1:J5")
        .Value = "てすと"
        '表全体
        .Cells.Interior.Color = vbYellow
        '1行下の1列右
        .Offset(1, 1).Interior.Color = vbBlue
        '表体
        Set rngDataBody = Intersect(.Cells, .Offset(1, 1))
    End With

    rngDataBody.Interior.Color = vbGreen
    '表頭
    rngDataBody.Rows(0).Interior.Color = vbRed
    '表側
    rngDataBody.Columns(0).Interior.Color = vbMagenta
End Sub

この、
Intersect(.Cells, .Offset(1, 1))
は、タイトル行などをセル範囲から排除したい場合に使うテクニックです。
覚えておくと便利でしょう。
まぁ、他の書き方も普通にできますが。。。。

    With Range("F1:J5")
        .Resize(.Rows.Count - 1, .Columns.Count - 1).Offset(1, 1).Interior.Color = vbBlack
    End With
とにかくマクロでやりたいことはシート上のセルを扱うことがほとんどでしょうから、
セル範囲を示す語彙を増やすことは、重要だと思います。
他の方の質問の回答などたくさんコードをみて、よさそうな情報を集めて勉強してください。

あ、
もしかして、With句の使い方がわかりませんでした?
または、「.Cells」の使い方わからなかったですかね?
それとも、Rows(0)という0行目の意味がわからないですかね?
まぁ、また聞いてください。
あ、あと用語がわからない場合はまずは検索してみてください。
検索が上手になると、いつ回答があるかわからない掲示板より
速やかに解決できるようになります。勉強がはかどると思います。
ま、ピンポイントで解説してほしいときは掲示板ですけどね(^^v

(まっつわん) 2017/07/23(日) 10:05


あぁ、失礼しました。

>同じ題材でFind以外でもできると説明したかったのですが、気に入らない方がいらっしゃるので。
いや、同じ題材の別のやり方を示すのは問題ないです。

>If dic.exists(r.Value) Then 'このままだと「オブジェクトが必要です424」エラー。オブジェクト修飾子?
このコメントを稲葉さんのコメントと勘違いしてただけです。

謝ります。m(_ _)m

(まっつわん) 2017/07/23(日) 10:10


 >保存しておくのは「データベース」です
 ですね…7月からじゃなく1月からのデータがあればそもそもピボットテーブルで済む話ですし。

 >Intersect関数は「積集合」を返す
 リサイズで1行1列減らして、オフセットで右下に1行1列ずらす、よりも
 スッキリした書き方、って言うことですね?

 >サンプル>> 
 >Sub test001() 
 ご解説ありがとうございました。よくわかりました。

私が誤解していないか確認

 >With句または、「.Cells」の使い方わからなかったですかね? 
 指定した範囲のセルを表す Range オブジェクトを返す。
 rngTable.CurrentRegion.Cells で、F1からの全体
 rngTable.CurrentRegion.Offset(1, 1) で、F2からにズレた範囲

 >Rows(0)という0行目の意味
 指定の1行上で良いんですよね?
 Rows(-1)にすれば2行上ですよね?

色々ありがとうございます。頑張ります。

(tata) 2017/07/23(日) 11:12


 まっつわんさん
 こちらも大人げない言い方でした
 すみません
 スレが長くなったのて、やがて別スレでと思っていたので
 良い機会でした

(稲葉) 2017/07/23(日) 11:45


  >>Rows(0)という0行目の意味
  >指定の1行上で良いんですよね?
  >Rows(-1)にすれば2行上ですよね?
そうでうすね。

Rows(-1)とか一瞬どこかな?って迷うので可読性で難点がありますが、
セル範囲の0行目がタイトル行っていうのは個人的にありかなぁと思います。

あとは他の方のコードとかも参考にしながら取捨選択してやってください。^^

あと、

 > ワークシート関数使う方が楽、速い、ですね…
タイピングは楽ですが、処理速度は速くはないかもしれません。

また、ディクショナリの話も出てますが、
フィルターオプションの重複削除も一行で終わるので、
コードはすっきりしますよね。
処理速度はどっちがどうなんでしょうね。。。
VBAでループ処理を書くなら、
フィルターオプションも負けてない気がしますが、実際はどうなんでしょうね。
そこから先はぼくは興味ないので、興味があればいろいろ実験してみてください。

(まっつわん) 2017/07/23(日) 19:49


コメント返信:

[ 一覧(最新更新順) ]


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