- 締切済み
エクセルVBA 全ての組み合わせを作る
エクセルVBAで、全ての組み合わせを表現する方法を教えてください 既にA列に色、B列に数字、C列にサイズが入力済みだとします A B C 1 色 数字 サイズ ←タイトルの行 2 赤 1 S 3 青 2 M 4 緑 3 L (完成例) D E F 1 色 数字 サイズ ←タイトルの行 2 赤 1 S 3 赤 1 M 4 赤 1 L 5 赤 2 S 6 赤 2 M 7 赤 2 L 8 赤 3 S 9 赤 3 M 10 赤 3 L 11 青 1 S 12 青 1 M 13 青 1 L 14 青 2 S 15 青 2 M 16 青 2 L 17 青 3 S 18 青 3 M 19 青 3 L 20 緑 1 S 21 緑 1 M 22 緑 1 L 23 緑 2 S 24 緑 2 M 25 緑 2 L 26 緑 3 S 27 緑 3 M 28 緑 3 L ・全ての組み合わせが表現できていれば、2~28行目は上の完成例の順番でなくてもいいです ・完成はD~F列の上から(2行目から)結果を反映させ、空白行を作らないようにする ・今回はA~C列の4行まで入力されている例をあげましたが、実際はA~C列の何行まで入力されているか随時変更します ・A~C列のデータ入力は、必ず上から(2行目から)されています ・A~C列のデータ入力は、5行目以降に続くこともあります ・A~C列のデータ入力は、データがない場合もあります (データ入力がない場合) A B C 1 色 数字 サイズ ←タイトルの行 2 1 S 3 2 M 4 L (この場合の完成例) D E F 1 色 数字 サイズ ←タイトルの行 2 1 S 3 1 M 4 1 L 5 2 S 6 2 M 7 2 L 空白行が出来てしまってはいけないので、『まずデータ入力されている列を認識し、認識した列の情報で全ての組み合わせを作成する』という考え方なのかな?と思ったのですが、そのようなことをエクセルVBAでできるのでしょうか もし分かる方がいたら教えてください よろしくお願いします
- みんなの回答 (7)
- 専門家の回答
みんなの回答
- 某HN クロメート(Chromate)(@CoalTar)
- ベストアンサー率40% (705/1742)
関数だと速いのかなあ なんて思いから ちょっとやってみました Sub 並べるXL2003() Dim n As Long Dim n2 As Long Dim n3 As Long Dim n4 As Long Range("D1").Formula = "=IF(A2="""","""",COUNTA(A:A)-1)" Range("E1").Formula = "=IF(B2="""","""",COUNTA(B:B)-1)" Range("F1").Formula = "=IF(C2="""","""",COUNTA(C:C)-1)" Range("G1").Formula = "=PRODUCT(E1,F1)" Range("H1").Formula = "=PRODUCT(D1:F1)" If Range("H1") > 65535 * 4 Then Exit Sub n = WorksheetFunction.Min(Range("H1") + 1, 65536) Range("D2:D" & n).Formula = "=INDEX(A:A,2+(ROW()-2)/G$1)" Range("D2:D" & n).Value = Range("D2:D" & n).Value Range("E2:E" & n).Formula = "=INDEX(B:B,2+MOD((ROW()-2)/F$1,E$1))" Range("E2:E" & n).Value = Range("E2:E" & n).Value Range("F2:F" & n).Formula = "=INDEX(C:C,2+MOD(ROW()-2,F$1))" Range("F2:F" & n).Value = Range("F2:F" & n).Value If Range("H1") <= 65535 Then Exit Sub n2 = WorksheetFunction.Min(Range("H1") - 65535 + 1, 65536) Range("G2:G" & n2).Formula = "=INDEX(A:A,2+(ROW()-2+65535)/G$1)" Range("G2:G" & n2).Value = Range("G2:G" & n2).Value Range("H2:H" & n2).Formula = "=INDEX(B:B,2+MOD((ROW()-2+65535)/F$1,E$1))" Range("H2:H" & n2).Value = Range("H2:H" & n2).Value Range("I2:I" & n2).Formula = "=INDEX(C:C,2+MOD(ROW()-2+65535,F$1))" Range("I2:I" & n2).Value = Range("I2:I" & n2).Value If Range("H1") <= 65535 * 2 Then Exit Sub n3 = WorksheetFunction.Min(Range("H1") - 65535 * 2 + 1, 65536) Range("J2:J" & n3).Formula = "=INDEX(A:A,2+(ROW()-2+65535*2)/G$1)" Range("J2:J" & n3).Value = Range("J2:J" & n2).Value Range("K2:K" & n3).Formula = "=INDEX(B:B,2+MOD((ROW()-2+65535*2)/F$1,E$1))" Range("K2:K" & n3).Value = Range("K2:K" & n2).Value Range("L2:L" & n3).Formula = "=INDEX(C:C,2+MOD(ROW()-2+65535*2,F$1))" Range("L2:L" & n3).Value = Range("L2:L" & n2).Value n4 = WorksheetFunction.Min(Range("H1") - 65535 * 3 + 1, 65536) Range("M2:M" & n4).Formula = "=INDEX(A:A,2+(ROW()-2+65535*3)/G$1)" Range("M2:M" & n4).Value = Range("M2:M" & n2).Value Range("N2:N" & n4).Formula = "=INDEX(B:B,2+MOD((ROW()-2+65535*3)/F$1,E$1))" Range("N2:N" & n4).Value = Range("N2:N" & n2).Value Range("O2:O" & n4).Formula = "=INDEX(C:C,2+MOD(ROW()-2+65535*3,F$1))" Range("O2:O" & n4).Value = Range("O2:O" & n2).Value End Sub 約25万件になるデータが11秒ほどでできました 遊びですがf(^^;
- tom04
- ベストアンサー率49% (2537/5117)
No.5です。 たびたびごめんなさい。 前回の >No.1さんの補足に は >No.2さんの の間違いです。ごめんなさい。 それと、前回のコードはA~C列の1行目からデータがある!という前提のコードでしたが 1行目は項目行になっていてデータは2行目からあるのですよね? For i = 1 To Cells(Rows.Count, "A").End(xlUp).Row For j = 1 To Cells(Rows.Count, "B").End(xlUp).Row For k = 1 To Cells(Rows.Count, "C").End(xlUp).Row の3行の「1」をすべて「2」に変更してください。 どうも失礼しました。m(_ _)m
- tom04
- ベストアンサー率49% (2537/5117)
こんにちは! 横からお邪魔します。 No.1さんの補足に >今のところ、最大で238,238の組み合わせが生じそうです とありますので・・・ Sub Sample1() Dim i As Long, j As Long, k As Long, cnt As Long, myRow As Long, myCol As Long, endCol As Long endCol = ActiveSheet.UsedRange.Columns.Count If endCol > 3 Then Range(Columns(4), Columns(endCol)).ClearContents End If For i = 1 To Cells(Rows.Count, "A").End(xlUp).Row For j = 1 To Cells(Rows.Count, "B").End(xlUp).Row For k = 1 To Cells(Rows.Count, "C").End(xlUp).Row cnt = cnt + 1 If cnt Mod 65536 = 0 Then myRow = 65536 myCol = Int(cnt / 65536) * 3 + 1 Else myRow = cnt Mod 65536 myCol = (Int(cnt / 65536) + 1) * 3 + 1 End If With Cells(myRow, myCol) .Value = Cells(i, "A") .Offset(, 1) = Cells(j, "B") .Offset(, 2) = Cells(k, "C") End With Next k Next j Next i MsgBox "処理完了" End Sub ※ じっくり考えれば1セルずつ舐めるように表示させるより、一定範囲をコピー&ペーストすれば もっと早くなると思いますが、65536行目がどこで終わるか?の判断が難しくなりますので、 単純にずらぁ~~~!っと並べています。 腕組みをしてじっくり画面とにらめっこしてください。m(_ _)m
- keithin
- ベストアンサー率66% (5278/7941)
んじゃまぁついでに。 sub macro1for2003() dim c1 as long, c2 as long, c3 as long dim r as long dim w as worksheet set w = activesheet range("D:F").clearcontents range("A1:C1").copy range("D1") r = 2 for c1 = 2 to application.max(2, w.cells(rows.count, "A").end(xlup).row) for c2 = 2 to application.max(2, w.cells(rows.count, "B").end(xlup).row) for c3 = 2 to application.max(2, w.cells(rows.count, "C").end(xlup).row) if r > 65536 then worksheets.add after:=activesheet w.range("A1:C1").copy range("D1") r = 2 end if cells(r, "D").value = w.cells(c1, "A").value cells(r, "E").value = w.cells(c2, "B").value cells(r, "F").value = w.cells(c3, "C").value r = r + 1 next c3 next c2 next c1 end sub 言わずもがなですが、わざわざ65536までひっぱる必要は勿論ありません。 もっとも10万行を超える書き出しを逐一行ってると、随分とろくさいですけどね。
お礼
2003用までありがとうございます このようなコードを作成したおことがないのですが、最終行までいくと、新しいシートが作成させるのでしょうか? できれば一つのシートで完成させたいので、(1)エクセル2003では使用しない、(2)入力できるデータ数を制限する、で何とか1つのシートで完成しようと思います 本当に助かりました
- keithin
- ベストアンサー率66% (5278/7941)
>●=2は何を意味するのでしょうか? ABC列に記入が無い場合の対処です。 >■で65536を使わずにできないかな?と思い 現実問題としてABC列に6万を超える「リストの元ネタ」を、本当に並べるつもりがあるのですか?(DEF列の事じゃ勿論ありませんよ) もしマジメにそうする必要があってそうしたいと言うのでしたら、勿論そのように対処してください。 ふつーに考えると「A9999」とかでも全然十分だと思ってましたけどね。 sub macro1r1() dim c1 as long, c2 as long, c3 as long dim r as long range("D:F").clearcontents range("A1:C1").copy range("D1") r = 2 for c1 = 2 to application.max(2, cells(rows.count, "A").end(xlup).row) for c2 = 2 to application.max(2, cells(rows.count, "B").end(xlup).row) for c3 = 2 to application.max(2, cells(rows.count, "C").end(xlup).row) cells(r, "D").value = cells(c1, "A").value cells(r, "E").value = cells(c2, "B").value cells(r, "F").value = cells(c3, "C").value r = r + 1 next c3 next c2 next c1 end sub
お礼
分かりやすい説明ありがとうございます 後になり、Cells(Rows.Count, 1).End(xlUp).Row で解決すると気が付きました ありがとうございます
- MarcoRossiItaly
- ベストアンサー率40% (454/1128)
基本的には No.1 さんの方法でいいと思いますが、シートの最大の行数に注意してください。Excel 2003 以前で 65,536、Excel 2007 以後で 1,048,576 です。 組み合わせの数は、爆発的に増加します。質問文の例だと色・数字・サイズという 3 属性が 3 種類ずつなので、組み合わせの数は 3^3 = 27 通りとなりますね。 しかし 41 種類ずつだと 41^3 = 68,921、102 種類ずつだと 102^3 = 1,061,208 となって、すぐに制限を超えてしまいます。他にも例えば、色・数字・サイズが 20・350・10 であれば 20 x 350 x 10 = 70,000、120・1,000・10 であれば 120 x 1,000 x 10 = 1,200,000 などとなり、やはりオーバーします。 ですから制限を超えそうな場合は、途中で列を変えて続きを記入していくなどの工夫が必要になります。コード中、For の行に記述している行番号の変数(No.1 さんのコードで言えば c1 あたり)の最大値を調節することなどによって、途中で無事に終われます。 そうしておかないと、最大の行数までの記入は実行されますが、その次の記入でマクロがエラーになります。 なおマクロの実行にかかる時間は、環境にもよりますが、100 万行を記入するなら何分かかかると思ってください。 >……、『まずデータ入力されている列を認識し、……と思ったのですが、そのようなことをエクセルVBAでできるのでしょうか 条件に関する必要な情報が十分に与えられれば、その手のことはいくらでも可能だと思います。しかし難しいことをしようとするほど、VBA に関する様々な知識が必要になっていきます。まずは基本の処理を習得し、それ以上のことは追々、学んでいってください。
お礼
回答ありがとうございます 今のところ、最大で238,238の組み合わせが生じそうです 既にエクセル2003では超えてしまいますね また、データが今後増えていくことが予想できるので エクセル2003で使用できるように、入力できるデータ数を制限するのも一つの手かなと考えています ありがとうございました
- keithin
- ベストアンサー率66% (5278/7941)
奇をてらわずに、単純にぐるぐる廻してくだけで十分です。 sub macro1() dim c1 as long, c2 as long, c3 as long dim r as long range("D:F").clearcontents range("A1:C1").copy range("D1") r = 2 for c1 = 2 to application.max(2, range("A65536").end(xlup).row) for c2 = 2 to application.max(2, range("B65536").end(xlup).row) for c3 = 2 to application.max(2, range("C65536").end(xlup).row) cells(r, "D").value = cells(c1, "A").value cells(r, "E").value = cells(c2, "B").value cells(r, "F").value = cells(c3, "C").value r = r + 1 next c3 next c2 next c1 end sub
お礼
早い回答ありがとうございます 意外と短く表現できるんですね! 助かりました Application.Max(●, ■)で、最大行を取得しているんだと思うのですが、 ●=2は何を意味するのでしょうか? また、■で65536を使わずにできないかな?と思い Cells(Rows.Count, 1).End(xlUp) としましたが、うまくいきませんね・・
お礼
回答ありがとうございます 返信が遅くなりすみませんでした