[[20160429092217]] 『VBA 複数データ抽出について』(こまんど) ページの最後に飛ぶ

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

 

『VBA 複数データ抽出について』(こまんど)

 VBAの初歩的な質問になるかもしれませんが、どうしても分からず。

データ抽出で条件を
「4つ条件のデータ以外を削除する」ということをしたく。

if notで始めorを4つ並べたのですが、どうしても最初の条件のみ動き
後続条件が弾かれてしまいます。

セルA5に
テストA
テストB
テストC
テストD
テストE
テストF
テストA
テストB
のようなデータがあるのですが、テストA〜Dを抽出し、それ以外データ行を削除することは可能なのでしょうか?

抽出条件としては下記を考えていますが…
「If Not Range("A5").Value="テストA" Or Range("A5").Value="テストB" Or Range("A5").Value="テストC Or Range("A5").Value="テストD Then」

つらつらと書くと有識者より読みにくい構文と言われるかもしれませんが
今後の参考の為、よろしくご教授お願いします。

< 使用 Excel:Excel2013、使用 OS:Windows7 >


If .... Then のあとも含めて、ワンセット提示してみてはいかがですか?

削除するなら下から上に向かって一行ずつ実行していくのがよいと思います。

(γ) 2016/04/29(金) 09:45


あ、一つのセルに入っているのですか。
それなら、vbLfでSplitした各要素が条件に該当するかどうかを判定して、
条件にあっていれば、それをvbLFで連結していくんでしょうね。
条件判定はSelect Caseを使うとよいでしょう。

