Excel VBA質問箱 IV

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

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


2105 / 13645 ツリー ←次へ | 前へ→

【69916】Excel2003 重複データをまとめたい やみ 11/9/23(金) 15:34 質問[未読]
【69919】Re:Excel2003 重複データをまとめたい kanabun 11/9/23(金) 20:05 発言[未読]
【69921】Re:Excel2003 重複データをまとめたい やみ 11/9/23(金) 21:18 お礼[未読]

【69916】Excel2003 重複データをまとめたい
質問  やみ  - 11/9/23(金) 15:34 -

引用なし
パスワード
   A列〜J列までデータがあります。
A列とB列をKEYにしてC列〜J列の値をまとめて、重複してる文字を消したいと考えています。A列〜I列は文字列でJ列が数値です。C列〜I列はセル内が空欄の場合もあります。

現在は下記のコードでそれぞれシートに分けて列をコピーし(<A列B列C列> <A列B列D列>など)、最後に別シートにコピーしてまとめています。
これを元データのまま加工する方法を教えて下さい。

A列  |B列  |C列  |D列  |E列  |F列  |G列  |H列  |I列  |J列
りんご|いちご|かき |みかん|ぶどう|なし |すいか|メロン|ライチ|30
りんご|いちご|スイカ|みかん|ぶどう|いも |すいか|メロン|ライチ|20
りんご|もも |スイカ|みかん|ぶどう|いも |すいか|メロン|ライチ|30

りんご|いちご|かき・スイカ|みかん|ぶどう|なし・いも|すいか|メロン|ライチ|50
りんご|もも |スイカ|みかん|ぶどう|いも |すいか|メロン|ライチ|30
--------------------------------------------
Sub test()
Dim r As Range, LRng As Range, s1, s2, i As Integer
Dim fAdd As String, flg As Boolean, n As Integer

Application.ScreenUpdating = False '画面ちらつき制御

With ActiveSheet

  For Each r In Range("A2", .Range("A" & Rows.count).End(xlUp))
  
      s1 = Array(r.Offset(0, 1).Value)
      s2 = Array(r.Offset(0, 2).Value)

    For i = 0 To UBound(s1)
     
      'A列の値E列からを探す
      Set LRng = .Columns("E:E").Find(What:=r, after:=.Range("E1"), LookAt:=xlWhole)
      
      'A列=E列が無かった場合→転機
      If LRng Is Nothing Then
        Set LRng = .Range("E" & Rows.count).End(xlUp).Offset(1, 0)
        LRng.Value = r.Value
        LRng.Offset(0, 1).Value = s1(i)
        LRng.Offset(0, 2).Value = s2(i)
      Else
        'A列=E列があった場合
        fAdd = LRng.Address
        flg = False
        Do
          'B列=F列を確認しマッチした場合の処理
          If LRng.Offset(0, 1).Value = s1(i) Then
            If LRng.Offset(0, 2).Value = "" Then
             LRng.Offset(0, 2).Value = s2(i)
            ElseIf s2(i) <> "" And InStr(1, LRng.Offset(0, 2).Value, s2(i), 1) = 0 Then
             LRng.Offset(0, 2).Value = LRng.Offset(0, 2).Value & "・" & s2(i)
            End If
            flg = True
            Exit Do
          End If
  
          Set LRng = .Columns("E:E").FindNext(LRng)
          
        Loop While Not LRng Is Nothing And LRng.Address <> fAdd
       
        'A列=E列は見つかったが、B列=F列がマッチしない場合の処理
        If Not flg Then
         Set LRng = .Range("E" & Rows.count).End(xlUp).Offset(1, 0)
           LRng.Value = r.Value
           LRng.Offset(0, 1).Value = s1(i)
           LRng.Offset(0, 2).Value = s2(i)
        End If

      End If

    Next i
    
  Next r

  
End With

End Sub

【69919】Re:Excel2003 重複データをまとめたい
発言  kanabun  - 11/9/23(金) 20:05 -

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

>A列とB列をKEYにしてC列〜J列の値をまとめて、重複してる文字を消したい

すみません。ろくすっぽ、書かれたコード読んでませんが m(_ _)m
こういう key別(A列+B列の項目別)にC列〜J列のデータをまとめる処理には
Dictionaryオブジェクトを利用するのが手っ取り早いと思います。

↓でやってることは、
dicというDictionaryオブジェクトに各行の(A列+B列の)keyを格納していって
・keyがはじめて出現したものだったら(if not dic.Exists(ss) then)
 出力用配列の k行目にデータをコピーし、
・すでに出現済みのkeyであれば、すでに出力してある行に、C列からJ列までの
 データを結合(または合計)していき、
最後に Sheet2 にまとめた結果を出力する、
というものです。

