[[20190313154738]] 『一覧に存在しないデータの抽出』(もも) ページの最後に飛ぶ

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

 

『一覧に存在しないデータの抽出』(もも)

以前質問させていただきました。
そもそもでデータの入力方法を変更すれば解決する問題なのですが。。。

一覧

     A         B
 1  aaa       12000
    AAA
    aaa
    株式会社aaa

  ↑1セルに入力されています

データ

   A  ・・・   E
1 aaa           1000
2 AAA           2000
3 aaa      3000
4 株式会社aaa   6000
5 株式会社aaa 10000

B1セルには

=SUMPRODUCT((COUNTIF(A1,"*"データA$1:A$1600&"*"))*(データ!E$1:E$1600<>""),データ!E$1:E$1600)

と入力しています。

この時にデータの5行目の「株式会社aaa」が集計漏れしているので、漏れてしまっているデータを抽出したいと思っております。
(想定としてはG1から表示)

B1の集計も含めたマクロor集計漏れ抽出のみだけでも行いたいです。
よろしくお願いします。

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


Sub main()
    Dim dic As Object, dic2 As Object, k As Variant, c As Range, i As Long, tot As Long
    Set dic = CreateObject("Scripting.dictionary")
    Set dic2 = CreateObject("Scripting.dictionary")
    For Each c In Sheets("データ").Range("A:A").SpecialCells(2)
       dic(c.Value) = dic(c.Value) + Val(c.Offset(, 1).Value)
       dic2(c.Value) = 1
    Next c
    For i = 0 To UBound(Split(Sheets("一覧").Range("A1").Value, Chr(10)))
       tot = tot + dic(Split(Sheets("一覧").Range("A1").Value, Chr(10))(i))
       dic2(Split(Sheets("一覧").Range("A1").Value, Chr(10))(i)) = 0
    Next i
    Sheets("一覧").Range("B1").Value = tot
    For Each k In dic2
        If dic2(k) = 1 Then MsgBox k & "が漏れてます。"
    Next k
End Sub
(mm) 2019/03/13(水) 17:13

mmさま

漏れていないデータも表示されてしまいます。。。
(もも) 2019/03/13(水) 18:09


一覧の方がA列B列ではなく、B列C列だったので直しましたが、大部分が漏れていますと表示されます
(もも) 2019/03/13(水) 18:13

何を「漏れ」とするかですが、ワイルドカードで抽出しているので、重複カウントされている場合がありますよね? 例えば「aaa」があれば「株式会社aaa」も抽出されているので、「株式会社aaa」は漏れてない、と考えるのでしょうか? それだとちょっと面倒かもです。

とりあえず、ワイルドカードは考えず、「データ」シートのA列にあるけど、「一覧」シートのB列に無いものを抽出するだけの例を書きます。

 Sub test()
    Dim AR As Object
    Dim i As Long

    Set AR = CreateObject("System.Collections.ArrayList")

    With Sheets("データ")
        For i = 1 To .Cells(.Rows.Count, "A").End(xlUp).Row
            If AR.Contains(.Cells(i, "A").Value) = False Then
                AR.Add .Cells(i, "A").Value
            End If
        Next i
    End With

    With Sheets("一覧")
        For i = 1 To .Cells(.Rows.Count, "B").End(xlUp).Row
            If AR.Contains(.Cells(i, "B").Value) = True Then
                AR.Remove .Cells(i, "B").Value
            End If
        Next i
        If 0 < AR.Count Then
            .Range("G1").Resize(AR.Count, 1).Value = WorksheetFunction.Transpose(AR.toarray)
        Else
            MsgBox "漏れなし", vbInformation
        End If
    End With
 End Sub
(???) 2019/03/13(水) 18:36

どんなデータか理解できていないので
的はずれな質問かもしれませんが…

>大部分が漏れていますと表示されます

1600行のデータがあるとすると、一覧のA1には4個しかないのだから、
1596行分のデータが漏れていますとなるのでしょうか?

>一覧の方がA列B列ではなく、

一覧のA2やA3にも別のデータが続くのでしょうか?
それとも、A1だけですか。

(マナ) 2019/03/13(水) 20:03


