C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

抽出条件を満たすデータのみをリストにコピーする方法

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■92132 / inTopicNo.1)  抽出条件を満たすデータのみをリストにコピーする方法
  
□投稿者/ 河童 (64回)-(2019/08/28(Wed) 18:32:45)

分類:[C#] 

データグリッドビューのソースにリストを設定しようと思っています。

InfoCSVLstのリストにはすべてのデータが入っています。

データグリッドビューのソースには、
グループと班で抽出したリストのdgvCSVLstを設定したいです。

InfoCSVLstから検索条件で抽出されたデータをdgvCSVLstにコピーするにはどのようにすればよいでしょうか?
ソースに設定したいのは、dgvCSVLstのリストです。

わからない点は、InfoCSVLstの抽出方法です。

ご教示よろしくお願いします。


    public partial class Form1 : Form
    {
        /// <summary>
        /// CSVデータ
        /// </summary>
        public class infoCSVLst
        {
            public string group_id { get; set; }
            public string han_id { get; set; }
            public string staff_name { get; set; }
        }

        /// <summary>
        /// グリッド表示用
        /// </summary>
        public class dgvCSVLst
        {
            public string group_id { get; set; }
            public string han_id { get; set; }
            public string staff_name { get; set; }
        }

        public static List<infoCSVLst> InfoCSVLst = new List<infoCSVLst>();
        public static List<dgvCSVLst> DgbCSVLst = new List<dgvCSVLst>();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            for (int i = 1; i < 6; i++)
            {

                // 検査項目の入力内容のリストを作成
                for (int j = 1; j < 6; j++)
                {
                    infoCSVLst d = new infoCSVLst();

                    d.group_id = i + "課";
                    d.han_id = i + "班";
                    d.staff_name = "スタッフ" + j;

                    InfoCSVLst.Add(d);
                }
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {

            Create_DataSource("1課", "1班");
            dataGridView1.DataSource = DgbCSVLst;

        }

        void Create_DataSource(string group_id, string han_id)
        {

            var query = InfoCSVLst.AsEnumerable();

            // グループが入力されている場合
            if (group_id != "")
            {
                query = query.Where(d => d.group_id == group_id);
            }
            // 班が入力されている場合
            if (han_id != "")
            {
                query = query.Where(d => d.han_id == han_id);
            }

            //(不明点)
            List<string> data = query.Select(d => d.group_id).ToList();

            // 検索条件を反映したリストを作成
            for (int i = 0; i < data.Count; i++)
            {
                dgvCSVLst d = new dgvCSVLst();

                d.group_id = data[i];
                d.han_id = "";
                d.staff_name = "";

                DgbCSVLst.Add(d);
            } 
        }

    }

引用返信 編集キー/
■92136 / inTopicNo.2)  Re[1]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ WebSurfer (1891回)-(2019/08/29(Thu) 07:15:23)
No92132 (河童 さん) に返信
> データグリッドビューのソースにリストを設定しようと思っています。
>
> InfoCSVLstのリストにはすべてのデータが入っています。
>
> わからない点は、InfoCSVLstの抽出方法です。

データグリッドビューではなくて DataGridView と書きましょう。

DataGridView のデータソースには List<T> 型より DataTable の方が親和性が高いと
思いますので、DataTable を使うのがお勧めです。

具体的な話に興味があれば聞いてください。

ただし、前のスレッドの時のように、詳しく読まないでスルーするなら、このレスもスルー
しておいてください。
引用返信 編集キー/
■92137 / inTopicNo.3)  Re[1]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 魔界の仮面弁士 (2331回)-(2019/08/29(Thu) 09:13:05)
2019/08/29(Thu) 10:39:46 編集(投稿者)

No92132 (河童 さん) に返信
> public static List<infoCSVLst> InfoCSVLst = new List<infoCSVLst>();
> public static List<dgvCSVLst> DgbCSVLst = new List<dgvCSVLst>();
> private void button1_Click(object sender, EventArgs e)
> {
>   Create_DataSource("1課", "1班");
>   dataGridView1.DataSource = DgbCSVLst;
> }

