Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(指定なし : 指定なし)
配列について
投稿日時: 23/08/06 15:45:20
投稿者: yama1006
メールを送信

以下にコードを添付します。
 
Option Explicit
 
Function getlager(first, Second)
 
If first >= Second Then
 
 getlager = first
  
 Else
  
 getlager = Second
  
 End If
  
 
End Function
 
Function timetodecimal(timestring As Variant) As Double
     
    timetodecimal = timestring * 24
     
End Function
 
 
Sub 勤怠比較()
 
Dim ws1 As Worksheet, ws2 As Worksheet
 
Dim ws As Worksheet
 
 
Set ws = Sheets("使い方")
 
 
Set ws1 = Sheets("元データ")
 
Set ws2 = Sheets("受入データ")
 
 
 
'最終列シリーズ
 
Dim i As Long
 
Dim l As Long
 
Dim o As Long
 
 
 
 
 
'開始行
 
Dim r1 As Long
 
Dim r2 As Long
 
Dim r3 As Range
 
Dim r4 As Range
 
r1 = ws1.UsedRange.Rows(1).Row + 6
  
r2 = ws2.UsedRange.Rows(1).Row + 1
 
 
 
'find検索範囲
 
Set r3 = ws2.UsedRange.Rows(1)
 
Set r4 = ws1.UsedRange.Rows(6)
 
 
 
 
 
'検索のレンジ
 
 
Dim time As Range
 
Dim time2 As Range
 
 
Dim time3 As Range
 
Dim time4 As Range
 
Dim time5 As Range
 
Dim time6 As Range
 
Dim time7 As Long
 
Dim time8 As Long
 
'列格納
 
Dim zikan As Long
 
Dim zikan2 As Long
 
Dim zikan3 As Long
 
Dim zikan4 As Long
 
Dim zikan5 As Long
 
'検索列の格納
 
Dim kennsaku1 As Long
 
Dim kennsaku2 As Long
 
 
 
Dim maxrow As Long
 
 
Dim maxrow2 As Long
 
 
Dim s As Long '列の格納
 
Dim k As Long
 
ws2.Cells.ClearFormats
 
'比較最終行大きい方を取得
 
maxrow2 = getlager(ws1.UsedRange.Rows.Count, ws2.UsedRange.Rows.Count)
 
maxrow = ws.Cells(Rows.Count, 4).End(xlUp).Row
 
'比較列の大きい方を取得
 
Dim maxcol As Long
 
Dim o2 As Long
 
maxcol = getlager(ws1.UsedRange.Columns.Count, ws2.UsedRange.Columns.Count)
 
 
'検索の定義
 
Set time = r3.Find("SWTF010", lookat:=xlWhole) '出勤時間
 
Set time2 = r3.Find("SWTF030", lookat:=xlWhole) '普通残業時間
 
Set time3 = r3.Find("SWTF040", lookat:=xlWhole) '深夜残業時間
 
Set time4 = r3.Find("SWTF020", lookat:=xlWhole) '遅早時間
 
Set time5 = r3.Find("SWTF050", lookat:=xlWhole) '法定外休日時間
 
Set time6 = r3.Find("SWTF060", lookat:=xlWhole) '法定休日時間
 
For o = r2 To maxrow2
 
 
Application.ScreenUpdating = False
 
Application.Calculation = xlManual
 
 
If time Is Nothing Then
 
GoTo L1
 
Else
 
zikan = r3.Find("SWTF010", lookat:=xlWhole).Column
 
If ws2.Cells(o, zikan) <> "" Then
 
ws2.Cells(o, zikan) = timetodecimal(ws2.Cells(o, zikan))
 
ws2.Cells(o, zikan).NumberFormatLocal = "G/標準"
 
End If
 
End If
 
 
L1:
 
If time2 Is Nothing Then
 
GoTo L2
 
Else
 
 zikan2 = r3.Find("SWTF030", lookat:=xlWhole).Column
  
