[VBA,VB.NET,C#,PHP]プログラムTips集

[VBA,VB.NET,C#,PHP]プログラムのちょっとしたテクニック

範囲/枠(FromとTo)データを分割/統合するベースとなるアルゴリズム

Start(S)列とEnd(E)列で昇順にソートしておくと、
データの並び順は以下の4パターンに分類されます。

pattern1(start is same value)
S1■■■■■■■■■■E1
S2■■■■■■■■■■□□□□□□□□□□□E2
pattern2
S1■■■■■■■■■■■■■■■■■■E1
□□□□□□□S2■■■■■■■■■■■■■■■■■■■■■■■E2
pattern3(END is same value)
S1■■■■■■■■■■■■■■■■■■E1
□□□□□□□S2■■■■■■■■■■■E2
pattern4(all same)
S1■■■■■■■■■■■■■■■■■■E1
S2■■■■■■■■■■■■■■■■■■E2

	''' <summary>
	''' 例)呼び出し方
	''' </summary>
	Private Sub ExecSplitAndConsolidateRangeData()
    	' Init Class
    	Dim clsCTRL As New ClsSplitAndConsolidateRangeData()
    	' DataTable
    	Dim datTable As New DataTable
    	' Create Initial DataTable
    	clsCTRL.InitializeDataTable(datTable)
 
    	' データ作成
    	For Each s As String In Me.RichTextBox1.Lines
        	Dim rwNew As DataRow = datTable.NewRow
        	Dim sItems() As String = s.Split(",")
        	rwNew(0) = sItems(0)
        	rwNew(1) = sItems(1)
        	datTable.Rows.Add(rwNew)
    	Next
 
    	' 変換されたデータが0件になるまで続ける
    	Dim result As Double = 1
    	While (result > 0)
        	' 呼出し
        	result = clsCTRL.SplitAndConsolidateRangeData(datTable)
    	End While
 
    	' データをセット
    	Me.DataGridView1.DataSource = datTable
	End Sub

★ここからがデータを整合性を保ちながら分割・統合するクラス

''' <summary>
''' 範囲(From~To,Start~End...etc)からなるデータを統合、分割する方法
''' </summary>
Public Class ClsSplitAndConsolidateRangeData
	' 変数宣言エリア
	Public Const S As String = "S" '開始
	Public Const E As String = "E" '終了
	Public Const FORMAT As String = "000000"
	Public Const DATA As String = "DATA" 'データエリア
	Public Const DEL As String = "DEL" ' 削除対象フラグ
	' クラス内テーブル
	Public TBL_MAIN As New DataTable()
 
	''' <summary>
	''' 削除対象フラグ
	''' </summary>
	Enum DELFLAG
    	DEL
    	NONE
	End Enum
 
	''' <summary>
	''' データテーブルを初期化する
	''' </summary>
	''' <param name="DATA_TABLE"></param>
	Public Sub InitializeDataTable(ByRef DATA_TABLE As DataTable)
    	Try
        	DATA_TABLE = New DataTable()
        	DATA_TABLE.Columns.Add(S, Type.GetType("System.String"))
        	DATA_TABLE.Columns.Add(E, Type.GetType("System.String"))
        	DATA_TABLE.Columns.Add(DATA, Type.GetType("System.String"))
        	DATA_TABLE.Columns.Add(DEL, Type.GetType("System.String"))
    	Catch ex As Exception
    	End Try
	End Sub
 
	''' <summary>
	''' 実処理(分割、統合)
	''' </summary>
	''' <param name="TBL_TARGET"></param>
	Public Function SplitAndConsolidateRangeData(ByRef TBL_TARGET As DataTable) As Double
    	' 何件更新したか?0件になるまで再帰呼出し
    	Dim dblResult As Double = 0
    	Try
        	' インデックス:カレント行
        	Dim idx As Double
        	' インデックス:最大行
        	Dim idxMax As Double = TBL_TARGET.Rows.Count - 2
 
        	'SORT
        	TBL_TARGET = SortDataTable(TBL_TARGET, S & " Asc, " & E & " Asc ")
        	'DELETE
        	DeleteRowData(TBL_TARGET)
        	'ChangeAccept
        	TBL_TARGET.AcceptChanges()
 
        	' 現在行と次の行を比較していく
        	For idx = 0 To idxMax
            	' 元データ格納
            	Dim _S1 As String ' 開始1
            	Dim _E1 As String ' 終了1
            	Dim _S2 As String ' 開始2
            	Dim _E2 As String ' 終了3
            	Dim _S3 As String ' 開始new
      	      Dim _E3 As String ' 終了new
            	' 加工済みデータ格納用
            	Dim E1 As String ' 終了1
            	Dim S2 As String ' 開始2
            	Dim S3 As String ' 開始new
            	Dim E3 As String ' 終了new
 
            	'現在行を取得
            	_S1 = TBL_TARGET.Rows(idx).Item(S).ToString()
            	_E1 = TBL_TARGET.Rows(idx).Item(E).ToString()
 
            	'次の行を取得
            	_S2 = TBL_TARGET.Rows(idx + 1).Item(S).ToString()
            	_E2 = TBL_TARGET.Rows(idx + 1).Item(E).ToString()
 
            	'新しく作成される行
            	_S3 = ""
            	_E3 = ""
 
            	' 重複範囲がある場合は分割/統合対象
            	If (_S1 <= _S2) And (_S2 <= _E1) Then
                	' 影響を受けたデータをカウント
                	dblResult = dblResult + 1
                	' データの成形
                	S3 = _S2
                	E3 = _E1
                	E1 = (CDbl(_S2) - 1).ToString(FORMAT)
                	S2 = (CDbl(_E1) + 1).ToString(FORMAT)
   	             TBL_TARGET.Rows(idx).Item(E) = E1
                	TBL_TARGET.Rows(idx + 1).Item(S) = S2
                	'新規Row作成
                	Dim rwNEW As DataRow
                	rwNEW = TBL_TARGET.NewRow()
                	rwNEW.Item(S) = S3 'または、_S2でもOK
                	rwNEW.Item(E) = E3 'または、_E1でもOK
                	' データカラムがある場合(DATA1,DATA2が影響を受けない)
                	rwNEW.Item(DATA) =
                            TBL_TARGET.Rows(idx).Item(DATA) & TBL_TARGET.Rows(idx + 1).Item(DATA)
                	'データの整合性チェック(削除フラグを設定する)
                    CheckDataValue(TBL_TARGET.Rows(idx))
                    CheckDataValue(TBL_TARGET.Rows(idx + 1))
                	CheckDataValue(rwNEW)
       	         'とりあえず追加してしまう(Rows.Addメソッドだと必ず最後尾に追加)
                	TBL_TARGET.Rows.Add(rwNEW)
            	End If
        	Next
    	Catch ex As Exception
    	Finally
        	TBL_TARGET.AcceptChanges()
    	End Try
    	Return dblResult
	End Function
 
	''' <summary>
	''' テーブルをソートする
	''' DataTable自体にはソートをしてくれる機能がありませんが、DataTable.Select を応用することでソート処理ができます。
	''' </summary>
	''' <param name="tblTarget"></param>
	''' <param name="sortText">"Age DESC , ID ASC"</param>
	''' <returns></returns>
	Public Function SortDataTable(ByVal tblTarget As DataTable, ByVal sortText As String) As DataTable
    	'ソート後の DataTable を用意
    	Dim dtblSrt As New DataTable()
    	Try
        	'ソート前テーブルの情報をクローン
  	      dtblSrt = tblTarget.Clone()
        	'DataTable.Select()を使いソート(第二引数にソート条件を書く)
        	Dim rows As DataRow() = tblTarget.Select(Nothing, sortText).Clone()
        	'ソートされてる DataRow 配列をソート後の DataTable に追加
        	For Each row As DataRow In rows
            	dtblSrt.ImportRow(row)
        	Next
    	Catch ex As Exception
    	End Try
    	Return dtblSrt
	End Function
 
	''' <summary>
	''' 削除フラグを設定する
	''' </summary>
	''' <param name="rwTarget"></param>
	Private Sub CheckDataValue(ByRef rwTarget As DataRow)
    	Try
        	' 開始>終了は不整合データで削除
        	If rwTarget.Item(S).ToString() > rwTarget.Item(E).ToString() Then
            	rwTarget.Item(DEL) = CDbl(DELFLAG.DEL)
        	End If
    	Catch ex As Exception
    	End Try
	End Sub
 
	''' <summary>
	''' 実際にテーブルから削除する
	''' </summary>
	''' <param name="DEL_TABLE"></param>
	Private Sub DeleteRowData(ByRef DEL_TABLE As DataTable)
    	For Each rwDEL As DataRow In DEL_TABLE.Rows
        	' 実際に削除
        	If rwDEL.Item(DEL).ToString() = CStr(CDbl(DELFLAG.DEL)) Then
            	rwDEL.Delete()
        	End If
    	Next
    	DEL_TABLE.AcceptChanges()
	End Sub
 
End Class