前のスレを読んでやっとわかりました(たぶん)。
データシートで、aaaと入力する人、AAAと入力する人、様々で困っているのですね。

こういうことでしょうか。

 Option Explicit

 Sub test()
    Dim dic As Object
    Dim al As Object, al2 As Object
    Dim c As Range
    Dim s As String
    Dim e
    Dim tmp As Long

    Set dic = CreateObject("scripting.dictionary")
    Set al = CreateObject("system.collections.arraylist")

    For Each c In Sheets("データ").Columns("A").SpecialCells(2)
        s = c.Value
        dic(s) = dic(s) + c.Offset(, 4).Value
        If Not al.contains(s) Then al.Add s
    Next

    For Each c In Sheets("一覧").Columns("B").SpecialCells(2)
        Set al2 = al.Clone
        tmp = 0
        For Each e In Split(c.Value, vbLf)
            tmp = tmp + dic(e)
            al2.Remove e
        Next
        c.Offset(, 1).Value = tmp
        If al2.Count > 0 Then
            c.Offset(, 5).Resize(, al2.Count).Value = al2.toarray
        End If
    Next

 End Sub

(マナ) 2019/03/13(水) 21:22


↑やっぱり、だめですね。
株式会社BBBとか、漏れとして扱ってしまいます。
不可能では?

(マナ) 2019/03/13(水) 21:27


データリストの名前を置換で修正したほうがよいのでは?

(マナ) 2019/03/13(水) 21:33


 >一覧 
 >     A         B
 > 1  aaa       12000
 >    AAA
 >    aaa
 >    株式会社aaa
 >  ↑1セルに入力されています 
 >
 >データ 
 >   A  ・・・   E
 >1 aaa           1000
 >2 AAA           2000
 >3 aaa      3000
 >4 株式会社aaa   6000
 >5 株式会社aaa 10000

 掲示されたサンプルデータは不適切な気がします。(いい加減なデータと言った方が近い)

 集計漏れが話題になっていますが、それは集計対象になっているものの裏返しですよね?

 だとすると、まず、集計対象になっている会社は何であり、集計結果は何円が正しいのですか?
 それをまず示してください。

 ※簡単に説明しようとして、いい加減なサンプルを使うと、却って理解しにくくなります。

(半平太) 2019/03/13(水) 23:47


一覧には60社ほど記載があります。
正確に書くと

   A          B            ・・・ K
 1 会社名     検索用会社名 ・・・ 金額
 
 2 田中製造所  田中製造所   ・・・ 12000
                TANAKA
                タナカ
                伊勢三重
 3 佐藤製鉄所  佐藤製鉄所   ・・・ 8000
                SATO
                静岡富士
・
・
・
60 東洋株式会社 東洋株式会社 ・・・  9000
                TOYO
                東洋山梨
                東洋名古屋

データのシートは

     A   ・・・  E
 1   田中     5000
 2  東洋         2000
 3  SATO         4000
 4   東洋山梨     3000
 5   TANAKA    5000
 6   佐藤     2000 
 7   たなか    4000 ←
 8   トーヨー     2000
 9   伊勢三重     2000 
 10  静岡富士   8000
 11  さとう    7000 ←
 12  東洋株式会社 4000
 ・
 ・
 ・

 1200 トーヨー    1000 ←

となっています。

集計結果は

        【誤】   【正】
田中製造所   12000   16000
佐藤製鉄所   8000    15000
東洋株式会社  9000    10000

となってほしいです。
回答になっていますでしょうか

   
(もも) 2019/03/14(木) 09:25


 >回答になっていますでしょうか 

 詳細は見ていませんが、キッチリしていてすばらしいです。

 ※ちょっと前までは、危なっかしい処理だなと思っていたのですが、
  結構しっかりしているので安心しました。 

 処理手順としては、以下のツーステップになると思います。

 (1)検索用会社名に該当しないデータの有無を確認する(プログラムで洗い出す)
   有ったら、該当する会社の検索法会社名に追加する(これは手作業)。

 (2)全ての追加が終わったら、普通にプログラムで集計する。

 今日はちょっと忙しいので戻るのが遅くなります。

 それまでに他の回答者からレスが付くといいですね。