こういったケースでは、
 DgvCSVList = Create_DataSource("1課", "1班");
 dataGridView1.DataSource = DgbCSVLst;
あるいは
 DgvCSVList = Create_DataSource(InfoCSVLst, "1課", "1班");
 dataGridView1.DataSource = DgbCSVLst;
のようにして、メソッドの戻り値として抽出結果を返すようにした方が
処理の影響範囲が分かりやすくなりそうです。


> public class infoCSVLst
> public class dgvCSVLst

この 2 つは、何が違うのでしょうか?

メンバー構成に差異が無いのなら、後者の dgvCsvLst 型は使わず、
DgbCSVLst を List<dgvCSVLst> 型から List<infoCSVLst> 型に
変更しておくことで、
> //(不明点)
> List<string> data = query.Select(d => d.group_id).ToList();
の部分を
 DgbCSVLst = query.ToList();
だけで済ませられるようになりますよ。


もしも DgbCSVLst の型を List<dgvCSVLst> のままにしたいのであれば、
 // List<string> data = query.Select(d => d.group_id).ToList();
 DgbCSVLst = query.Select(d => new dgvCSVLst
 {
  group_id = d.group_id,
  han_id = d.han_id,
  staff_name = d.staff_name,
 }).ToList();
のようにして、dgvCSVLst の各メンバーを指定してやれば OK です。

上記では、フィールド変数 DgbCSVLst が指し示すオブジェクトが、
.ToList() で生成されたインスタンスに指し換わることになりますので、
実際の抽出メソッドにおいては、
 return query.Select(d => new ……).ToList();
のように戻り値で返すようにし、呼び出し側の button1_Click で
 DgvCSVList = Create_DataSource("1課", "1班");
のように受け取ることをお奨めしておきます。この書き方だと、
button1_Click の部分を見るだけで、DgvCSVList のインスタンスが
変更されるという事を明確にできるため、分かりやすくなります。


もちろん、抽出メソッドの定義は現状どおり void のままとし、
戻り値として返すのではなく、フィールド変数を更新する形として
実装しても構いません。ただしその場合、
 (a案) 抽出のたびに DgvCSVList のインスタンスを作り直す
 (b案) DgvCSVList のインスタンスは維持し、中身だけを差し替える
のいずれの実装とするのかを考える必要がありますね。

ただし a 案とする場合は、戻り値として返却させた方が
望ましいのは先述の通りですし、b 案もあまりお奨めしません。

b案の場合は、
 // 既存インスタンスの中身をクリア
 DgbCSVLst.Clear();
 // 抽出結果を詰めなおす
 DgbCSVLst.AddRange(query.Select(d => new dgvCSVLst
 {
  group_id = d.group_id,
  han_id = d.han_id,
  staff_name = d.staff_name,
 }));
のように書けるわけですが、List<dgvCSVLst> は変更通知機能を
持っていないため、DataGridView との相性が良く無いためです。

このような使い方をしたいなら、DataTable を用いた方が良いでしょう。
DataTable は更新通知機能を持っているため、
DataGridView 等とのバインド時の相性が良いためです。


> public static List<infoCSVLst> InfoCSVLst = new List<infoCSVLst>();
> public static List<dgvCSVLst> DgbCSVLst = new List<dgvCSVLst>();

これらを static にするべきではありません。
そもそも、静的メンバーをインスタンスメソッドから更新するのは不自然です。


たとえば、Form1 (あるいは別フォームでも良い)などに
 private void button2_Click(object sender, EventArgs e)
 {
  var f1 = new Form1();
  var f2 = new Form1();
  var f3 = new Form1();
  f1.Show();
  f2.Show();
  f3.Show();
 }
などとすれば、Form1 クラスのインスタンスを 3 つ作成し、
同時に表示することも出来るわけです。(普段そんなことはしないと思いますが)

この場合、それぞれのフォームごとに DataGridView があるわけですが、
その場合の結果としては、
 (案1) どの Form1 からも、同じ一覧が表示されている
 (案2) それぞれの Form1 で、それぞれの抽出結果が表示されている
のいずれかになるのが普通です。(案2 にすることが多いかな?)


しかし今はそのどちらにもなりません。

