none
配列データの並び替え RRS feed

  • 質問

  • VB初心者です。

     

    .NET FrameWork 2.0

    VisualBasic 2005

    環境です。

     

    下記のような配列の並び替えを行うのに、便利なコマンドはないでしょうか?

    ご教授下さい。

     

    「並び替え前配列」

    氏名    身長      体重

    Aさん   150cm            60kg

    Bさん       170cm            80kg

    Cさん   180cm            70kg

     

     

    「身長高い順に並び替え」

    氏名    身長      体重

    Cさん   180cm            70kg

    Bさん       170cm            80kg

    Aさん   150cm            60kg

     

    「体重重い順に並び替え」

    氏名    身長      体重

    Bさん       170cm            80kg

    Cさん   180cm            70kg

    Aさん   150cm            60kg

     

     

     

     

     

    2008年2月10日 3:59

回答

  • 外池です。

     

    「並べ替えしたい」の状況をもう少し詳しく教えていただけませんか?

     

    並べ替えして表示したいだけですか? であれば、表示するコントロールにそういう機能が見つかるかもしれません。

     

    並べ替えた結果を、アプリの中で記憶して保持したり、ファイルに出力したりしたいですか? であれば、内部的に並べ替えるプログラムをつくらないといけません。単純な一次元の配列を並べ替えるだけならば、配列の機能としてSortがりますが、2次元の配列を並び替えるのであれば、自分で書くことになります。

     

    ちなみに、並べ替えたりりすとの長さは、どれぐらいですか? 数十程度であれば、数百? 数万? 数十万? (数によって、プログラムの方法、いわゆるアルゴリズムを結構工夫しないといけなくなりますが。)

     

    できることなら、並べ替えぐらいは、勉強や練習を兼ねて、ご自分でプログラムされることをお奨めします。

     

    2008年2月10日 4:20
  • 外池です。

     

    配列のSortメソッドは、1次元の配列にしか使えませんので、今回のような用途(2次元配列であること。Sortする対象の項目を体重だったり身長だったりイロイロ換えて並べ替えをやりなおしたい。)には使えません。

     

    一方で、フォームに表示する方法と関連して、縦横2次元の表の形式にするには、ListViewコントロール(ただしViewプロパティーをDetailとします。)、DataGridViewコントロールが使えます。

     

    今回の状況では、たぶん、DataGridViewが便利かな? と思います。コントロールに並べ替えして表示する機能もありますし。

     

    50行、というデータの大きさであればまったく問題ないので、ソートの方法のことは忘れてもらって結構です。

     

    2008年2月10日 11:57
  •  

    一般的な2次元配列では無く ジャグ配列で作成すると Sortメソッドでソート可能ですよ

     

    普通の2次元配列の宣言は Dim ar(,) as String といった宣言をしますが

    ジャグ配列は Dim ar()() as String といった宣言をします

     

    例示のデータなら

    Dim ar()() as String = new String(2)() { }

    といった宣言をします ... 行末尾は() 『カッコ』ではなく{}『中カッコです』

     

    データの初期化を

    Code Snippet

    ar(0) = new String() { "A","B","C" }

    ar(1) = new String() { "150","170","180" }

    ar(2) = new String() { "60","80","70" }

     

     

    といった具合です

     

    ArrayクラスのSortは昇順のソートですから、降順にソートするならIComparerを継承したクラスを用意します

     

    Code Snippet

    Public Class myReverser

      Implements IComparer

      Public Function Compare(ByVal x as Object, ByVal y as Object) as Integer Implements System.Collections.IComparer.Compare

        return New CaseInsensitiveComparer().Compare(y, x)

      End Function

    End Class

     

     

     

    ソートの実行は キーとなる列を最後にソートするようにします

    身長順なら ar(1)を最後にします

    Code Snippet

    dim myComp as new myReverser

    Array.Sort( ar(1), ar(0), myComp)

    Array.Sort( ar(1), ar(2), myComp)

    Array.Sort( ar(1), ar(1), myComp)

     

     

    といった具合です

     

    体重順なら

    Code Snippet

    dim myComp as new myReverser

    Array.Sort( ar(2), ar(0), myComp)

    Array.Sort( ar(2), ar(1), myComp)

    Array.Sort( ar(2), ar(2), myComp)

     

     

    といった具合です

    2008年2月10日 13:01
  • 外池です。

     

    redfox63さんのご指摘、大変便利そうなのですが・・・、ちょっと不安なところがありまして、承知の上で使っていただきたい点を指摘しておきます。

     

    1) ジャグ配列という用語が出てきましたが・・・、これ、特にジャグ配列という「括り方」をしなくても、Name()、Height()、Weight()という単純な1次元配列を3本用意しておいて、Heightでソートしたければ、

      Array.Sort(Height, Name,myComp)

      Array.Sort(Height, Weight, myComp)

      Array.Sort(Height, Height, myComp)

    でもよろしいかと思います。まぁ、ジャグ配列にしておけば、ソートするKeyの項目をIndexで可変にできるという利点はありますが。

     

    2) ジャグ配列にせよ、上述のような項目ごとに用意した複数の配列にせよ、ある人の属性としての名前、身長、体重というような、個々人を括るモデルにはなっていないことです。最近のオブジェクト指向の考え方からすれば、ちょっと古いし、プログラムをミスると、別人の名前、身長、体重がゴチャゴチャになってしまう危険性があります。

     

    3) 本質的に並べ替えをする(着目する項目で順位をつける)操作は1回で済ませられるはずですが、この方法ですと、内部的にまったく同じことを何度も繰り返していることになります。(データの数が少ないので、ほとんど気づかないほどの時間のロスにしかなりませんが)

    2008年2月10日 13:32
  • 外池です。先ほどの記事の2)に関連して・・・、Array.Sortを複数回使用することに疑念が一つあります。

     

    ある一つの配列に対してSortをかけた「結果」は、必ず同じになるように見えるのですが、問題は、同じ値を持つものが複数要素あった場合、その要素がどのように並ぶのか? 実は不定です。不定と言うのは、Sortを呼び出すアプリ側では制御できない、という意味です。(.Net FrameworkがQuick Sortというアルゴリズムを採用していることが原因で、このことは、ちゃんとドキュメントに書いてある。) で、この「不定」ということ自体は、それほど大きな問題ではないのですが・・・

     

    同じ配列をキーにしてSortを複数回呼び出したときに、不定な要素の並び方が毎度必ず同じになるのか? 同じになる保証を何で確認すればよいのか? 私は今のところわかりません。

     

    具体的に、GOMBUさんの例題にredfox63さんの手法を適用した場合で、例えば、体重で並べ替えたとしましょう。そして、体重が同じ人が複数人いた場合ですが、その人たちの名前と身長のデータの対応が崩れてしまう可能性は排除できない、という意味です。

     

    たぶん・・・、大丈夫だとは思うのですが・・・、いや、わからないな。O(n^2)になるようなQuick Sortの最悪なシナリオを避けるために、事前にわざとランダムな切り混ぜをすることは在り得るし・・・。どなたか、よくご存知の方おられたら、ご教示頂きたいのですが・・・。

     

    いっそ、Microsoftにお願いして、このあたりの仕様をドキュメントに明記してもらった方が良いように思います。

     

     

     

     

     

     

    2008年2月10日 14:10
  • 同値の場合は考慮していませんでした ・・・

     

    構造体でデータを持って ソートのほうが確実かなぁ

     

    Code Snippet

    Public Structure Person

        Inherits IComparer

        dim Name  as String

        dim Height as double

        Dim Weight as double

        Public Function Compare ( x As Objecty As Object  ) As Integer Implements System.Collections.IComparer.Compare

            return new CaseInsensitiveComparer().Compare( y, x )

        End Function

    End Sutruct

     

     

     

    といった構造体を定義して

     

    Code Snippet

    Dim ar() as Person

    Dim sr() as Double

     

    ' データの代入は他でしているものとして ・・・

    for n = 0 to ar.GetUpperBound(0)

      sr(n) = ar(n).Height

    next

    Array.Sort( sr, ar, ar(0))

     

     

     

     

    2008年2月11日 5:20