(半平太) 2019/03/14(木) 10:06


 最後の正誤表は間違っていませんか?

 私の結果は違っていますし、正誤表は手作業でないと判断できないでしょう...

 集計結果を一覧シートのK列、データシートのG列に集計漏れのマークを記入。

 ということで、

 Sub test()
    Dim a, b, i As Long, ii As Long, myRow As Long, temp, mySum As Double
    With Sheets("データ")
        a = .Range("a1", .Range("a" & Rows.Count).End(xlUp)).Resize(, 5).Value
        With Sheets("一覧")
            b = .Range("a1", .Cells.SpecialCells(11)).Resize(, 3).Value
            b(1, 1) = "金額"
            For i = 2 To UBound(b, 1)
                If ((b(i, 1) <> "") * (b(i, 1) <> temp)) Then
                    If myRow > 0 Then b(myRow, 1) = mySum
                    myRow = i: mySum = 0: b(i, 1) = 0
                End If
                If b(i, 2) <> "" Then
                    For ii = 1 To UBound(a, 1)
                        If (b(i, 2) Like a(ii, 1) & "*") * (a(ii, 5) <> Chr(2)) Then
                            mySum = mySum + a(ii, 5): a(ii, 5) = Chr(2)
                        End If
                    Next
                End If
                If i = UBound(b, 1) Then b(myRow, 1) = mySum
            Next
            .Range("k1").Resize(UBound(b, 1)).Value = b
        End With
        ReDim b(1 To UBound(a, 1), 1 To 1): b(1, 1) = "集計漏れ"
        For i = 1 To UBound(a, 1)
            If a(i, 5) <> Chr(2) Then b(i, 1) = "←"
        Next
        With .Range("g1").Resize(UBound(b, 1))
            .Value = b
            .Replace Chr(2), ""
        End With
    End With
End Sub

(seiya) 2019/03/14(木) 12:26


 以下、マナさんのコードを参考にさせていただきます。m(__)m

 マクロ「洗い出しand集計」を実行すると以下のような結果が出ます。

 <一覧 結果図>
  行  ______A______  ______B______  ___C___ _J_  __K__  _L_  ______M______
   1  会社名         検索用会社名   ・・・       金額           1行,田中  
   2  田中製造所     田中製造所     ・・・        7000          2行,東洋  
   3                 TANAKA                                     6行,佐藤  
   4                 タナカ                                        7行,たなか
   5                 伊勢三重                                   8行,トーヨー  
   6  佐藤製鉄所     佐藤製鉄所     ・・・       12000         11行,さとう
   7                 SATO                                      13行,トーヨー  
   8                 静岡富士                                             
   9  東洋株式会社   東洋株式会社   ・・・        7000                    
  10                 TOYO                                                 
  11                 東洋山梨                                             
  12                 東洋名古屋                                                                          

 M列に集計に漏れたデータ、つまり「検索用会社名をメンテする必要がある名前」が出ますので、
 全てをB列に反映させて、再度「洗い出しand集計」を実行してください。

 ※「田中」もチャンと一覧シートのB列に載せない限り、
   集計に反映させないようにしております。
   (それは、堅実な事務処理上、必須と考えます)
  Option Explicit

 Sub 洗い出しand集計()
     Const 親列 As Long = 1
     Const 検索列 As Long = 2
     Dim dicT As Object       'Dictionary
     Dim 漏れリスト As Object 'ArrayList
     Dim 一覧名s
     Dim Idx As Long, RW As Long
     Dim データ名, データ金額, amtAdded()
     Dim VL
     Dim oyaRow As Long

     Set dicT = CreateObject("scripting.dictionary")

     With Sheets("一覧")
         一覧名s = .Range("A2", .Cells(.Rows.Count, "B").End(xlUp)).Value
         ReDim amtAdded(1 To UBound(一覧名s), 1 To 1)
     End With

     Rem まず、一覧に重複が無いかチェックする
     Dim isChecking As Boolean

     For Idx = 1 To UBound(一覧名s)
         If 一覧名s(Idx, 親列) <> "" Then '親行に該当
             isChecking = True
             oyaRow = Idx
             If 一覧名s(Idx, 親列) <> 一覧名s(Idx, 検索列) Then    '右隣りが親と同名かチェック
                 MsgBox "同行に同会社名が無いです。想定外です。処理中止。"
                 Exit Sub
             End If
         End If

         If isChecking And isNew(dicT, 一覧名s(Idx, 検索列), oyaRow) = False Then
             MsgBox "一覧に重複があります。(行=" & Idx + 1 & "、社名=" & _
             一覧名s(Idx, 検索列) & ") 処理中止"
             Exit Sub
         End If

     Next Idx

     Rem 漏れリストを作成する。

     Set 漏れリスト = CreateObject("system.collections.arraylist")

     With Sheets("データ")
         データ名 = .Range("A1", .Cells(.Rows.Count, "A").End(xlUp)).Value
         データ金額 = .Range("A1", .Cells(.Rows.Count, "A").End(xlUp)).Offset(0, 4).Value
     End With

     For RW = 1 To UBound(データ名)
         If データ名(RW, 1) <> "" Then
             VL = getUCase(データ名(RW, 1)) '統一名を取得:「株 Abc」→「株ABC」
             If Not dicT.exists(VL) Then
                 漏れリスト.Add Application.Text(RW, "???0行、") & VL
             Else
                 amtAdded(dicT(VL), 1) = amtAdded(dicT(VL), 1) + データ金額(RW, 1)
             End If
         End If

     Next RW

     Rem 結果を打ち出す

     With Sheets("一覧")
         .Range("K2").Resize(UBound(amtAdded), 1).Value = amtAdded

         .Columns("M").ClearContents

         If 漏れリスト.Count > 0 Then
             .Range("M1").Resize(漏れリスト.Count, 1).Value = Application.Transpose(漏れリスト.toarray)
             MsgBox "漏れあり、検索用会社名のメンテ要。"
         Else
             MsgBox "漏れはありません。"
         End If
     End With
 End Sub

 Private Function isNew(ByRef dicT As Object, ByVal Val, RW As Long) As Boolean
     Dim VL
     VL = getUCase(Val)

     If dicT.exists(VL) Then
         isNew = False
         Exit Function
     Else
         isNew = True
         dicT(VL) = RW
     End If
 End Function

 Private Function getUCase(Val) '統一名を作る。「株 Abc」→「株ABC」
     getUCase = UCase(Replace(Application.Asc(Val), " ", ""))
 End Function

 (半平太) 2019/03/14(木) 23:49

 へんてこな二重ループが気になっていたので、修正しました。

 2019/03/17(日) 23:02