If ws2.Cells(o, zikan2) <> "" Then
  
 ws2.Cells(o, zikan2) = timetodecimal(ws2.Cells(o, zikan2))
 
ws2.Cells(o, zikan2).NumberFormatLocal = "G/標準"
 
End If
 
End If
 
 
L2:
 
If time3 Is Nothing Then
 
GoTo L3
 
Else
 
 zikan3 = r3.Find("SWTF040", lookat:=xlWhole).Column
  
If ws2.Cells(o, zikan3) <> "" Then
  
  ws2.Cells(o, zikan3) = timetodecimal(ws2.Cells(o, zikan3))
 
ws2.Cells(o, zikan3).NumberFormatLocal = "G/標準"
 
End If
 
End If
 
L3:
 
If time4 Is Nothing Then
 
GoTo L4
 
Else
 
 zikan4 = r3.Find("SWTF020", lookat:=xlWhole).Column
  
If ws2.Cells(o, zikan4) <> "" Then
 
 ws2.Cells(o, zikan4) = timetodecimal(ws2.Cells(o, zikan4))
 
 ws2.Cells(o, zikan4).NumberFormatLocal = "G/標準"
 
End If
End If
 
L4:
 
If time5 Is Nothing Then
 
GoTo L5
 
Else
 
 zikan5 = r3.Find("SWTF050", lookat:=xlWhole).Column
  
If ws2.Cells(o, zikan5) <> "" Then
  
  ws2.Cells(o, zikan5) = timetodecimal(ws2.Cells(o, zikan5))
 
 ws2.Cells(o, zikan5).NumberFormatLocal = "G/標準"
  
 
End If
End If
 
 zikan6 = r3.Find("SWTF060", lookat:=xlWhole).Column
  
If ws2.Cells(o, zikan6) <> "" Then
  
  ws2.Cells(o, zikan6) = timetodecimal(ws2.Cells(o, zikan6))
 
 ws2.Cells(o, zikan6).NumberFormatLocal = "G/標準"
  
 
End If
End If
 
 
L5:
 
Next
 
こちらですが、時間表記を145:30→145.5のように変換するためのコードになります。
このままでも使用自体は問題ないのですが、
 
Set time = r3.Find("SWTF010", lookat:=xlWhole) '出勤時間
 
Set time2 = r3.Find("SWTF030", lookat:=xlWhole) '普通残業時間
 
Set time3 = r3.Find("SWTF040", lookat:=xlWhole) '深夜残業時間
 
Set time4 = r3.Find("SWTF020", lookat:=xlWhole) '遅早時間
 
Set time5 = r3.Find("SWTF050", lookat:=xlWhole) '法定外休日時間
 
Set time6 = r3.Find("SWTF060", lookat:=xlWhole) '法定休日時間
 
例えば2行目の社員コードと各列見出し"SWTF010"などをそれぞれkeyとして配列に格納して
 
受入データの表記を一気に変換することは出来るでしょうか。。。。
 
 

回答
投稿日時: 23/08/06 16:16:33
投稿者: simple

まずは、シートレイアウトの説明をしてください。
そのうえで、サンプル例をあげて、実行したいことを説明してもらえますか?
 
# あなたはシートを目にしているので、当たり前のように思っていますが、
# 他人にはわかりませんので、(a)回答者の負荷軽減のためにも、
# (b)そして案外重要ですが、質問者の考え方の整理のためにも、
# 是非上記のことを実行してください。

投稿日時: 23/08/06 19:35:32
投稿者: yama1006
メールを送信

[quote="simple"]まずは、シートレイアウトの説明をしてください。
 
【使い方】sheets
【受入データ】sheets
【元データ】sheets
 
というシートに分かれております。
 
【受入データ】の1行目にコードがあり、
 
"SWTF010"
 
"SWTF030"
 
"SWTF040"
 
"SWTF020"
 
"SWTF050"
 
