[[20230404125527]] 『5行毎に横に並べたい(関数)』(サクラ) ページの最後に飛ぶ

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

 

『5行毎に横に並べたい(関数)』(サクラ)

以下の相談で回答を受けて汎用の関数を作成できないか?と考えました。

『5行毎に横に並べたい』
https://www.excel.studio-kazu.jp/kw/20230403125946.html

    Range("B1").Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/5,5))"
    Range("B1#").Value = Range("B1#").Value
    Columns(1).Delete

以下のように変更してみましたが、

Option Explicit

Function 行分割(出力列 As Long, 指定行数 As Long)

    Cells(1, 出力列).Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/指定行数,指定行数))"
    Cells(1, 出力列).Value = Cells(1, 出力列).Value
    'Columns(1).Delete
End Function

行分割(2,5)では、B1に#VALUE!と表示されます。
どのように変更したらエラーが無くなりますか?

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


 変数まで""でくくってはいけません。
(MK) 2023/04/04(火) 13:01:44

mkさん、アドバイスありがとうございます。

以下に変更してみると
コードとして見なしてくれません。

Cells(1, 出力列).Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/" & 指定行数,指定行数))

Cells(1, 出力列).Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/" 指定行数,指定行数))

(サクラ) 2023/04/04(火) 13:32:42


以下でコードとしては認識しているようですが
やはり同じエラーがでます。

B1 に =行分割(A1,5)

Cells(1, 出力列).Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/" & 指定行数 & "," & 指定行数 & "))"
(サクラ) 2023/04/04(火) 14:18:29


最初のコードのみ残してその後の行はコメントアウトすると
形式的にはエラー無く上手く処理できました。

=行分割(A1,5)

Function 行分割(指定行数 As Long)

    Range("B1").Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/" & 指定行数 & "," & 指定行数 & "))"
    'Range("B1#").Value = Range("B1#").Value
    'Columns(1).Delete
End Function

以下は、結果を関数で出たのを文字列に変換しているのだと思いますが、合ってますか?
Range("B1#").Value = Range("B1#").Value

"B1#"の意味はどう言うことか教えて下さい。
  #の意味が判らない

(サクラ) 2023/04/04(火) 16:01:09


https://support.microsoft.com/ja-jp/office/%E3%82%B9%E3%83%94%E3%83%AB%E7%AF%84%E5%9B%B2%E6%BC%94%E7%AE%97%E5%AD%90-3dd5899f-bca2-4b9d-a172-3eae9ac22efd
 スピル範囲演算子 らしいです。
(稲葉) 2023/04/04(火) 16:40:46

アドバイスありがとうございます。

#の意味は、スピル範囲演算子。

Range("B1#")でB列で使用されているセルまでの範囲。
具体的には、B24まで使用されていれば、Range("B1:B24")相当

少しコードを変更して以下のようにすると

Function 行分割(出力列 As Long, 指定行数 As Long)

    Cells(1, 出力列).Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/" & 指定行数 & "," & 指定行数 & "))"
End Function

出力範囲以外のセル(例えばB1)をセレクト状態で関数を呼び出してパラメーター3,5を指定してOKをクリックすると
B1セルに「=行分割(3,5)」のように関数が表示されて出力範囲に結果が出力されますが
B1セルに#VALUE!と表示されます。

