質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

Q&A

解決済

1回答

1232閲覧

Excelで表に対して、VBAでカスタムなソートを実行したい

xail2222

総合スコア1497

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

0グッド

0クリップ

投稿2021/12/04 23:44

編集2021/12/07 14:23

前提・実現したいこと

Excelで並べ替えをしたい。

列1列2列3
100A0001-1
100A0002-1
100A0011-1
100B0001-1
100B0001-1-1
101A0001-1
101A0001-2
101A0001-12

というような感じで並べ変えをしたい。

第1ソートキーは列1で第2ソートキーは列2です。
ただし、ソートはハイフン区切りで数値化できるものは数値化してソートです。

標準機能のソートでは、実現できないカスタムなソートを
どのように実現すればいいのかという質問です。

プログラミング言語によっては、カスタムなCompare関数を定義して
ソートを実行するとかいう感じです。

ソート用の比較関数は、以下のようなものを作成済みと考えてください。

VBA

1Public Function myCompare(a1 As String, a2 As String, b1 As String, b2 As String) As Long 2 ' aとbの比較を行って、aの方が小さい場合は-1、同じ場合は0、aの方が大きい場合は1を返す 3 ' 実装は略 4End Function

また、ソート処理を実装するマクロとソート処理対象のエクセルは別ファイルを想定しています。

どのような実装を行えば、これが実現できるのか検討しています。
何かいい方法があれば教えてください。

試してみようとしていること

今考えているマクロの内容は

1.選択範囲の表の最終列のもう一つ右の列に、順位を数値で設定
2.順位の列でソート
3.順位の列を削除

という感じなのを考えています。

補足情報(FW/ツールのバージョンなど)

Office 365

追記

ソート用の列を追加するという上述の案の方針で
以下のような実装をしてみました。
このままではソートのアルゴリズムが遅いので、早いソートのアルゴリズムにすれば
少しは早くなりました。

VBA

1Public Sub CallSort(rRange As Range) 2 Dim tSheet As Worksheet 3 Set tSheet = rRange.Parent 4 Dim tRange As Range 5 Set tRange = rRange 6 7 tRange.Columns(tRange.Columns.Count + 1).Insert Shift:=xlToRight 8 9 Dim tCol1 As Range 10 Dim tCol2 As Range 11 Set tCol1 = tRange.Columns(1) 12 Set tCol2 = tRange.Columns(2) 13 14 Dim tV1 As Variant 15 Dim tV2 As Variant 16 17 Dim tR1 As Long 18 Dim tR2 As Long 19 Dim tC1 As Long 20 Dim tC2 As Long 21 22 23 tR1 = tRange.Row 24 tR2 = tR1 + tRange.Rows.Count - 1 25 tC1 = tRange.Column 26 tC2 = tC1 + tRange.Columns.Count 27 28 Set tRange = tSheet.Range(tSheet.Cells(tR1, tC1), tSheet.Cells(tR2, tC2)) 29 30 tV1 = tCol1.Value 31 tV2 = tCol2.Value 32 33 ReDim tOrder(1 To UBound(tV1, 1)) 34 ReDim tOrder2(1 To UBound(tV1, 1)) 35 For tR = 1 To UBound(tV1, 1) 36 tOrder(tR) = tR 37 Next 38 39 MySort tOrder, tV1, tV2 40 41 For tR = 1 To UBound(tV1, 1) 42 tOrder2(tOrder(tR)) = tR 43 Next 44 45 tRange.Columns(tRange.Columns.Count).Value = WorksheetFunction.Transpose(tOrder2) 46 47 tSheet.Sort.SortFields.Clear 48 tSheet.Sort.SortFields.Add2 Key:=tRange.Columns(tRange.Columns.Count), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal 49 With tSheet.Sort 50 .SetRange tRange 51 .Header = xlNo 52 .MatchCase = False 53 .Orientation = xlTopToBottom 54 .SortMethod = xlPinYin 55 .Apply 56 End With 57 tRange.Columns(tRange.Columns.Count).Delete Shift:=xlToLeft 58 59End Sub 60 61Public Sub MySort(r_AryRow As Variant, r_V1 As Variant, r_V2 As Variant) 62 Dim i As Long 63 Dim j As Long 64 Dim tTmp As Variant 65 For i = UBound(r_AryRow) To LBound(r_AryRow) + 1 Step -1 66 For j = LBound(r_AryRow) To i - 1 67 Dim rtn As Long 68 rtn = Compare(r_V1(r_AryRow(j), 1), r_V2(r_AryRow(j), 1), r_V1(r_AryRow(j + 1), 1), r_V2(r_AryRow(j + 1), 1)) 69 If rtn > 0 Then 70 tTmp = r_AryRow(j) 71 r_AryRow(j) = r_AryRow(j + 1) 72 r_AryRow(j + 1) = tTmp 73 End If 74 Next 75 Next 76End Sub

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