"SWTF060"
 
他にも色々なコードがあります。
 
"SWTF010"
 
"SWTF030"
 
"SWTF040"
 
"SWTF020"
 
"SWTF050"
 
"SWTF060"
 
こちらの列全体に×24をしたいと思っております。
 
現在は1行目をfindで検索して、該当のColumnを取得して2行目から最終行までループして24を掛けております。
 
実行したいことですが、
 
"SWTF010"
 
"SWTF030"
 
"SWTF040"
 
"SWTF020"
 
"SWTF050"
 
"SWTF060"
 
これらを配列に格納して、受入データのシートの1行目の見出しに、該当する列全体に24を掛けたいのですが、可能でしょうか?

回答
投稿日時: 23/08/06 20:12:26
投稿者: simple

例えば、こんな書き方はどうでしょうか。
 

Sub test1()
    Call maketime24(Range("A1").Resize(10, 1)) 
            '' 例です。引数は実際に沿って工夫してください。
End Sub

Function maketime24(rng As Range)
    Dim mat As Variant
    dim v   As Double
    Dim k   As Long

    mat = rng.Value
    For k = LBound(mat) To UBound(mat)
        v = mat(k, 1)
        If v <> Empty Then
            mat(k, 1) = v * 24
        End If
    Next
    rng.Value = mat
    rng.NumberFormatLocal = "G/標準"
End Function

# なお、動作確認していませんので、そちらで確認をお願いします。
 
老婆心ながら、気づいたことをメモします。
(1)インデントを正確につけたほうが、あなたにとって有益でしょう。
 
(2)Gotoは使わないほうがいいと思います。
   要するに、なければ次のブロックに移るだけですから、
 
   If Not time Is Nothing Then
        jikan = time.Column
        If ws2.Cells(o, jikan) <> "" Then
            ws2.Cells(o, jikan) = timetodecimal(ws2.Cells(o, jikan))
            ws2.Cells(o, jikan).NumberFormatLocal = "G/標準"
        End If
    End If

    などと、 GoTo など使わなくても書けます。
     
(3)繰り返しのなかで、
   r3.Find("SWTF010", lookat:=xlWhole)を何度も実行する必要は全く無いと思います。
   無駄をできるだけなくすようにされたほうがよいと思いました。
ざっと拝見して気づいた点です。
他の皆さんからの回答をお待ちください。

投稿日時: 23/08/07 00:03:06
投稿者: yama1006
メールを送信

simple さんの引用:
例えば、こんな書き方はどうでしょうか。
 
Sub test1()
    Call maketime24(Range("A1").Resize(10, 1)) 
            '' 例です。引数は実際に沿って工夫してください。
End Sub

Function maketime24(rng As Range)
    Dim mat As Variant
    dim v   As Double
    Dim k   As Long

    mat = rng.Value
    For k = LBound(mat) To UBound(mat)
        v = mat(k, 1)
        If v <> Empty Then
            mat(k, 1) = v * 24
        End If
    Next
    rng.Value = mat
    rng.NumberFormatLocal = "G/標準"
End Function

# なお、動作確認していませんので、そちらで確認をお願いします。
 
老婆心ながら、気づいたことをメモします。
(1)インデントを正確につけたほうが、あなたにとって有益でしょう。
 
(2)Gotoは使わないほうがいいと思います。
   要するに、なければ次のブロックに移るだけですから、
 
   If Not time Is Nothing Then
        jikan = time.Column
        If ws2.Cells(o, jikan) <> "" Then
            ws2.Cells(o, jikan) = timetodecimal(ws2.Cells(o, jikan))
            ws2.Cells(o, jikan).NumberFormatLocal = "G/標準"
        End If
    End If

    などと、 GoTo など使わなくても書けます。
     
(3)繰り返しのなかで、
   r3.Find("SWTF010", lookat:=xlWhole)を何度も実行する必要は全く無いと思います。
   無駄をできるだけなくすようにされたほうがよいと思いました。