static な静的メンバーを、それぞれの Form1 インスタンスの Load から
.Add しあっているため、上記 button2 を押したときに、
一つの List<T> インスタンスに、データが三重に追加されることになります。


案2 とするならば、静的ではなくインタンスのメンバーが望ましいので、
public static List<T> を private static List<T> とすべきです。

案1 とするならば、List<T> は Form1.cs の public static フィールドとして
用意するのではなく、Program.cs あたりで管理された方が良いでしょう。

ただし案1 の場合は、List<T> への排他制御も考慮する必要があります。
(Add し終わるまで、他の場所で読み書きできないことを保証すべき)
引用返信 編集キー/
■92200 / inTopicNo.4)  Re[2]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 河童 (65回)-(2019/08/31(Sat) 15:33:49)
2019/08/31(Sat) 15:46:48 編集(投稿者)
2019/08/31(Sat) 15:46:41 編集(投稿者)

No92136 (WebSurfer さん) に返信

Listを多用するようになったのは、繰り返し処理を行うときに
DataTable.Rows を使うよりも List(クラス) を使ったほうが早いと
ネット情報を鵜呑みにしたからです。

もともとそんなに重たい処理がないので、
体感的に変化を感じることはありませんが。


DataGridViewのデータソースを設定するときに
バインド列のプロパティで「DataPropertyName」が設定できることと
繰り返し処理ができればいいと思っています。

魔界の仮面弁士 さんが言われているような変更通知機能があるので
親和性が高いといいことでしょうか?

DataGridViewのセルの値を直接変更したりとかかな?


引用返信 編集キー/
■92201 / inTopicNo.5)  Re[3]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 河童 (66回)-(2019/08/31(Sat) 16:14:29)
No92137 (魔界の仮面弁士 さん) に返信

ありがとうございます。希望する結果になりました。

メソッドの戻り値として抽出結果を返すようにしました。
reate_DataSource(InfoCSVLst,"1課", "1班");

確かにこの2つの違いは特にありませんでした。
public class infoCSVLst
public class dgvCSVLst
無駄にクラスを作成していただけでした。

List<infoCSVLst> 型に変更して、
DgbCSVLst = query.ToList();
にすると希望通りの結果を取得することができました。


インスタンスについての疑問点。
今はサンプルで「InfoCSVLst」はロード時に作成していますが、
本来は、Form1のCSV読込ボタンのクリック時に作成しています。

次にForm1の検索条件ボタンのクリック時に
Create_DataSource(InfoCSVLst,"1課", "1班");
dataGridView1.DataSource = DgbCSVLst;
を実行しています。

DgbCSVLstは、別フォームのForm2でも利用したくて
public static にしていました。



解決済み
引用返信 編集キー/
■92202 / inTopicNo.6)  Re[3]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ WebSurfer (1896回)-(2019/08/31(Sat) 21:40:43)
No92200 (河童 さん) に返信

解決済みマークを付けたということは、話は終わりということで、私の話は
聞く気はないと理解。

何なのですかね・・・
解決済み
引用返信 編集キー/
■92217 / inTopicNo.7)  Re[4]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ Hoge (1回)-(2019/09/03(Tue) 10:16:43)
2019/09/03(Tue) 11:28:06 編集(投稿者)

No92202 (WebSurfer さん) に返信

河童さんはWebSurferさんに返信されていると思いますが?

そもそも、1回目のコメント内容も河童さんの質問に何も答えてないですよね。さらに

>ただし、前のスレッドの時のように、詳しく読まないでスルーするな
>ら、このレスもスルーしておいてください。

「このレスもスルーしておいてください。」と書きながら、

>私の話は聞く気はないと理解。

喧嘩売っているだけにしか見えない内容ですね。
以前にトラブルがあったんでしょうが、その不満をぶつけるためだけに書いているようにしか読めません。

質問者が望んでもない情報をだして「具体的な話に興味があれば聞いてください」と、知りたければ教えを請えという言い草。
で、聞かれなかったから「聞く気はない」と・・・。
なんなんでしょうか?

ずいぶん前からWebSurferさんの書き込みには不快感を持っていましたが、
いまだにつづけられていることに驚きました。