すべての返信

  • 外池です。

     

    「並べ替えしたい」の状況をもう少し詳しく教えていただけませんか?

     

    並べ替えして表示したいだけですか? であれば、表示するコントロールにそういう機能が見つかるかもしれません。

     

    並べ替えた結果を、アプリの中で記憶して保持したり、ファイルに出力したりしたいですか? であれば、内部的に並べ替えるプログラムをつくらないといけません。単純な一次元の配列を並べ替えるだけならば、配列の機能としてSortがりますが、2次元の配列を並び替えるのであれば、自分で書くことになります。

     

    ちなみに、並べ替えたりりすとの長さは、どれぐらいですか? 数十程度であれば、数百? 数万? 数十万? (数によって、プログラムの方法、いわゆるアルゴリズムを結構工夫しないといけなくなりますが。)

     

    できることなら、並べ替えぐらいは、勉強や練習を兼ねて、ご自分でプログラムされることをお奨めします。

     

    2008年2月10日 4:20
  • 外池様

     

    配列並べ替え結果は、数の大きい順に選択してフォームに表示するのみです。

    配列リスト長は2次元で、50行10列です。

    sortについては試してみます。

    2008年2月10日 6:01
  • 外池です。

     

    配列のSortメソッドは、1次元の配列にしか使えませんので、今回のような用途(2次元配列であること。Sortする対象の項目を体重だったり身長だったりイロイロ換えて並べ替えをやりなおしたい。)には使えません。

     

    一方で、フォームに表示する方法と関連して、縦横2次元の表の形式にするには、ListViewコントロール(ただしViewプロパティーをDetailとします。)、DataGridViewコントロールが使えます。

     

    今回の状況では、たぶん、DataGridViewが便利かな? と思います。コントロールに並べ替えして表示する機能もありますし。

     

    50行、というデータの大きさであればまったく問題ないので、ソートの方法のことは忘れてもらって結構です。

     

    2008年2月10日 11:57
  •  

    一般的な2次元配列では無く ジャグ配列で作成すると Sortメソッドでソート可能ですよ

     

    普通の2次元配列の宣言は Dim ar(,) as String といった宣言をしますが

    ジャグ配列は Dim ar()() as String といった宣言をします

     

    例示のデータなら

    Dim ar()() as String = new String(2)() { }

    といった宣言をします ... 行末尾は() 『カッコ』ではなく{}『中カッコです』

     

    データの初期化を

    Code Snippet

    ar(0) = new String() { "A","B","C" }

    ar(1) = new String() { "150","170","180" }

    ar(2) = new String() { "60","80","70" }

     

     

    といった具合です

     

    ArrayクラスのSortは昇順のソートですから、降順にソートするならIComparerを継承したクラスを用意します

     

    Code Snippet

    Public Class myReverser

      Implements IComparer

      Public Function Compare(ByVal x as Object, ByVal y as Object) as Integer Implements System.Collections.IComparer.Compare

        return New CaseInsensitiveComparer().Compare(y, x)

      End Function

    End Class

     

     

     

    ソートの実行は キーとなる列を最後にソートするようにします

    身長順なら ar(1)を最後にします

    Code Snippet

    dim myComp as new myReverser

    Array.Sort( ar(1), ar(0), myComp)

    Array.Sort( ar(1), ar(2), myComp)

    Array.Sort( ar(1), ar(1), myComp)

     

     

    といった具合です

     

    体重順なら

    Code Snippet

    dim myComp as new myReverser

    Array.Sort( ar(2), ar(0), myComp)

    Array.Sort( ar(2), ar(1), myComp)

    Array.Sort( ar(2), ar(2), myComp)

     

     

    といった具合です

    2008年2月10日 13:01
  • 外池です。

     

    redfox63さんのご指摘、大変便利そうなのですが・・・、ちょっと不安なところがありまして、承知の上で使っていただきたい点を指摘しておきます。

     

    1) ジャグ配列という用語が出てきましたが・・・、これ、特にジャグ配列という「括り方」をしなくても、Name()、Height()、Weight()という単純な1次元配列を3本用意しておいて、Heightでソートしたければ、

      Array.Sort(Height, Name,myComp)

      Array.Sort(Height, Weight, myComp)

      Array.Sort(Height, Height, myComp)

    でもよろしいかと思います。まぁ、ジャグ配列にしておけば、ソートするKeyの項目をIndexで可変にできるという利点はありますが。

     

    2) ジャグ配列にせよ、上述のような項目ごとに用意した複数の配列にせよ、ある人の属性としての名前、身長、体重というような、個々人を括るモデルにはなっていないことです。最近のオブジェクト指向の考え方からすれば、ちょっと古いし、プログラムをミスると、別人の名前、身長、体重がゴチャゴチャになってしまう危険性があります。

     

    3) 本質的に並べ替えをする(着目する項目で順位をつける)操作は1回で済ませられるはずですが、この方法ですと、内部的にまったく同じことを何度も繰り返していることになります。(データの数が少ないので、ほとんど気づかないほどの時間のロスにしかなりませんが)

    2008年2月10日 13:32
  • 外池です。先ほどの記事の2)に関連して・・・、Array.Sortを複数回使用することに疑念が一つあります。

     

    ある一つの配列に対してSortをかけた「結果」は、必ず同じになるように見えるのですが、問題は、同じ値を持つものが複数要素あった場合、その要素がどのように並ぶのか? 実は不定です。不定と言うのは、Sortを呼び出すアプリ側では制御できない、という意味です。(.Net FrameworkがQuick Sortというアルゴリズムを採用していることが原因で、このことは、ちゃんとドキュメントに書いてある。) で、この「不定」ということ自体は、それほど大きな問題ではないのですが・・・

     

    同じ配列をキーにしてSortを複数回呼び出したときに、不定な要素の並び方が毎度必ず同じになるのか? 同じになる保証を何で確認すればよいのか? 私は今のところわかりません。

     

    具体的に、GOMBUさんの例題にredfox63さんの手法を適用した場合で、例えば、体重で並べ替えたとしましょう。そして、体重が同じ人が複数人いた場合ですが、その人たちの名前と身長のデータの対応が崩れてしまう可能性は排除できない、という意味です。

     

    たぶん・・・、大丈夫だとは思うのですが・・・、いや、わからないな。O(n^2)になるようなQuick Sortの最悪なシナリオを避けるために、事前にわざとランダムな切り混ぜをすることは在り得るし・・・。どなたか、よくご存知の方おられたら、ご教示頂きたいのですが・・・。

     

    いっそ、Microsoftにお願いして、このあたりの仕様をドキュメントに明記してもらった方が良いように思います。

     

     

     

     

     

     

    2008年2月10日 14:10
  • 同値の場合は考慮していませんでした ・・・

     

    構造体でデータを持って ソートのほうが確実かなぁ

     

    Code Snippet

    Public Structure Person

        Inherits IComparer

        dim Name  as String

        dim Height as double

        Dim Weight as double

        Public Function Compare ( x As Objecty As Object  ) As Integer Implements System.Collections.IComparer.Compare

            return new CaseInsensitiveComparer().Compare( y, x )

        End Function

    End Sutruct

     

     

     

    といった構造体を定義して

     

    Code Snippet

    Dim ar() as Person

    Dim sr() as Double

     

    ' データの代入は他でしているものとして ・・・

    for n = 0 to ar.GetUpperBound(0)

      sr(n) = ar(n).Height

    next

    Array.Sort( sr, ar, ar(0))

     

     

     

     

    2008年2月11日 5:20
  • redfox63さん

     

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

     

    ジャグ配列の作成自体は出来たのですが、

     

    氏名    身長      体重

    Aさん   150cm            60kg

    Bさん       170cm            80kg

    Cさん   180cm            70kg

     

    のデータをTextFieldParserを使って、

    1行づつ読取を行い、

    csvファイル→ジャグ配列にしているため、

     

    ar(0) = new String() { "A","B","C" }

    ar(1) = new String() { "150","170","180" }

    ar(2) = new String() { "60","80","70" }

     

    のようにはならず、

     

    ar(0) →  "A","150","60"

    ar(1) →  "B","170","80"

    ar(2) →  "C","180","70"

     

    と読み込んでしまい、

    身長、体重等でsort出来る事が出来ませんでした。

     

    TextFieldParserのような命令で、

    1列づつ読取出来るものはないのでしょうか?

     

     

    2008年2月11日 9:33
  • 外池です。

     

    「同値の場合」というのが、揚げ足とりみたいになっちゃって、すいません。(ただ・・・、100名ほどのデータで、身長、体重とかだと、同値が十分にありえそうなので・・・。というか、同値が起こらない例というのが考えにくいので。)

     

    redfox63の今回のご提案のPerson構造体と、前回ご提案のジャグ配列と、融合させてみると良いのかもしれません。

    名前、身長、体重、胸囲などなどのデータを一列にして、{日本太郎, 143.5, 40.5, 80.0, ...}というような配列にしてしまって、あと、さらに、これを配列にする。

     

    こうしておけば、DataGridViewコントロールとの相性もよくなって、表示だけで並べ替えをすればよいのであれば、ほとんど解決です。

     

    また、並べ替えの結果を保存する場合でも、redfox63さんが今回ご提案になった方法をほんの少し修正して適用できますよね。

     

    ただ・・・、まぁ、データの型をいっぱ一からげにObject型にしておくのがたまに傷ですが。

     

     

    2008年2月11日 9:38