ざっと拝見して気づいた点です。
他の皆さんからの回答をお待ちください。

 
 
ご忠告、ご意見、大変助かります。初心者でどこが無駄で、必要な部分かがあいまいです。
 
頂いたコードとは別なのですが、
 
下記のように、
 
arr1に受入データのセルを格納
 
arr2に時間表記の変えたコードを格納
 
arr1の配列をiに格納
 
i と arr2を比較して同じ単語であれば
 
r2にその範囲を格納して24を掛けるということはできるでしょうか。
 
 
Sub 時間表記変換()
 
 
Dim r1 As Range
 
Set r1 = Sheets("受入データ").Range("a1").CurrentRegion
 
Dim arr1() As Variant
 
Dim arr2() As String
 
 
arr1 = r1.Value
 
arr2 = Split("SWTF010;SWTF040;SWTF020;SWTF050;SWTF060", ";") '配列に受入データの見出しと一致する単語を格納
 
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
 
 
Dim i As Long
 
Dim l As Variant
 
Dim r2 As Range
 
    For i = 2 To UBound(arr1, 2)
     
        For Each l In arr2()
         
             If i = l Then
                       
                If r2 Is Nothing Then
                 
                    Set r2 = r1.Range(i, l)
                     
                        Else
                             
                            Set r2 = Union(r2, r1.Cells(i, l))
                             
                 End If
                  
               End If
     
             
             
    Next
      
        Next
     
  
  
End Sub
 
 

回答
投稿日時: 23/08/07 08:52:14
投稿者: simple

# 全文引用は必要ありません。直前の発言なのですぐにわかりますし、
# スクロールする手間がかかるだけで、閲覧者には意味が乏しいです。
# あなたがコメントするのに必要な部分だけ引用するようにしてください。
 
提示されたものに、手を入れてみました。主としてインデントの改善です。
気づいたことを少しコメントしました。
 

Sub 時間表記変換()
    Dim r1    As Range
    Dim arr1() As Variant
    Dim arr2() As String
    Dim dic   As Object
    Dim i     As Long
    Dim l     As Variant
    Dim r2    As Range

    Set r1 = Sheets("受入データ").Range("a1").CurrentRegion
    arr1 = r1.Value
    
    arr2 = Split("SWTF010;SWTF040;SWTF020;SWTF050;SWTF060", ";")
    
    Set dic = CreateObject("Scripting.Dictionary")'' 何に使うのですか?

    For i = 2 To UBound(arr1, 2)
        For Each l In arr2()
            If i = l Then   ''' iは整数。
                            ''' l は"SWTF010","SWTF040",.... といった文字列のどれか。
                            '’’ i と l が 一致することはありえません。
                If r2 Is Nothing Then
                    Set r2 = r1.Range(i, l)
                            ''' セル範囲の意味が不明です。
                            ''' こういうセルの指定方法はありません。
                Else
                    Set r2 = Union(r2, r1.Cells(i, l))
                End If
            End If
        Next
    Next
End Sub
# 全体として意味がわかりかねました。
 
------------------------------
23/08/06 20:12:26の発言で示したコードには興味が無かったですか?
ご希望の配列処理の積りでしたが。
 
これを利用して、時刻表示の変更するには、こんな書き方ができると思います。
参考にしてください。
 
