- PR -

DataViewのソート方法を指定するには?

1
投稿者投稿内容
Tog
ベテラン
会議室デビュー日: 2002/06/09
投稿数: 76
投稿日時: 2004-02-07 16:23
1AB
2AAA
10ACDEG
のようなデータが入力されたDataTableのColumnをソートしたいと思っています。
ただ、標準では文字列比較してしまうため、

1AB
10ACDEF
2AAA

とソートされてしまいます。

1AB
2AAA
10ACDEF

とソートさせるためにIComparerを渡すなどの方法でDataViewのソート方法を指定させたいとおもい、いろいろと調べてみたのですが、DataViewのソートをコントロールする方法が見つかりませんでした。

IComparableを実装したクラスを作成してDataTableに設定すれば可能だとは思うのですが、データベースから取得したString型のColumn列に対してSort方法を指定する方法はあるのでしょうか?

ご存知の方がおられましたらよろしくお願いいたします。
Tog
ベテラン
会議室デビュー日: 2002/06/09
投稿数: 76
投稿日時: 2004-02-09 10:52
DataViewのソートはDataTableのLocalに設定されたCultureInfo内プロパティのCompareInfoクラスを利用して文字列比較をしているようなので、CultureInfoを継承し、指定したCompareInfoを返すクラスを作成してあげれば、ソート方法を指定できそうなのですが、

MSDNにCompareInfoを作成する方法として、

>CompareInfo.GetCompareInfo メソッド (String, Assembly)

を利用するとかかれていました。
引数のAssemblyは

>使用する文字列比較メソッドを格納している Assembly 。

とかかれているのですが、どのようにAssemblyに文字列比較メソッドを格納するかわからずに躓いています。

System.ReflectionやAssemblyあたりを御存知の方がおられましたらアドバイスいただければ嬉しいです。
todo
ぬし
会議室デビュー日: 2003/07/23
投稿数: 682
投稿日時: 2004-02-09 14:45
int型でソートしたいのであれば、
.Sort = "Convert(列名, 'int')"

MSDNのDataColumn.Expressionにある書式はDataView.Sortでも使えます。
http://www.microsoft.com/japan/msdn/library/ja/Vbcon/html/vbtskfilteringsortingdatausingdataview.asp
Tog
ベテラン
会議室デビュー日: 2002/06/09
投稿数: 76
投稿日時: 2004-02-09 17:53
todoさんお返事ありがとうございます。

数値文字列でもint型にConvertをかけて思ったように並べ替えられるかやってみました。

しかし

Sortプロパティに"Convert(列名,'System.Int32')"
等と設定しても、