この書き込みは規約違反で消されることと思いますが、それならこの前のWebSurferさんの書き込みも消してもらいたいものです。




引用返信 編集キー/
■92218 / inTopicNo.8)  Re[4]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 大谷刑部 (8回)-(2019/09/03(Tue) 10:47:01)
No92202 (WebSurfer さん) に返信
> ■No92200 (河童 さん) に返信
>
> 解決済みマークを付けたということは、話は終わりということで、私の話は
> 聞く気はないと理解。
>
> 何なのですかね・・・

単に、あんたはうざい、弁さんの言ってることが役に立つと思われただけかと。
引用返信 編集キー/
■92220 / inTopicNo.9)  Re[3]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 大谷刑部 (9回)-(2019/09/03(Tue) 11:08:38)
No92200 (河童 さん) に返信
> 2019/08/31(Sat) 15:46:48 編集(投稿者)
> 2019/08/31(Sat) 15:46:41 編集(投稿者)
>
> ■No92136 (WebSurfer さん) に返信
>
> Listを多用するようになったのは、繰り返し処理を行うときに
> DataTable.Rows を使うよりも List(クラス) を使ったほうが早いと
> ネット情報を鵜呑みにしたからです。

鵜呑みかどうかはともかくDatatableが件数増えると遅いという印象を持たれてそのような情報が多いのは事実ですね。
ただ、DBのテーブルと同様に主キーを設定しておけばそうでもない気はします。


引用返信 編集キー/
■92223 / inTopicNo.10)  Re[4]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 魔界の仮面弁士 (2349回)-(2019/09/03(Tue) 11:59:15)
No92220 (大谷刑部 さん) に返信
> 鵜呑みかどうかはともかくDatatableが件数増えると遅いという印象を持たれてそのような情報が多いのは事実ですね。
> ただ、DBのテーブルと同様に主キーを設定しておけばそうでもない気はします。

DataGridView 等にバインドしている場合、ユーザー操作により
DefaultView に対する Sort 指定が行われることがありますね。

その結果、.Rows.Clear() 後にループで .Rows.Add するといった処理が低速化することがあります。
https://blog.doizaki.com/entry/2014/01/11/004238


この点については、行を削除して詰めなおすのではなく、DataTable インスタンスごとを作り直すことで
改善されるのですが、DataSet の場合は No81781 のスレッドで報告されている事象もあるのが悩ましいところ。
http://bbs.wankuma.com/index.cgi?mode=al2&namber=81781&KLOG=140



>> Listを多用するようになったのは、繰り返し処理を行うときに
>> DataTable.Rows を使うよりも List(クラス) を使ったほうが早いと
>> ネット情報を鵜呑みにしたからです。

データバインディングが目的なら、BindingList<T> クラスなんてのもあります。

このクラスは変更通知機構を有しているため、バインド時に元データを修正すると、
編集結果が直ちに、バインド先のコントロールにも反映されるようになっています。
https://www.oborodukiyo.info/Forms/VS2015/F-SetBindingListOnDataSource
引用返信 編集キー/
■92225 / inTopicNo.11)  Re[5]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 大谷刑部 (10回)-(2019/09/03(Tue) 14:57:13)
No92223 (魔界の仮面弁士 さん) に返信
> ■No92220 (大谷刑部 さん) に返信
>>鵜呑みかどうかはともかくDatatableが件数増えると遅いという印象を持たれてそのような情報が多いのは事実ですね。
>>ただ、DBのテーブルと同様に主キーを設定しておけばそうでもない気はします。
>
> DataGridView 等にバインドしている場合、ユーザー操作により
> DefaultView に対する Sort 指定が行われることがありますね。
>
> その結果、.Rows.Clear() 後にループで .Rows.Add するといった処理が低速化することがあります。
> https://blog.doizaki.com/entry/2014/01/11/004238

質問者ほったらかしでレスするのもなんですが、
上記のリンクの例は、そもそも行追加をばかちょんで2万行ループでaddするロジックがナンセンスな気が。
SQLでもインデックスはったらinsert分が遅くなるてことはあり得る話なので、datatableでも当然起こり得るでしょう。
少なくとも検索する項目が一定なら、定説遅いといわれているDatatableのselectメソッドも検索項目を主キーにしとけばそれほど遅くないので
Datatableへのアレルギーを持つ必要はないと言いたかっただけです。