Sub main()
    Dim ws受入  As Worksheet
    Dim header  As Range         '見出し行のセル範囲
    Dim dataR   As Long          'データ本体開始行
    Dim colmn(1 To 6) As Long    '時間関連6項目の列番号
    Dim lastrow As Long
    Dim rng     As Range
    Dim k       As Long

    Set ws受入 = Sheets("受入データ")

    ws受入.Cells.ClearFormats

    '見出し行
    Set header = ws受入.UsedRange.Rows(1)

    '時間関連6項目の列番号
    colmn(1) = header.Find("SWTF010", lookat:=xlWhole).Column    '出勤時間
    colmn(2) = header.Find("SWTF030", lookat:=xlWhole).Column    '普通残業時間
    colmn(3) = header.Find("SWTF040", lookat:=xlWhole).Column    '深夜残業時間
    colmn(4) = header.Find("SWTF020", lookat:=xlWhole).Column    '遅早時間
    colmn(5) = header.Find("SWTF050", lookat:=xlWhole).Column    '法定外休日時間
    colmn(6) = header.Find("SWTF060", lookat:=xlWhole).Column    '法定休日時間

    dataR = ws受入.UsedRange.Rows(1).Row + 1    'データ本体開始行

    '時間関連6項目の列の書式変更
    For k = 1 To 6
        lastrow = ws受入.Cells(Rows.Count, colmn(k)).End(xlUp).Row
        Set rng = ws受入.Range(ws受入.Cells(dataR, colmn(k)), _
                               ws受入.Cells(lastrow, colmn(k)))
        Call maketime24(rng)        '時間表示の変更
    Next
End Sub

Function maketime24(rng As Range)   '時間表示の変更  (再掲)
    Dim mat As Variant
    Dim v   As Double
    Dim k   As Long
    
    mat = rng.Value     '配列に取得
    For k = LBound(mat) To UBound(mat)
        v = mat(k, 1)
        If v <> Empty Then
            mat(k, 1) = v * 24
        End If
    Next
    rng.Value = mat     '変更後の配列を元に戻す
    rng.NumberFormatLocal = "G/標準"
End Function

# 私はここまでとします。

投稿日時: 23/08/08 17:03:16
投稿者: yama1006
メールを送信

 

Function maketime24(rng As Range)   '時間表示の変更  (再掲)
    Dim mat As Variant
    Dim v   As Double
    Dim k   As Long
    
    mat = rng.Value     '配列に取得
    For k = LBound(mat) To UBound(mat)
        v = mat(k, 1)
        If v <> Empty Then
            mat(k, 1) = v * 24
        End If
    Next
    rng.Value = mat     '変更後の配列を元に戻す
    rng.NumberFormatLocal = "G/標準"
End Function

 
 rng.Value = mat '変更後の配列を元に戻す
 
すみません、コードまで書いていただき、ありがとうございます。。。。 
 
こちらについてなのですが、変更後に配列を元に戻さない場合、どのようになるのでしょうか?

回答
投稿日時: 23/08/08 20:44:24
投稿者: simple

# 前回発言までとしたのですが、敢えて。
   
まず、コードが提示されたのですから、動作確認した結果をきちんと書いて下さい。
・機能が果たされたものになっていたのかどうか。
・エラー等で動かないのか、書いてください。
そのうえで、何を問題と考えているのか説明してください。
   
それで、私には質問の意図がわからなったので、他の方も同じでしょう。
もう一度説明してください。
時間関連の項目を24倍して10進表現にするという目的をどのように果たそうとしているんですか?
それを説明されれば、他の方から回答があるのではないでしょうか。
( 私ならこう書くというのを示しましたので、私には他の案はありません。)

投稿日時: 23/08/10 13:57:36
投稿者: yama1006
メールを送信

コードありがとうございました。
動作確認しました。
 
一点ご教示願います。
 
 '時間関連6項目の列番号
    colmn(1) = header.Find("SWTF010", lookat:=xlWhole).Column '出勤時間
    colmn(2) = header.Find("SWTF030", lookat:=xlWhole).Column '普通残業時間
    colmn(3) = header.Find("SWTF040", lookat:=xlWhole).Column '深夜残業時間
    colmn(4) = header.Find("SWTF020", lookat:=xlWhole).Column '遅早時間
    colmn(5) = header.Find("SWTF050", lookat:=xlWhole).Column '法定外休日時間
    colmn(6) = header.Find("SWTF060", lookat:=xlWhole).Column '法定休日時間
 