作業列を使ってそこでソートするという考え方でいいと思います。

いろいろな方法が考えられますが、列2をエクセルの「区切り位置」機能で分割して作業列に出力して、ソートするという方法で作成してみました。

マクロ実装ブックの標準モジュール

vba

1Sub MySort(Tbl As Range) 2 Dim tmpTbl As Range 3 Set tmpTbl = Tbl.Resize(Tbl.Rows.Count, 5) 4 5 '2列目をハイフンで分割して4列,5列に出力 6 tmpTbl.Columns(2).TextToColumns _ 7 Destination:=tmpTbl.Columns(4), Other:=True, OtherChar:="-", _ 8 FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 9)) 9 10 '1列,4列,5列を基準にソート 11 With tmpTbl.Parent.Sort 12 .SortFields.Clear 13 .SortFields.Add2 Key:=tmpTbl.Columns(1), Order:=xlAscending 14 .SortFields.Add2 Key:=tmpTbl.Columns(4), Order:=xlAscending 15 .SortFields.Add2 Key:=tmpTbl.Columns(5), Order:=xlAscending 16 .SetRange Range("A1:E9") 17 .Header = xlYes 18 .Apply 19 End With 20 '作業列(4,5列)を削除 21 tmpTbl.Columns("D:E").Clear 22End Sub

上記マクロの呼び出し例

vba

1MySort Workbooks("ソート対象ブック").Sheets(1).Cells(1,1).Currentregion

投稿2021/12/05 02:01

hatena19

総合スコア33699

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

xail2222

2021/12/07 14:29

回答ありがとうございます。 やはり、ソート用の列を追加するという方針になるでしょうか。 ただ、実際の比較のルールがもう少し複雑なのです。 そのため自作の比較関数を使って、ソートをしたかったので その実装例を考えてみました。 ソートのアルゴリズムを自前で書かないといけないというのが 今時じゃないなと思ったりしています。
hatena19

2021/12/08 00:25

残念ながら、VBAは今時の言語ではないですからね。(10年以上前に更新が止まっている) 検索すれば、VBAでのバブルソースとかクイックソートのサンプルコードは見つかるので、それの比較部分を自作の比較関数にすれば実装はできると思います。 作業列をうまく使えば複雑なソートでは可能だとは思います。 どちらが楽かは、プログラマーの経験、スキルによると思います。
hatena19

2021/12/08 01:29

データーベース的観点でみると、ソートの条件が複雑になるというのは、データの正規化ができていないということになります。データを取得、入力した時点で列を分割しておく(正規化しておく)という発想も、処理のシンプル化、高速化に有効だと思います。
xail2222

2021/12/11 00:28

ソートのアルゴリズムは、ヒープソートにしました。 サンプルとして受領した2万件のデータで15秒だったので まぁ。運用は可能かなと感じました。 ソートの条件を詳しくヒアリングしていくと、ハイフン区切りが何個あるのか不明だったり ハイフン区切りで区切った後も、99AA11と言うようなデータがあるようで そこも数値、数値以外で、区切って要素ずつで比較する必要があるということがわかりました。 あと甲乙の順番も出来たらいいな。とか 正確に、どのような内容が入っているのかはわからないけれど 上記のルールでソートしてほしい。という要望でしたので 自作の比較関数以外では、私には出来ませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問