半平太さま

 If 一覧名s(Idx, 親列) <> "" Then '親行に該当

がエラーになります

(もも) 2019/03/18(月) 08:55


 seiyaさんの回答もありますので、それを先に試してください。

 その結果によって、私の案について検討するかどうか決めます。(あと、今日もちょっと用事があります)

(半平太) 2019/03/18(月) 10:06


 アッ、私のは試したくない、或は結果が希望したのもではなかったのでしょうから無視して結構です。
(seiya) 2019/03/18(月) 10:43

seiyaさま

金曜日の帰りにフォーマットが変わってしまいまして

【一覧】

   A          B            ・・・ K
 1 会社名     検索用会社名 ・・・ 金額

↓↓

   C          D            ・・・ K
 1 会社名     検索用会社名 ・・・ 金額

【データ】

     A   ・・・  E
 1   会社名    金額

↓↓

     K   ・・・ L  
 1   金額     会社名

と項目の行が変わってしまったため、いただいたコードを変更する必要がありました。
半平太さまのコードは直しやすかったので、先に回答しました。
seiyaさまのはどこをどう直せばわからず格闘している真っ最中でございます。。すみません。。

(もも) 2019/03/18(月) 11:03


=IF(COUNTIF(表!D:D,"*"&L2&"*"),"","要確認")

この式をデータのL列の横のM列に入れるマクロ

'====================名称不一致確認

    Range("M2").Select
    Range("M2:M" & Range("H" & Rows.Count).End(xlUp).Row).Formula = "=IF(COUNTIF(一覧!C[-9],""*""&RC[-1]&""*""),"""",""要確認"")"

を入れて解決としました。
アイデアいただきありがとうございました。
(もも) 2019/03/19(火) 17:48


コメント返信:

[ 一覧(最新更新順) ]


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