Sub Try1()
  Dim dic As Object
  Set dic = CreateObject("Scripting.Dictionary")
  
  Dim vi, i As Long, j As Long
  Dim k As Long, n As Long
  Dim ss As String
  
  '元データはSheet1 と仮定
  With Worksheets(1)
    vi = .Range("A2", .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 10).Value
  End With
  ReDim vo(1 To UBound(vi), 1 To 10)
  For i = 1 To UBound(vi)
    ss = vi(i, 1) & vbTab & vi(i, 2)
    If Not dic.Exists(ss) Then
      k = k + 1
      dic(ss) = k
      For j = 1 To 10 '値のうめこみ(すべての列)
        vo(k, j) = vi(i, j)
      Next
    Else
      n = dic(ss)
      For j = 3 To 9 '文字列の結合
        If InStr(vo(n, j), vi(i, j)) = 0 Then
          vo(n, j) = vo(n, j) & "・" & vi(i, j)
        End If
      Next
      vo(n, 10) = vo(n, 10) + vi(i, 10)
    End If
  Next
  Set dic = Nothing
  
  'Sheet2 に貼り付け (テストのため、別シートに結果を出力)
  With Worksheets(2)
    .UsedRange.ClearContents
    .Cells(1).Resize(, 10) = Worksheets(1).Cells(1).Resize(, 10).Value
    .Cells(2, 1).Resize(k, 10) = vo
  End With
        
End Sub

※なお、簡単のため、文字列がすでに書き込まれているかのチェックを
非常に簡単な方法でチェックしています。

   If InStr(vo(n, j), vi(i, j)) = 0 Then

たとえば
出力用配列のある位置vo(n,j)に 「みかん・いも」とすでに書き込んであって
いま vi(i,j) が「スイカ」だったとします。
「みかん・いも」のなかには「スイカ」という文字列は見つかりませんから、
"スイカ" はこれまでの文字列と結合されて 「みかん・いも・すいか」とな
ります。
ところが、チェックする文字列が仮に「かん」だったとしますと、
  InStr(vo(n, j), vi(i, j))
は 2 を返しますから(「みかん・いも」の2文字目にマッチする)結果「かん」
は追加されない、という不具合が発生します。
上のコードは、こういう特殊ケースがありえないと仮定したコードです。

【69921】Re:Excel2003 重複データをまとめたい
お礼  やみ  - 11/9/23(金) 21:18 -

引用なし
パスワード
   ▼kanabun さん:こんばんは!初めまして!

素晴らしいコードありがとうございました。
Dictionaryオブジェクト勉強します!!
kanabun さんのコードじっくり読みます!
ありがとうございました!!!

>▼やみ さん: こんにちは〜
>
>>A列とB列をKEYにしてC列〜J列の値をまとめて、重複してる文字を消したい
>
>すみません。ろくすっぽ、書かれたコード読んでませんが m(_ _)m
>こういう key別(A列+B列の項目別)にC列〜J列のデータをまとめる処理には
>Dictionaryオブジェクトを利用するのが手っ取り早いと思います。
>
>↓でやってることは、
>dicというDictionaryオブジェクトに各行の(A列+B列の)keyを格納していって
>・keyがはじめて出現したものだったら(if not dic.Exists(ss) then)
> 出力用配列の k行目にデータをコピーし、
>・すでに出現済みのkeyであれば、すでに出力してある行に、C列からJ列までの
> データを結合(または合計)していき、
>最後に Sheet2 にまとめた結果を出力する、
>というものです。
>
>Sub Try1()
>  Dim dic As Object
>  Set dic = CreateObject("Scripting.Dictionary")
>  
>  Dim vi, i As Long, j As Long
>  Dim k As Long, n As Long
>  Dim ss As String
>  
>  '元データはSheet1 と仮定
>  With Worksheets(1)
>    vi = .Range("A2", .Cells(.Rows.Count, 1).End(xlUp)).Resize(, 10).Value
>  End With
>  ReDim vo(1 To UBound(vi), 1 To 10)
>  For i = 1 To UBound(vi)
>    ss = vi(i, 1) & vbTab & vi(i, 2)
>    If Not dic.Exists(ss) Then
>      k = k + 1
>      dic(ss) = k
>      For j = 1 To 10 '値のうめこみ(すべての列)
>        vo(k, j) = vi(i, j)
>      Next
>    Else
>      n = dic(ss)
>      For j = 3 To 9 '文字列の結合
>        If InStr(vo(n, j), vi(i, j)) = 0 Then
>          vo(n, j) = vo(n, j) & "・" & vi(i, j)
>        End If
>      Next
>      vo(n, 10) = vo(n, 10) + vi(i, 10)
>    End If
>  Next
>  Set dic = Nothing
>  
>  'Sheet2 に貼り付け (テストのため、別シートに結果を出力)
>  With Worksheets(2)
>    .UsedRange.ClearContents
>    .Cells(1).Resize(, 10) = Worksheets(1).Cells(1).Resize(, 10).Value
>    .Cells(2, 1).Resize(k, 10) = vo
>  End With
>        
>End Sub
>
>※なお、簡単のため、文字列がすでに書き込まれているかのチェックを
>非常に簡単な方法でチェックしています。
>
>   If InStr(vo(n, j), vi(i, j)) = 0 Then
>
>たとえば
>出力用配列のある位置vo(n,j)に 「みかん・いも」とすでに書き込んであって
>いま vi(i,j) が「スイカ」だったとします。
>「みかん・いも」のなかには「スイカ」という文字列は見つかりませんから、
>"スイカ" はこれまでの文字列と結合されて 「みかん・いも・すいか」とな
>ります。
>ところが、チェックする文字列が仮に「かん」だったとしますと、
>  InStr(vo(n, j), vi(i, j))
>は 2 を返しますから(「みかん・いも」の2文字目にマッチする)結果「かん」
>は追加されない、という不具合が発生します。
>上のコードは、こういう特殊ケースがありえないと仮定したコードです。

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