> >> Listを多用するようになったのは、繰り返し処理を行うときに
> >> DataTable.Rows を使うよりも List(クラス) を使ったほうが早いと
> >> ネット情報を鵜呑みにしたからです。
>
> データバインディングが目的なら、BindingList<T> クラスなんてのもあります。
>
> このクラスは変更通知機構を有しているため、バインド時に元データを修正すると、
> 編集結果が直ちに、バインド先のコントロールにも反映されるようになっています。
> https://www.oborodukiyo.info/Forms/VS2015/F-SetBindingListOnDataSource

質問者さんのソースをあまり変えずに済む案としては上記が現実的なのかもしれませんね。
引用返信 編集キー/
■92227 / inTopicNo.12)  Re[6]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 魔界の仮面弁士 (2350回)-(2019/09/03(Tue) 15:04:22)
2019/09/03(Tue) 15:41:17 編集(投稿者)

No92225 (大谷刑部 さん) に返信
> 上記のリンクの例は、そもそも行追加をばかちょんで2万行ループでaddするロジックがナンセンスな気が。

それが必ずしも、件数の問題とも言い切れない面があるようで。

当方環境でも、.Columns.Count == 2 かつ .Rows.Count == 0 な
DataTable に対して、31 レコードを Row.Add するだけの単純な処理で
 修正前: 27.9 秒  … インスタンスを使いまわした場合
 修正後: 0.17 秒  … DataSet を作り直してから .Rows.Add した場合
という速度差になったことがあります。(PrimaryKey 設定済み)


1 件目の .Rows.Add は数十ミリ秒(既に遅いが、許容範囲)だったですが、
件数が増えるにつれて徐々に遅くなり、30 件目では
1 秒以上を要していました。
(EnforceConstraints を false にしても効果なし)

根本的な再現要因は特定できていないのですが、DataSet インスタンスを
使いまわさず、new しなおしてから .Rows.Add することで速度改善したため、
以降は対処療法として、都度、インスタンスを作り直すようにしています。
引用返信 編集キー/
■92241 / inTopicNo.13)  Re[7]: 抽出条件を満たすデータのみをリストにコピーする方法
□投稿者/ 大谷刑部 (11回)-(2019/09/04(Wed) 13:51:12)
No92227 (魔界の仮面弁士 さん) に返信
> 2019/09/03(Tue) 15:41:17 編集(投稿者)
>
> ■No92225 (大谷刑部 さん) に返信
>>上記のリンクの例は、そもそも行追加をばかちょんで2万行ループでaddするロジックがナンセンスな気が。
>
> それが必ずしも、件数の問題とも言い切れない面があるようで。
>
> 当方環境でも、.Columns.Count == 2 かつ .Rows.Count == 0 な
> DataTable に対して、31 レコードを Row.Add するだけの単純な処理で
>  修正前: 27.9 秒  … インスタンスを使いまわした場合
>  修正後: 0.17 秒  … DataSet を作り直してから .Rows.Add した場合
> という速度差になったことがあります。(PrimaryKey 設定済み)
>
>
> 1 件目の .Rows.Add は数十ミリ秒(既に遅いが、許容範囲)だったですが、
> 件数が増えるにつれて徐々に遅くなり、30 件目では
> 1 秒以上を要していました。
> (EnforceConstraints を false にしても効果なし)
なるほど。何かの理由でメモリ負荷が高くなってるのかもしれないですね。
Excelの処理をセル単位でぐるぐるやった時の症状になんか似てる気がします。

>
> 根本的な再現要因は特定できていないのですが、DataSet インスタンスを
> 使いまわさず、new しなおしてから .Rows.Add することで速度改善したため、
> 以降は対処療法として、都度、インスタンスを作り直すようにしています。
まあ、そうなるのでしょうが、何かオブジェクト指向っぽくないので納得はしにくい現象ですね。
「再利用」という観点から考えても。

質問者さんからもレスがないので、ひとまず解決済にしておきます。

解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