逆に出力範囲内のセルをセレクトして関数を呼び出すとのエラー表示(#SPILL!,#VALUE!)が出て
何も出力されませんでした。

これを修正することは出来ますか?

修正できないようならこの関数の利用は諦めて他の方法を考えます。

(サクラ) 2023/04/04(火) 17:20:36


 それってB1セルに=行分割(3,5)って入れてます?
 おそらくFunctionの使い方を勘違いされてます。
 Functionは値を戻す関数ですので、例えば下記のコードを入れて、
 A1=test(1,2)
 とすると、A1セルに3が表示されます。
    Function test(x As Long, y As Long) As Long
        test = x + y
        '~~~~~~~~~~~~
        'この部分でFunction testに値を戻し入れている
    End Function

 サクラさんのコードに戻ります。
 セルに書き出す処理は記載されていますが、Function 行分割になんの値を返すか記載してません。
 ですので、#Valueエラーが帰ります。

    Function 行分割(出力列 As Long, 指定行数 As Long)
        Cells(1, 出力列).Formula2 = "=INDEX(A:A,SEQUENCE(COUNTA(A:A)/" & 指定行数 & "," & 指定行数 & "))"
        '行分割 = hogehoge
        '~~~~~~~~~~~~~~~~~
        'この部分がない
    End Function

 考え方は二通りあって、Functionを使わず、SubプロシジャとINPUTBOXなどで出力するか
 Functionプロシジャの戻り値として配列を入れるかになると思います。
 スピルする製品を持ってないので、何とも言えないですが
    Function 行分割(対象列 As Range, 指定行数 As Long) As Variant()
        Dim f As String
        Dim v As Variant
        f = "=INDEX(♪,SEQUENCE(COUNTA(♪)/◆,◆))"
        f = Replace(f, "♪", 対象列.Address)
        f = Replace(f, "◆", 指定行数)
        v = Evaluate(f)
        行分割 = v
    End Function
 B1=行分割(A:A,5)
 ってやると勝手にスピルしませんかね・・・?

(稲葉) 2023/04/04(火) 18:47:49


稲葉さん、ありがとうございます。

>おそらくFunctionの使い方を勘違いされてます。

全くそのとうりです。

関数なので出力されるセルを指定(選択、アクティブ)にして
fxの手順 (fx>関数挿入>関数の引数でパラメター入力>OK)
にしないと#Valueエラーになる。

つまり、
= 行分割(出力列 As Long, 指定行数 As Long)

出力列が3(C列)の場合は、C1をセルを指定(選択、アクティブ)にしてfxしないとダメで
C1以外のセルを指定してfxしても#Valueエラーとなる。

なので出力列のパラメーターを指定する意味は無いと言うことになります。

C1を指定(選択、アクティブ)した時点で出力先はC列(C1)と決定。
(3ならC1を指定(選択、アクティブ)する=ここから出力される事になる)
'--------------------------------------------

アドバイスの以下を試してみました。

Option Explicit

Function 行分割(出力列 As Range, 指定行数 As Long)

    Dim f As String
    Dim v As Variant
    f = "=INDEX(♪,SEQUENCE(COUNTA(♪)/◆,◆))"
    f = Replace(f, "♪", 出力列.Address)
    f = Replace(f, "◆", 指定行数)
    v = Evaluate(f)
    行分割 = v
End Function

出力先はC:CだけどB1を指定(選択、アクティブ)にしてfxの手順を実施

残念ながら
B1=行分割(C:C,5) −−−−−> #VALUE!
勝手にスピルせずにエラーになります。

'-----------------------------------------------

>Functionを使わず、SubプロシジャとINPUTBOXなどで出力するか

処理列がA:Aと固定していますが、
隠居Zさんの以前のコードを拝借して以下で処理しています。

Sub 行分割2()

    Dim i As Long
    Dim r As Range
    Dim j As Long
    Dim x As Long
    Dim cOlp As Long
    Dim rOwp As Long
    Dim v() As Variant
    Dim w() As Variant

    With ActiveSheet
        Set r = Intersect(.Range("A:A"), .UsedRange)
        If r.Cells.Count = 1 Then
            ReDim v(1 To 1, 1 To 1)
            v(1, 1) = r(1, 1).Value
        Else
            v = r.Value
        End If
    End With

    Dim 指定行数 As String

    指定行数 = InputBox("何個ずつ取り出しますか ?", "指定行数")

    If StrPtr(指定行数) = 0 Then
        MsgBox "Cancelが選択されました。" & vbCrLf & _
               "処理を中止します。"
        Exit Sub
    End If

    指定行数 = Val(指定行数)

    If 指定行数 < 0 Then
        MsgBox "指定行数がマイナスは、有りえません。" & vbCrLf & _
               "処理を中止します。"
        Exit Sub
    End If

    x = IIf(UBound(v, 1) Mod 指定行数 > 0, (UBound(v, 1) \ 指定行数) + 1, UBound(v, 1) \ 指定行数)

    ReDim w(1 To x, 1 To 指定行数)

    rOwp = 1
    For i = 1 To UBound(v, 1)
        cOlp = cOlp + 1
        If cOlp > 指定行数 Then
            cOlp = 1
            rOwp = rOwp + 1
        End If
        w(rOwp, cOlp) = v(i, 1)
    Next

    Dim ws As Worksheet, flag As Boolean

    '書き出し前に「New」シートの存在をチェック > 存在していれば削除
    For Each ws In Worksheets
        If ws.Name = "New" Then flag = True
    Next ws

    If flag = True Then  'メッセージを表示せずシートを削除
        Application.DisplayAlerts = False 'メッセージを非表示
        Sheets("New").Delete
        Application.DisplayAlerts = True 'メッセージを表示
    End If

    '「New」シートに結果を書き出す。「New」は、シートの最後に追加
    Worksheets.Add After:=Worksheets(Worksheets.Count)
    ActiveSheet.Name = "New"

    '書き出し
    With Worksheets("New")
        .UsedRange.Clear
        .Cells(1).Resize(UBound(w, 1), UBound(w, 2)) = w
    End With
End Sub

(サクラ) 2023/04/05(水) 06:30:45


 第1引数は出力元を指定するんだけど、、、
(稲葉) 2023/04/05(水) 06:50:51

 もし汎用的に使いたいのであれば、Office365であれば
 =LET(範囲,A:A,列数,5,INDEX(範囲,SEQUENCE(COUNTA(範囲)/列数,列数)))
      ~~~~~~~~~~~~~~~
 LET関数使っておけばいいんじゃないですかね?

 Office365以前だとVBAに軍配上がる気はしますが・・・。

 いくつかやり方あると思うけど
 Subプロシジャから出力する場合、
 Functionプロシジャをシート上で配列として使う場合をそれぞれ列記しました。

 出力例
     |[A]|[B]|[C] |[D] |[E] |[F]|[G]|[H]                                     
 [1] |a  |   |    |    |    |   |   |SubプロシジャからC2を指定して出力       
 [2] |b  |   |a   |b   |c   |   |   |GetColToSEQUENCE(Range("A:A"), 3, True) 
 [3] |c  |   |d   |e   |f   |   |   |                                        
 [4] |d  |   |i   |l   |    |   |   |                                        
 [5] |e  |   |    |    |    |   |   |SubプロシジャからC7を指定して出力       
 [6] |f  |   |    |    |    |   |   |GetColToSEQUENCE(Range("A:A"), 3, False)
 [7] |   |   |a   |b   |c   |   |   |                                        
 [8] |   |   |d   |e   |f   |   |   |                                        
 [9] |i  |   |    |    |i   |   |   |                                        
 [10]|   |   |    |    |l   |   |   |                                        
 [11]|   |   |    |    |    |   |   |SubプロシジャからC13を指定して出力      
 [12]|l  |   |    |    |    |   |   |GetColToSEQUENCE(Range("A:A"), 5, True) 
 [13]|   |   |a   |b   |c   |d  |e  |                                        
 [14]|   |   |f   |i   |l   |   |   |                                        
 [15]|   |   |    |    |    |   |   |                                        
 [16]|   |   |    |    |    |   |   |C18:E23に配列数式で確定                 
 [17]|   |   |    |    |    |   |   |GetColToSEQUENCE(Range("A:A"), 3, True) 
 [18]|   |   |a   |b   |c   |   |   |                                        
 [19]|   |   |d   |e   |f   |   |   |                                        
 [20]|   |   |i   |l   |   0|   |   |                                        
 [21]|   |   |   0|   0|   0|   |   |                                        
 [22]|   |   |#N/A|#N/A|#N/A|   |   |                                        
 [23]|   |   |#N/A|#N/A|#N/A|   |   |                                        

 標準モジュールのコード
    Function GetColToSEQUENCE(RowRng As Range, ColCnt As Long, Optional IgnoreSpace As Boolean = False) As Variant()
        'GetColToSEQUENCE(変換対象範囲,横方向の列数指定,空白を無視するか)
        Dim v As Variant, RowCnt As Long
        Dim ans As Variant
        Dim r As Long, c As Long
        Dim cnt As Long
        '
        '//指定した列を配列vに取り込む
        v = Range(RowRng(1), Cells(Rows.Count, RowRng.Column).End(xlUp)).Value
        '
        '//vが配列になっているか確認し、処理を分岐
        If IsArray(v) Then
            '//二次配列を一次配列に変換
            v = Application.Transpose(v)
            '
            '//データ数÷列数で必要行数を確保し、出力用二次配列ansのサイズを確保
            RowCnt = WorksheetFunction.RoundUp(UBound(v, 1) / ColCnt, 0)
            ReDim ans(1 To RowCnt, 1 To ColCnt)
            cnt = 0
            For r = 1 To RowCnt
                For c = 1 To ColCnt
continue:
                    cnt = cnt + 1
                    '
                    '//配列がcntを上回ったら処理を抜ける
                    If UBound(v) < cnt Then Exit For
                    '
                    '//空白を無視する場合、vが空白以外までcntを増加する
                    If IgnoreSpace = True Then
                        If v(cnt) = "" Then GoTo continue
                    End If
                    ans(r, c) = v(cnt)
                Next c
            Next r
        Else
            '//vが配列以外なら、ans(1,1)にvの値を入れる
            ReDim ans(1 To 1, 1 To 1)
            ans(1, 1) = v
        End If
        '
        '//結果をFunctionプロシジャに返す
        GetColToSEQUENCE = ans
    End Function

 出力用Subプロシジャ
    Sub test()
        Dim a As Variant
        '変数aに、A列のデータを横3列で折り返すように指示
        a = GetColToSEQUENCE(Range("A:A"), 3, True)
        '結果をC1を先頭に出力
        Range("C1").Resize(UBound(a, 1), UBound(a, 2)).Value = a
        MsgBox "出力しました"

        '[使い方例]
    '    '変数aに、B列のデータを横3列で折り返すように指示
    '    a = GetColToSEQUENCE(Range("B:B"), 3, True)
    '
    '    '変数aに、A列のデータを横5列で折り返すように指示
    '    a = GetColToSEQUENCE(Range("B:B"), 5, True)
    End Sub

(稲葉) 2023/04/05(水) 10:03:36


稲葉さん、ありがとうございます。

>第1引数は出力元を指定するんだけど、

失礼しました。
自分の仕様に固執してちゃんと内容を見ていませんでした。

Function 行分割(出力列 As Range, 指定行数 As Long)

    Dim f As String
    Dim v As Variant
    f = "=INDEX(♪,SEQUENCE(COUNTA(♪)/◆,◆))"
    f = Replace(f, "♪", 出力列.Address)
    f = Replace(f, "◆", 指定行数)
    v = Evaluate(f)
    行分割 = v
End Function

もう一度
A1:A17が出力元(対象列)でC列(指定行数)に出力させるとして
(コードでは、「指定行数」と記載されていますが列番号の指定で良いのですよね ?)

B1を指定(選択、アクティブ)にしてfxの手順を実施

B1 =行分割(A1:A17,3)  −−−−−> #VALUE!
勝手にスピルせずにエラーになります。

=行分割(A:A,3) でも同じエラーでした。

'-------------------------------------------------

GetColToSEQUENCE関数をありがとうございます。

出力用Subプロシジャと直接ワークシートでfx手順で試してみました。
どちらも指定の列に出力されました。

疑問に思った点ですが、第3パラメーターですが
無指定(第3パラメーターのところを空白で指定しない)でも
TrueもしくはFalseでも下記3つの出力結果が同じでした。

=GetColToSEQUENCE(A1:A17,3)
=GetColToSEQUENCE(A1:A17,3,True)
=GetColToSEQUENCE(A1:A17,3,False)

第3パラメーターも存在意義は何でしょうか?
(利用すべき事例を教えてください。)

(サクラ) 2023/04/05(水) 14:11:25


すいません。

第3パラメーターの件、理解できました。

(サクラ) 2023/04/05(水) 14:47:33


 OFFICE365であれば、VBA使わず
 > =LET(範囲,A:A,列数,5,INDEX(範囲,SEQUENCE(COUNTA(範囲)/列数,列数)))
 が一番無難かと・・・
 繰り返しますが、自分は365持ってないのでスピルもSQUENCEもテストできてないです。
 365以前では
 C1=INDEX($A:$A,(ROW(A1)-1)*5+MOD(COLUMN(A1),6))
 下と右方向にコピーで同じ動作(空白の無視はできないが)ができます。

 本件に関しましては、もう少し基本(エクセル標準機能や3行マクロ)などを勉強されてから
 再度トライされてもよいような学習度とお見受けしました。

(稲葉) 2023/04/05(水) 15:09:07


稲葉さん、ありがとうございます。

>OFFICE365であれば、VBA使わず

ダメ元でExcel2021で試したら
=LET(範囲,A:A,列数,5,INDEX(範囲,SEQUENCE(COUNTA(範囲)/列数,列数)))
でスピルされて出力されました。

勉強不足なので頑張ります。
お付き合い願いありがとうございました。

(サクラ) 2023/04/05(水) 17:22:13


コメント返信:

[ 一覧(最新更新順) ]


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