前提・実現したいこと
初めて質問致します。
1行目に番号が振られていて、同じ番号の列の内容を行ごとに結合したいと考えています。
実際のものはデータが多い為、なるべく速く完了する方法はありませんでしょうか。
処理後のイメージは次のようにコロン等の記号を挟み、余分な列は削除された形です。
重たくなるようなら記号を挟まず結合でも構いません。元のデータでA2,B2セルやE2,F2のように値が同じ場合は重複せず出力したいです。
試したこと・発生している問題
行と列がそれぞれ数千ずつあり、次のような方法で試してみたのですが一向に処理が終わりません。中断後にシートを見たところ処理自体は進んでいるようでした。
- 値が重複しているセルの入力値を削除
・例えば1行目が"1"の列について
最初の列Aと2番目の列Bの値を各行繰り返し処理で比較。同じ値のセルがあればB列の該当セルをUnionメソッドで連結する。3番目C列以降の列も同様にA列と比較し連結。
・Unionメソッドに入れたセルの入力値をまとめてClear
_
0. 1行目の値が同じセルの値を結合
・TextJoin 関数で"1"の列(A:C)の文字列を各行ごとに結合
・最終行まで繰り返し
_
0. 1行目が"1"の列2番目以降の列は必要無いのでDelete
_
0. 1行目が"2"以降も、同じ番号の列が複数あれば繰り返す
最初、列をそれぞれ配列に格納し、要素が同じもの同士の値を連結できるような方法があればと思い調べましたが、配列内の全要素の連結しか見つからず… 初学者なので知識が足りていないかもしれませんが、どなたかお答え頂ければ幸いです。
※追記:1行目の番号は1からの連番です。出力先は別シートでも構いません。
"行と列がそれぞれ数千ずつ"と書きましたが、列は別の処理により減っているので、正しくは列が100、行が8000程度になります。
※追記2:セル内のデータですが、空白の場合もあります。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/02/19 13:51
2021/02/20 00:57 編集
2021/02/20 04:54
回答3件
0
以下のようにしてください。
こちらの環境で、10000行、150列のデータで約2分30秒かかりました。(corei5 メモリ12G)
Sheet1の内容を読み込み、Sheet2へ出力します。
VBA
1Option Explicit 2Public Sub 列統合() 3 Dim dicT As Object '番号対応の列を記憶 4 Dim dicV As Object '重複した値の削除用 5 Dim rg As Range 6 Dim maxrow As Long '最大行 7 Dim maxcol As Long '最大列 8 Dim wrow As Long '行 作業用 9 Dim wcol As Long '列 作業用 10 Dim vcol As Variant '列 作業用 11 Dim key As Variant 'キー 12 Dim keys As Variant 'キーの一覧 13 Dim val As Variant '値 14 Dim vals As Variant '値の一覧 15 Dim sh1 As Worksheet 'データのシート 16 Dim sh2 As Worksheet '出力先シート 17 Dim arr As Object '列のArrayList 18 Dim stime As Variant '開始時刻 19 Dim etime As Variant '終了時刻 20 Application.ScreenUpdating = False 21 stime = Time 22 Set dicT = CreateObject("Scripting.Dictionary") ' 連想配列の定義 23 Set dicV = CreateObject("Scripting.Dictionary") ' 連想配列の定義 24 Set sh1 = Worksheets("Sheet1") 25 Set sh2 = Worksheets("Sheet2") 26 Set rg = sh1.Range("A1").CurrentRegion 27 maxrow = rg.Rows.Count 28 maxcol = rg.Columns.Count 29 '番号を取得し、番号対応の列を記憶する 30 For wcol = 1 To maxcol 31 key = sh1.Cells(1, wcol).Value 32 If key = "" Then 33 MsgBox ("番号が空です") 34 Exit Sub 35 End If 36 If dicT.Exists(key) = False Then 37 '新規番号なら、空のArrayListを作成する 38 Set arr = CreateObject("System.Collections.ArrayList") '.NET Frameworkへの参照 39 dicT.Add key, arr 40 End If 41 '番号対応の列を追加する 42 dicT(key).Add wcol 43 Next 44 'Sheet2クリア&1行目作成 45 sh2.Cells.ClearContents 46 keys = dicT.keys 47 wcol = 1 48 For Each key In keys 49 sh2.Cells(1, wcol).Value = key 50 wcol = wcol + 1 51 Next 52 'Sheet1を2行から最終行まで繰り返す 53 For wrow = 2 To maxrow 54 '番号=1~最大番号迄繰り返す 55 keys = dicT.keys 56 wcol = 1 57 For Each key In keys 58 dicV.RemoveAll 59 '番号に対応する列の一覧を取得 60 Set arr = dicT(key) 61 '各列の重複削除及び空でない要素の登録 62 For Each vcol In arr 63 val = sh1.Cells(wrow, vcol).Value 64 If val <> "" Then 65 dicV(val) = True 66 End If 67 Next 68 '空でないなら、:で区切りSheet2へ出力 69 If dicV.Count > 0 Then 70 vals = dicV.keys 71 sh2.Cells(wrow, wcol).Value = Join(vals, ":") 72 End If 73 wcol = wcol + 1 74 Next 75 Next 76 etime = Time 77 Application.ScreenUpdating = True 78 MsgBox ("実行時間(時:分:秒)=" & Hour(etime - stime) & ":" & Minute(etime - stime) & ":" & Second(etime - stime)) 79End Sub 80
投稿2021/02/20 05:10
総合スコア5493
0
**「実際のものはデータが多い為、なるべく速く完了する方法」**ということなので、
表データを配列に格納して配列内で処理して、一気に出力する方法で考えてみました。
重複排除は、Dictionaryオブジェクトを利用しました。
そのため、参照設定で「Microsoft Scripting Runtime」にチェックを入れておく必要があります。
vba
1Option Explicit 2Const sep = ":" '区切り文字 3 4Public Sub Main() 5 Dim inTbl 6 inTbl = Worksheets(1).Cells(1, 1).CurrentRegion.Value 7 8 Dim outTbl 9 ReDim outTbl(1 To UBound(inTbl, 1), 1 To UBound(inTbl, 2)) 10 11 Dim c As Long, c1 As Long, c3 As Long 12 13 c1 = 1: c3 = 1 14 For c = 1 To UBound(inTbl, 2) - 1 15 If inTbl(1, c) <> inTbl(1, c + 1) Then 16 columnJoin inTbl, outTbl, c1, c, c3 17 c1 = c + 1: c3 = c3 + 1 18 End If 19 Next 20 columnJoin inTbl, outTbl, c1, c, c3 21 22 Worksheets(2).Cells(1, 1).Resize(UBound(outTbl), UBound(outTbl, 2)).Value = outTbl 23End Sub 24 25Sub columnJoin(inTbl, outTbl, c1 As Long, c2 As Long, c3 As Long) 26 Dim r As Long 27 outTbl(1, c3) = inTbl(1, c1) 28 For r = 2 To UBound(inTbl) 29 outTbl(r, c3) = RemoveDuplicatesBlank(inTbl, r, c1, c2) 30 Next 31End Sub 32 33Function RemoveDuplicatesBlank(Tbl, r As Long, c1 As Long, c2 As Long) As String 34 Dim dic As New Dictionary 35 Dim c As Long 36 For c = c1 To c2 37 If Tbl(r, c) <> "" And Not dic.Exists(Tbl(r, c)) Then dic(Tbl(r, c)) = c 38 Next 39 RemoveDuplicatesBlank = Join(dic.Keys, sep) 40End Function
一番目のシートに表データがあり、2番目のシートに出力するコードになってます。
同じシートに上書きする場合は、Mainの最後の行のWorksheets(2)
をWorksheets(1)
に変更するだけです。
投稿2021/02/20 01:17
編集2021/02/20 15:23総合スコア34075
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/02/20 04:58
2021/02/20 13:33
2021/02/20 15:22
2021/02/22 04:03
0
ベストアンサー
こんな感じでしょうか。
VBA
1Sub Sample() 2 3 Dim ws As Worksheet 4 Set ws = Sheets("Sheet1") 5 6 Dim d As Object 7 Set d = CreateObject("Scripting.Dictionary") 8 9 Dim r As Range, k As String 10 For Each r In ws.UsedRange 11 k = Join(Array(r.Row, r.Offset(1 - r.Row))) 12 If Not d.Exists(k) Then d.Add k, CreateObject("Scripting.Dictionary") 13 d(k)(r.Value) = 0 14 Next 15 16 ws.Cells.ClearContents 17 Dim e, rc, i As Long, j As Long 18 For Each e In d 19 rc = Split(e) 20 i = rc(0) 21 j = rc(1) 22 ws.Cells(i, j).Value = Join(d(e).Keys, ":") 23 Next 24 25End Sub 26 27
<追記>
空白セルもあるとのことなので、読み飛ばす処理を追加しました。
あわせて更なる速度向上のためセル範囲を配列化しました。
VBA
1Sub sample2() 2 3 Dim d As Object 4 Set d = CreateObject("Scripting.Dictionary") 5 6 Dim ws As Worksheet, arr As Variant 7 Set ws = Sheets("Sheet1") 8 arr = ws.UsedRange.Value 9 10 Dim r As Long, c As Long, k As Variant 11 For r = 1 To UBound(arr, 1) 12 For c = 1 To UBound(arr, 2) 13 k = Join(Array(r, arr(1, c))) 14 If Not d.Exists(k) Then d.Add k, CreateObject("Scripting.Dictionary") 15 If arr(r, c) <> "" Then d(k)(arr(r, c)) = 0 16 Next c, r 17 18 ws.Cells.ClearContents 19 For Each k In d 20 r = Split(k)(0) 21 c = Split(k)(1) 22 ws.Cells(r, c).Value = Join(d(k).Keys, ":") 23 Next 24 25End Sub
<再追記>
他の方のコードを拝見し、更に修正を加えました。
VBA
1 2Sub sample3() 3 4 Dim d1, d2 5 Set d1 = CreateObject("Scripting.Dictionary") 6 Set d2 = CreateObject("Scripting.Dictionary") 7 8 Dim ws, maxrow, maxcol, arr 9 Set ws = Sheets(1) 10 With ws.UsedRange 11 maxrow = .Rows.Count 12 maxcol = .Columns.Count 13 arr = .Value 14 End With 15 16 Dim c, k 17 For c = 1 To maxcol 18 k = arr(1, c) 19 If Not d1.Exists(k) Then Set d1(k) = CreateObject("Scripting.Dictionary") 20 d1(k).Add d1(k).Count, c 21 Next 22 ReDim arr2(1 To maxrow, 1 To d1.Count) 23 24 Dim r 25 For r = 1 To maxrow 26 For Each k In d1 27 d2.RemoveAll 28 For Each c In d1(k).Items 29 If arr(r, c) <> "" Then d2(arr(r, c)) = 0 30 Next 31 arr2(r, k) = Join(d2.keys, ":") 32 Next 33 Next 34 35 Set ws = Sheets(2) 36 ws.Cells.ClearContents 37 ws.Cells.Resize(maxrow, d1.Count).Value = arr2 38 39End Sub
投稿2021/02/20 01:10
編集2021/02/20 06:20総合スコア4592
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。