こちらの列が受入データに存在しない場合、エラーになってしまうので改善したいと思っております。
 
If header.Find("SWTF010", lookat:=xlWhole) Is Nothing Then
Else
End If
 
colmn(1) = header.Find("SWTF010", lookat:=xlWhole).Column '出勤時間
 
このような形で、nothingでそれぞれ判定するやり方が良いでしょうか?
 
また、
 
'時間関連6項目の列の書式変更
 For k = 1 To 7
    lastrow = ws1.Cells(Rows.Count, colmn(k)).End(xlUp).Row
     Set rng = ws1.Range(ws1.Cells(dataR, colmn(k)), _
                               ws1.Cells(lastrow, colmn(k)))
    Call maketime24(rng) '時間表示の変更
 
こちらについても、該当がない列を配列に格納しているためか、エラーになってしまいます。
 
お忙しい中申し訳ございませんが、よろしくお願いいたします。
 

投稿日時: 23/08/10 13:59:02
投稿者: yama1006
メールを送信

yama1006 さんの引用:
コードありがとうございました。
動作確認しました。
 
一点ご教示願います。
 
 '時間関連6項目の列番号
    colmn(1) = header.Find("SWTF010", lookat:=xlWhole).Column '出勤時間
    colmn(2) = header.Find("SWTF030", lookat:=xlWhole).Column '普通残業時間
    colmn(3) = header.Find("SWTF040", lookat:=xlWhole).Column '深夜残業時間
    colmn(4) = header.Find("SWTF020", lookat:=xlWhole).Column '遅早時間
    colmn(5) = header.Find("SWTF050", lookat:=xlWhole).Column '法定外休日時間
    colmn(6) = header.Find("SWTF060", lookat:=xlWhole).Column '法定休日時間
 
こちらの列が受入データに存在しない場合、エラーになってしまうので改善したいと思っております。
 
If header.Find("SWTF010", lookat:=xlWhole) Is Nothing Then
Else
End If
 
colmn(1) = header.Find("SWTF010", lookat:=xlWhole).Column '出勤時間
 
このような形で、nothingでそれぞれ判定するやり方が良いでしょうか?
 
また、
 
'時間関連6項目の列の書式変更
 For k = 1 To 6
    lastrow = ws1.Cells(Rows.Count, colmn(k)).End(xlUp).Row
     Set rng = ws1.Range(ws1.Cells(dataR, colmn(k)), _
                               ws1.Cells(lastrow, colmn(k)))
    Call maketime24(rng) '時間表示の変更
 
こちらについても、該当がない列を配列に格納しているためか、エラーになってしまいます。
 
お忙しい中申し訳ございませんが、よろしくお願いいたします。
 

回答
投稿日時: 23/08/10 17:05:55
投稿者: simple

コメント拝見しました。
 
・ポイントと思われる、時間関係の項目の配列による更新はOKだったということですね。
・勤務管理表のようなものは、そうしたデータの間違いがあれば、
  エラーにせずそこを迂回してしまうより、エラーにしてデータの修正を求めるべきです。
・もし使用しない項目がコード中に含まれているなら、コードを修正すべきです。
・いずれにしても、そちらで好きなように修正してください。
 
追加の質問の改めての説明もありませんでしたので、私はこれで失礼します。

回答
投稿日時: 23/08/12 14:52:07
投稿者: 半平太

後戻りする話で恐縮ですが、
この話は、受入データシート内の時間データを24倍に変えるものと認識したのですが、
 
1.「使い方」シートと「元データ」シートはこの処理に何か関係がありますか?
 
2.社員コードは、この処理に何か関係がありますか?
 
3.受入データシート内に数式が入ったセルはありますか?
  ある場合、それらは今回の処理で突然24倍に変わるセルが出現したとしても、
  悪影響を受けないですね?

投稿日時: 23/08/23 07:15:54
投稿者: yama1006
メールを送信

解決しました。ありがとうございました。