(γ) 2016/04/29(金) 09:53


 質問のテーマが解決したとしても、おそらく、この先、いくつか壁にぶつかるとは思いますが、
 現在までできているコードのアップがないので、そこまでの具体的なコメントはできません。

 今回の件に絞って。

 If文の中で条件を連結して判定する場合、特に 【否定】条件は、記述がややこしいというか、わかりにくくなりますね。

 アップされた構文は

 比べるものが テストA 以外か テストB か テストC か テストD なら という意味になります。
 Not は 最初の テストA にしかかかりません。
 ですから テストA 以外は すべてが対象になりますね。

 この形式で構文を書くなら

 If Not (Range("A5").Value="テストA" Or Range("A5").Value="テストB" Or Range("A5").Value="テストC" Or Range("A5").Value="テストD) Then

 というように、 Or 条件をすべて (  ) でくくって、それら Or の結果の Not にしなければいけませんね。

 γさん指摘の通り、この場合は Select Case のほうが、構文として書きやすいし読みやすいと思います。

(β) 2016/04/29(金) 09:58


 γさんのコメントを読み返し、改めて質問文を読みなおしましたら

 >>セルA5に ・・・・ のようなデータがあるのですが

 つまり、A5 の 1つのセル内に文字列が改行されて

 テストA 
 テストB 
 テストC 
 テストD 
 テストE 
 テストF 
 テストA 
 テストB 

 こんなように入っていて、その A5 の文字列を

 テストA 
 テストB 
 テストC 
 テストD 
 テストA 
 テストB 

 こんなように、書き換えたい そういう質問でしたか?

(β) 2016/04/29(金) 10:19


 おはようございます。

 >それ以外データ行を削除することは可能なのでしょうか?

 この文面も気になりますね。
 1つのセル内の不要文字を削除するのか、複数の行の不要行を削除するのかどちらでしょう?

(sy) 2016/04/29(金) 10:23


 >「4つ条件のデータ以外を削除する」
 文字列"テスト"に関わらず末尾にA,B,C,Dがあるものを残す、として

 Sub test()
    Dim x, i As Long
    x = Split([a5], vbLf)
    For i = 0 To UBound(x)
        If x(i) Like "*[!A-D]" Then x(i) = Chr(2)
    Next
    [a5] = Join(Filter(x, Chr(2), 0), vbLf)
End Sub

 正規表現でもできるけど、速度的には遅くなるしコードの行数も大差ないので...

(seiya) 2016/04/29(金) 11:12


γ様
 早速のご指摘、ありがとうございます。

「下から遡ってデータを見ていく」については参考になります。
SlelectCaseももう少し勉強してみます。

β様
 おっしゃる通り、テストA〜D以外の行を削除するということです。
 説明がごちゃごちゃし、すいません。
 ただ、γ様に教えていただいた、()のくくり場所を追加し、
 動きました。
 orが最初しかかかっていないとは思ってもみませんでした。
 
sy様
 言葉足らずで申し訳ございませんでした。
 テストA〜D以外の行削除という意味でした。
 Deleteについてはマクロ記録よりできるようになりました。

seiya様
 す、すごい。まだ私には読み解けていませんが、
 文字列ではなく、文字で判断するやり方もあるのですね。
 ごめんなさい、正直まだついていけていませんが、
 テストして結果見て、動きを理解していきたいとおもいます。

皆様
 γ様のアドバイスで思う動きが取れました。
 早朝より、色々ご指摘ありがとうございました。

勉強がてら色々な指定の仕方をしてみようと
If (Worksheets("worksheet").Cells(i, 2).Value <> "テストA") Or (Worksheets("worksheet").Cells(i, 2).Value <> "テストB") Or (Worksheets("worksheet").Cells(i, 2).Value = "テストC") Then

で指定したのですが、結果が空白?で返ってきます。
やはりどこかで指定方法がおかしいのでしょうか。
(こまんど) 2016/04/29(金) 12:27


ごめんなさい、セルA5だったので
「Worksheets("worksheet").Cells(i, 5).Value <> "テストA"」
                 ↑
              まずはここが違いますね。。
(こまんど) 2016/04/29(金) 12:28

 A列で判断するんですよね。

 If InStr("テストAテストBテストCテストD", Cells(i, "A").Value) = 0 Then

 でも良いですね。
(sy) 2016/04/29(金) 13:01

ふうむ。

(A または B)ではない
は普通、
Aでもなく、Bでもない
ですよねえ。

とすれば、OR じゃなく AND を使ってつなげていくんじゃなかろうか。
(γ) 2016/04/29(金) 13:02


 演算子を誤解していますね。

 >orが最初しかかかっていないとは思ってもみませんでした。

 皆さんからも指摘がありますが 、ANDは「且つ」、ORは「又は」です。

 NOTは否定なので、複数否定したい時は、ANDで全てが否定の時と言う風にしなければいけません。

 NOT i=a AND NOT i=b AND NOT i=c
 i<>a AND i<>b NOT i<>c

 上記2つはiがaではない、且つ、iがbではない、且つ、iがcではない、となって全てが否定された時になります。

 NOT(i=a OR i=b OR i=c)

 こちらは、()内が先に判定されて、iがa、または、iがb、または、iがc、どれかが肯定なので、それをNOTで否定するので、上と同じに全て否定になります。

 私も演算子間違えてました。。。
 訂正します、すいません。

(sy) 2016/04/29(金) 13:28


 演算子に対する誤解もあると思いますが、加えて、皆さんから質問されている点、
 【行】というのが、A5セル内の 改行されている、1つ1つのことを言っているのか、
 そうではなく、A1 、A2、A3、・・・ と 単独のセルに 該当の文字列があるかどうか、という、【本当の行】のことを言っているのか
 その返事がないということは、皆さんからの質問の意味を理解していない?

 seiyaさんのコードは A5 という1つのセル内の改行による セル内の行の削除で、それにたいして
 こまんどさんのレスが、NGではなかったということで、あぁ、やはり セル内の話だったかと
 そう思いきや、そちらで対応したコードは

 Worksheets("worksheet").Cells(i, 5).Value <> "テストA"  といったように、あくまで 別セル(別の行)に対する処理コードになっています。

 いったい、どちらが本当なんですか?

 もし、セル内改行であれば、改行コードを ● であらわすと A5 は
 テストA●テストB●テストC●テストD●テストE●テストF●テストA●テストB 
 といったように、連続した1つの文字列になっています。

 この場合、Range("A5").Value ないしは Cells(5,1).Value を "テストA"で比較しようが "テストB" で比較しようが
 完全一致の比較を行う限り、何で比較しても 同じではない という結果になりますよ。

(β) 2016/04/29(金) 13:46


皆様

 たくさんのご教授、ありがとうございます。

と言うことは
If (Worksheets("worksheet").Cells(i, 2).Value <> "テストA") Or (Worksheets("worksheet").Cells(i, 2).Value <> "テストB") Or (Worksheets("worksheet").Cells(i, 2).Value = "テストC") Then
で指定した場合は最初の
「(Worksheets("worksheet").Cells(i, 2).Value <> "テストA")」この段階で
falseとなれば抜けてしまうってことなのでしょうか。

sy様
これもすごいですね。検索するセルより対象文字列が無ければ"0"を返し
=であればと言う感じでしょうか。
この発想も参考になります。
今さらながら?演算子の誤解も気付かされました。

β様
 回答が言葉足らずと認識齟齬、すいません。
 かつA5とさらに誤記してしまい、皆様を惑わしてしまいました。

 データの格納はE列「Cells(i, 5)」に約MAX1000件ほどあり、
 各セルは「データA」〜「データZ」まで任意のデータが記載されています。
 その中で「データA」〜「データD」を抽出し、それ以外は行ごと削除する
 と言うことが目的でした。
 行…データ内のことではなく、本当の行

正直、seiya様のロジックには追い付けていませんでした。
各セルEに文字列の末尾がA〜Dであればという解釈でした。

(こまんど) 2016/04/29(金) 14:00


 こういうこと?
 Sub test()
    Dim i As Long
    For i = Range("e" & Rows.Count).End(xlUp).Row To 5 Step -1
        If Cells(i, "e") Like "*[!A-D]" Then Rows(i).Delete
    Next
End Sub
(seiya) 2016/04/29(金) 14:17
 修正:ループカウンターの最小値を1から5に変更 15:25

 >検索するセルより対象文字列が無ければ"0"を返し 
 >=であればと言う感じでしょうか。

 合ってます。 

 >If (Worksheets("worksheet").Cells(i, 2).Value <> "テストA") Or (Worksheets("worksheet").Cells(i, 2).Value <> "テストB") Or (Worksheets("worksheet").Cells(i, 2).Value = "テストC") Then

 長いので、Worksheets("worksheet").Cells(i, 2).Valueはiで、テストはそれぞれabcで表現しますね。

 if(i<>a) or (i<>b) or (i<>c)then

 これは、iがaでは無い、またはiがbでは無い、またはiがcでは無い、なので、
 例えばiがaの時に、(i<>a)これには一致しないのでここだけ見れば対象から外れますが、
 or で繋いだ(i<>b)(i<>c)に一致するので、行の削除対象と言う判断にされてしまいます。

 if(i<>a) or (i<>b) or (i<>c)then はどのようなパターンでも全て該当してしまうので、比較する意味が無くなります。

(sy) 2016/04/29(金) 14:26


 なかなか言うことが通じない。本気出す。

 まず、質問の訂正をきちんとすべきでした。

 セルA5以下の各行に 1行ずつ
 テストA 
 テストB 
 テストC 
 テストD 
 ・・・
 とあります。
 と明確に書いてください。

 以下のコードを比較してみてください。
 こういうことをしたかったんじゃないかと思う。

 Sub test1()
     Dim k As Long
     Dim s As String

     For k = Cells(Rows.Count, 1).End(xlUp).Row To 5 Step -1
         s = Cells(k, 1).Value
         If Not (s = "テストA" Or s = "テストB" Or s = "テストC" Or s = "テストD") Then
             Rows(k).Delete
         End If
     Next
 End Sub

 Sub test2()
     Dim k As Long
     Dim s As String

     For k = Cells(Rows.Count, 1).End(xlUp).Row To 5 Step -1
         s = Cells(k, 1).Value
         If s <> "テストA" And s <> "テストB" And s <> "テストC" And s <> "テストD" Then
             Rows(k).Delete
         End If
     Next
 End Sub

 Sub test3()
     Dim k As Long
     Dim s As String

     For k = Cells(Rows.Count, 1).End(xlUp).Row To 5 Step -1
         s = Cells(k, 1).Value
         Select Case s
         Case "テストA", "テストB", "テストC", "テストD"
         Case Else
             Rows(k).Delete
         End Select
     Next
 End Sub

(γ) 2016/04/29(金) 15:06


 1000行程度であれば、どういった処理をしても、時間的に大差はないと思いますが、
 仮に、1行目がタイトル行、2行目からデータがあるというレイアウトだとしたら
 フィルターオプションによる処理も一考の余地があるかと。

 1.既存の表の右側、1行ぐらいあけて(たとえばデータがJ列まであるならL列に)E列と同じタイトルを記入。
 2.E2:E5 に残すべき文字列を記入。
   データA
   データB
   データC
   データD
 3.A1を選択して フィルターグループの詳細設定(フィルターオプション)
 4.リスト範囲(L)には すでに、タイトル行含めた表の領域がセットされていると思います。
   検索条件範囲(C) に L1:L5 、指定した範囲(O)を選び、抽出範囲に N1。これでOKボタン。
 5.A:M列を削除。

 これをマクロ記録すれば、ループなしの処理コードが生成されます。

(β) 2016/04/29(金) 18:14


 ↑の操作をマクロ化したものです。(20:07 条件欄記述、ちょっと修正)

 Sub Sample()
    Range("L1").Value = Range("E1").Value
    Range("L2:L5").Value = [{"データA";"データB";"データC";"データD"}]
    Range("A1").CurrentRegion.AdvancedFilter Action:=xlFilterCopy, _
        CriteriaRange:=Range("L1").CurrentRegion, CopyToRange:=Range("N1"), Unique:=False
    Columns("A:M").Delete
 End Sub

(β) 2016/04/29(金) 18:26


コメント返信:

[ 一覧(最新更新順) ]


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