>列 Convert(列名が見つかりません。

とOutOfRangeExceptionが発生してしまいました。
SortでConvertを指定するのにSortプロパティ以外のプロパティの設定が必要なのでしょうか?
よろしくお願いいたします。
Tog
ベテラン
会議室デビュー日: 2002/06/09
投稿数: 76
投稿日時: 2004-02-12 11:48
SortプロパティではExpressiontは使えないようです。
とりあえずソート用のcolumnを追加する方法で実装しました。
CompareInfoのモジュールを指定する方法を引き続き調べています。
御存知の方がおられましたらアドバイスお願いします。

コード:
Imports System.Text
Imports System.Text.RegularExpressions

Public Class NumericSorter

#Region "定数"

    Private Const AddColumnString As String = "_NUMBERSTRINGSORTCOLUMN"

#End Region

#Region "コンストラクタ"

    Private Sub New()
        'インスタンス作成禁止
    End Sub

#End Region

#Region "共有メソッド"

    Public Shared Function Sort(ByVal table As DataTable, ByVal sortString As String) As DataTable
        Dim sorts() As String = sortString.Split(","c)
        Dim sortItems As New ArrayList

        'Sort情報アイテム作成
        For Each sortValue As String In sorts
            Dim value() As String = sortValue.Trim.Split(" "c)
            If 1 < value.Length Then
                sortItems.Add(New sortItem(value(0).TrimStart("["c).TrimEnd("]"c), value(1)))
            Else
                sortItems.Add(New sortItem(value(0).TrimStart("["c).TrimEnd("]"c)))
            End If
        Next

        'Sort用カラム追加
        For Each item As sortItem In sortItems
            'Column存在チェック
            If Not table.Columns.Contains(item.ColumnName) Then
                Throw New IndexOutOfRangeException(String.Concat("列:", item.ColumnName, "はテーブルに属していません。"))
            End If

            'Sort用Column追加
            Dim addColumnName As String = String.Concat(item.ColumnName, AddColumnString)
            If Not table.Columns.Contains(addColumnName) Then
                table.Columns.Add(addColumnName, GetType(NumericCompareData))
            Else
                table.Columns(addColumnName).ReadOnly = False
            End If

            For Each row As DataRow In table.Select("")
                row.Item(addColumnName) = New NumericCompareData(row.Item(item.ColumnName))
            Next

            table.Columns(addColumnName).ReadOnly = True
        Next

        'Sort文字列作成
        Dim sb As New StringBuilder
        Dim isFirst As Boolean = True
        For Each item As sortItem In sortItems
            If Not isFirst Then
                sb.Append(",")
            End If
            sb.Append(item.ColumnName)
            sb.Append(AddColumnString)
            sb.Append(" ")
            sb.Append(item.AscString)

            isFirst = False
        Next

        'Sort文字列設定
        table.DefaultView.Sort = sb.ToString

        Return table
    End Function

#End Region

#Region "サブクラス"

#Region "sortItem"

    Private Class sortItem
        Private iIsAsc As Boolean
        Private iAscString As String
        Private iColumnName As String

        Public Sub New(ByVal columnName As String, ByVal ascString As String)
            If String.Compare(ascString, "DESC", True) = 0 Then
                iIsAsc = False
            Else
                iIsAsc = True
            End If

            iAscString = ascString
            iColumnName = columnName
        End Sub

        Public Sub New(ByVal columnName As String)
            iColumnName = columnName
        End Sub

        Public Property IsAsc() As Boolean
            Get
                Return iIsAsc
            End Get
            Set(ByVal Value As Boolean)
                iIsAsc = Value
            End Set
        End Property

        Public Property ColumnName() As String
            Get
                Return iColumnName
            End Get
            Set(ByVal Value As String)
                iColumnName = Value
            End Set
        End Property

        Public Property AscString() As String
            Get
                Return iAscString
            End Get
            Set(ByVal Value As String)
                iAscString = Value
            End Set
        End Property

        Public Overrides Function ToString() As String
            Return String.Concat(iColumnName, " ", iAscString)
        End Function

    End Class

#End Region

#Region "NumericCompareData"

    Public Class NumericCompareData : Implements IComparable
        Private iValue As String
        Private Shared iComparer As New NumericComparer

        Public Sub New(ByVal value As String)
            iValue = value
        End Sub

        Public Sub New(ByVal value As Object)
            iValue = value.ToString
        End Sub

        Public Property Value() As String
            Get
                Return iValue
            End Get
            Set(ByVal Value As String)
                iValue = Value
            End Set
        End Property

        Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
            If TypeOf obj Is NumericCompareData Then
                Return iComparer.Compare(iValue, DirectCast(obj, NumericCompareData).Value)
            Else
                Return 0
            End If
        End Function

        Public Overrides Function ToString() As String
            Return iValue
        End Function

    End Class

#End Region

#Region "NumericIComparer"

    Public Class NumericComparer : Implements IComparer
        Private iNumberRegex As New Regex("(?<number>[0-9\.]*)(?<string>.*)")

        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
            Dim xNumber, yNumber As Double
            Dim xString, yString As String
            Dim xBool, yBool As Boolean

            xBool = getNumber(x.ToString, xNumber, xString)
            yBool = getNumber(y.ToString, yNumber, yString)

            If xBool AndAlso yBool AndAlso (Not IsNothing(xString)) And (Not IsNothing(yString)) Then
                Dim rtnCompareNumber As Integer = xNumber.CompareTo(yNumber)
                If rtnCompareNumber <> 0 Then
                    Return rtnCompareNumber
                Else
                    Dim rtnCompareString As Integer = xString.CompareTo(yString)
                    Return rtnCompareString
                End If
            ElseIf xBool Or IsNothing(yBool) Then
                Return -1
            ElseIf yBool Or IsNothing(xBool) Then
                Return 1
            Else
                Return 0
            End If

        End Function

        Private Function getNumber(ByVal str As String, ByRef matchNumber As Double, ByRef matchString As String) As Boolean
            Dim numberMatch As Match = iNumberRegex.Match(str)
            If numberMatch.Success Then
                Dim matchNumberString As String = numberMatch.Groups("number").ToString
                If matchNumberString <> "" Then
                    matchNumber = CType(matchNumberString, Double)
                Else
                    matchNumber = Double.MaxValue
                End If
                matchString = numberMatch.Groups("string").ToString
                Return True
            Else
                Return False
            End If
        End Function

    End Class

#End Region

#End Region

End Class

1

スキルアップ/キャリアアップ(JOB@IT)