2015年11月30日月曜日

正規表現によるSplit

数字部分でSplit
using System;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Regex splitReg = new Regex(@"[0-9]+");

            string[] a = splitReg.Split("AB12CD34EF");

            foreach (string data in a)
            {
                Console.WriteLine(data);
            }
        }
    }
}

AB
CD
EF
正規表現の部分が区切りとみなされ分割される。

正規表現の部分も取得したければ、グループ化すれば

取得してくれる。

using System;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Regex splitReg = new Regex(@"([0-9]+)");

            string[] a = splitReg.Split("AB12CD34EF");

            foreach (string data in a)
            {
                Console.WriteLine(data);
            }
        }
    }
}

AB
12
CD
34
EF


正規表現にマッチすると、分割し、捨てられる。
ただし、かっこでくくっておくと、それは取ってくれる。

極端なことを言えば、数字で分割するけど、分割後偶数はとっておこう。
なんて思うと
using System;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Regex splitReg = new Regex(@"(?:([02468])|[13579])+");

            string[] a = splitReg.Split("A1B2C3D45E67F8G9H");

            foreach (string data in a)
            {
                Console.WriteLine(data);
            }
        }
    }
}

A
B
2
C
D
4
E
6
F
8
G
H

2015年11月21日土曜日

棒グラフ

データのChartを追加




using を追加します。
using System.Windows.Forms.DataVisualization.Charting;


グラフのデータはSeriesクラスに格納し、それをグラフに追加すると表示してくれます。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY(1, 10);
    data.Points.AddXY(2, 20);
    data.Points.AddXY(3, 30);
    data.Points.AddXY(4, 40);
    data.Points.AddXY(5, 50);

    this.chart1.Series.Add(data);
}

オレンジ色のグラフが追加したものです。


青色は、デフォルトで存在するデータです。
プロパティのSeriesを選択。
SeriesコレクションエディタのPointsを選択。




DataPointコレクションエディタ画面で、データを追加します。
追加して、データのXValueとYValueを設定します。

0→XValue:1 YValue:50
1→XValue:2 YValue:40
2→XValue:3 YValue:30
3→XValue:4 YValue:20
4→XValue:5 YValue:10

を設定しました。

これで、デフォルトの青色のデータが表示されるはずです。


青色のグラフが追加されました。

判例の名称を変更してみたいもんです。
データは、SeriesインスタンスをAddしただけなので、foreachでぐるぐる回してみます。

private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY(1, 10);
    data.Points.AddXY(2, 20);
    data.Points.AddXY(3, 30);
    data.Points.AddXY(4, 40);
    data.Points.AddXY(5, 50);
   
    this.chart1.Series.Add(data);

    foreach (Series s in this.chart1.Series)
    {
        MessageBox.Show(s.Name);
    }
}

Series1
Series2
の順に表示されました。


ということは、Seriesをインスタンス化するときに、Name属性に指定すればよいようです。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY(1, 10);
    data.Points.AddXY(2, 20);
    data.Points.AddXY(3, 30);
    data.Points.AddXY(4, 40);
    data.Points.AddXY(5, 50);
    data.Name = "Test";

    this.chart1.Series.Add(data);
}



Testに変わっています。

private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY(1, 10);
    data.Points.AddXY(2, 20);
    data.Points.AddXY(3, 30);
    data.Points.AddXY(4, 40);
    data.Points.AddXY(5, 50);

    this.chart1.Series.Add(data);
    this.chart1.Series[1].Name = "名前変更";
}
デフォルトの青いデータの後に追加したので、2つ目のデータなので
this.chart1.Series[1].Nameで変更することもできます。


グラフの色は、SeriesのColorに設定すれば変えられます。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY(1, 10);
    data.Points.AddXY(2, 20);
    data.Points.AddXY(3, 30);
    data.Points.AddXY(4, 40);
    data.Points.AddXY(5, 50);
    data.Color = Color.Cyan;

    this.chart1.Series.Add(data);
    this.chart1.Series[1].Name = "名前変更";
}


今度はX軸に文字列を設定します。
ひとまず、デフォルトのSeriesは不要なので、プロパティの設定から削除してみます。
削除後、プログラムを書き換えます。

private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY("太郎", 10);
    data.Points.AddXY("次郎", 20);
    data.Points.AddXY("三郎", 30);
    data.Points.AddXY("四郎", 40);
    data.Points.AddXY("五郎", 50);

    this.chart1.Series.Add(data);
}




グラフに値を表示させてみます。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY("太郎", 10);
    data.Points.AddXY("次郎", 20);
    data.Points.AddXY("三郎", 30);
    data.Points.AddXY("四郎", 40);
    data.Points.AddXY("五郎", 50);
    data.IsValueShownAsLabel = true;

    this.chart1.Series.Add(data);
}




グラフの縦線はいまいち美しくないですねぇ。
消してみます。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY("太郎", 10);
    data.Points.AddXY("次郎", 20);
    data.Points.AddXY("三郎", 30);
    data.Points.AddXY("四郎", 40);
    data.Points.AddXY("五郎", 50);
    
    data.IsValueShownAsLabel = true;

    this.chart1.Series.Add(data);
    this.chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
}



少しすっきりしました。
せっかくなら、原点にある縦の線も消したいですね。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY("太郎", 10);
    data.Points.AddXY("次郎", 20);
    data.Points.AddXY("三郎", 30);
    data.Points.AddXY("四郎", 40);
    data.Points.AddXY("五郎", 50);
    
    data.IsValueShownAsLabel = true;

    this.chart1.Series.Add(data);
    this.chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
    this.chart1.ChartAreas[0].AxisY.LineColor = Color.White;
}

原点の軸を変えるならAxisY.LineColorってのが使えるのかな?


これだと、Y軸の原点の線が白なんで横軸と交わるとこが白くなって美しくないですね。
これじゃだめですね。

太さのプロパティを0にしてみます。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY("太郎", 10);
    data.Points.AddXY("次郎", 20);
    data.Points.AddXY("三郎", 30);
    data.Points.AddXY("四郎", 40);
    data.Points.AddXY("五郎", 50);
    
    data.IsValueShownAsLabel = true;

    this.chart1.Series.Add(data);
    this.chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
    this.chart1.ChartAreas[0].AxisY.LineWidth = 0;
}




最後に名前が長いと収まり悪いので、90度回転して表示します。
private void Form1_Load(object sender, EventArgs e)
{
    Series data = new Series();
    data.Points.AddXY("太郎", 10);
    data.Points.AddXY("次郎", 20);
    data.Points.AddXY("三郎", 30);
    data.Points.AddXY("四郎", 40);
    data.Points.AddXY("五郎", 50);
    
    data.IsValueShownAsLabel = true;

    this.chart1.Series.Add(data);
    this.chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
    this.chart1.ChartAreas[0].AxisY.LineWidth = 0;
    this.chart1.ChartAreas[0].AxisX.LabelStyle.Angle = -90; //-90~90
}



今回登場しなかったプロパティも含めまとめ。

■グラフについて(棒グラフで説明します)
Series 変数 = new Series(); でグラフやデータの情報を保持しています。
変数.Points.AddXY("XValue", "YValue"); で、データを追加できます。

※他のグラフは・・・
変数.ChartType = SeriesChartType.Bar のようにSeriesChartType列挙体を指定すればよいです。
<
メンバー名説明
Area面グラフ。
Bar横棒グラフ。
BoxPlotボックス プロット グラフ。
Bubbleバブル チャート。
Candlestickローソク足チャート。
Column縦棒グラフ。
Doughnutドーナツ グラフ。
ErrorBar誤差範囲グラフ。
FastLineFastLine グラフ。
FastPointFastPoint グラフ。
Funnelじょうごグラフ。
Kagiかぎ足チャート。
Line折れ線グラフ。
Pie円グラフ。
Pointポイント グラフ。
PointAndFigurePointAndFigure グラフ。
Polar極座標グラフ。
Pyramidピラミッド グラフ。
Radarレーダー チャート。
Range範囲グラフ。
RangeBarRangeBar グラフ。
RangeColumn範囲縦棒グラフ。
Renko練行足チャート。
Splineスプライン グラフ。
SplineAreaスプライン面グラフ。
SplineRangeスプライン範囲グラフ。
StackedArea積み上げ面グラフ。
StackedArea100100% 積み上げ面グラフ。
StackedBar積み上げ横棒グラフ。
StackedBar100100% 積み上げ横棒グラフ。
StackedColumn積み上げ縦棒グラフ。
StackedColumn100100% 積み上げ縦棒グラフ。
StepLineStepLine グラフ。
Stock株価チャート。
ThreeLineBreakThreeLineBreak グラフ。


■X軸
this.chart1.ChartAreas[0].AxisX.Enabled = AxisEnabled.False; //ラベル非表示
this.chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false; //主目盛りを非表示
this.chart1.ChartAreas[0].AxisX.MajorGrid.Interval = 10; //主目盛りの間隔
this.chart1.ChartAreas[0].AxisX.MajorGrid.IntervalOffset = 10; //主目盛りのオフセット値
this.chart1.ChartAreas[0].AxisX.MajorGrid.Color = Color.Blue; //主目盛りの色

this.chart1.ChartAreas[0].AxisX.MinorGrid.Enabled = true; //補助目盛りを表示
this.chart1.ChartAreas[0].AxisX.MinorGrid.Interval = 10; //補助目盛りの間隔
this.chart1.ChartAreas[0].AxisX.MinorGrid.IntervalOffset = 10; //補助目盛りのオフセット値


後で追加する・・・

2015年10月12日月曜日

ちょっとしたメモ

■int.Parse、 Convert.ToInt32の違い

string hoge = null;

Convert.ToInt32(hoge); //0

int.Parse(hoge) //ArgumentNullExceptionエラー


■繰り返し文字列
string str = new String('*', 10);

2015年10月11日日曜日

Visual Studioで個人的に使うショートカットの備忘録

■全体のインデントを整形
Ctrl+K, Ctrl+D

■1行削除
Ctrl + l

■定義に移動
F12

■リファクタリング
Ctrl + .(ピリオド)

■ソリューションのビルド
Ctrl + Shift + B

■デバッグ実行
F5

■ステップイン
F10

■ステップオーバー
F11

■ステップアウト
Shift + F11

2015年9月23日水曜日

DataGridView 列ヘッダー セルのドロップダウン フィルタ一覧を作成する

ExcelのフィルターのようなことをDataGridViewでも同じ操作を実現できたら・・・
とだれしもが思いますよ。


そして、ネットで調べりゃすぐにこんなサイトにたどり着きます。

DataGridView 列ヘッダー セルのドロップダウン フィルタ一覧を作成する



なんだ、簡単にできるんだな。

さっそくダウンロード
Building a Drop-Down Filter List for a DataGridView Column Header Cell Sample Code
https://www.microsoft.com/en-us/download/details.aspx?id=23459


解凍し、DataGridViewAutoFilterを動かしてみます。
DataGridViewAutoFilter\DataGridViewAutoFilter\CS\DataGridViewAutoFilter¥DataGridViewAutoFilter.sin
中に DesignerSetupDemoがあるのでこちらを動かしてみます。







一番左のフィルターを使ってみます。


左から二番目のフィルターを使ってみます。


最後に Show Allのリンクをクリックをするとフィルターが解除されます。


サンプルで使われているデータは解凍したフォルダの
DataGridViewAutoFilter\DataGridViewAutoFilter\TestData.xml
をつかっているので、これを使いながらそれっぽいものを作ってみます。


いきなり、Filterの話をするのではなく、まずはxmlデータをDataGridViewに表示するところを行います。
フォームDataGridViewを配置し親ドッキングします。



今回は、 データセットを作りデータセットのReadXMLでデータをDataGridViewに流し込みます。
ファイルの追加でデータセットを選択

名前はそのままでも問題ないです。


DataSet1.xsdを選択し、データセットを表示しツールボックスのDataTableをドラッグ&ドロップします



XMLの繰り返し部分であるCustomerOrderDataをDataTable名に
書くタグをプロパティ名として追加します。






あと、DataSetの画面で背景部分のしましまのとこをクリックすると
DataSetのプロパティが表示されます。

Namespaceに http://tempuri.org/DataSet1.xsd
って記載されてるので、削除しておきましょ。


 → 




では、読み込みます
private void Form1_Load(object sender, EventArgs e)
        {
            this.dataSet1.ReadXml("TestData.xml");
        }


データが表示できたのでようやく本題のフィルタの追加をします。
まずは、DataGridViewAutoFilter.dllを追加します。
そして、using DataGridViewAutoFilter; を追加します

今度は、DataGridViewの列の編集画面を開きます
ColumnTypeを選択するとDataGridViewAutoFilterTextBoxColumnってのが選べます
ここでは、すべての列にフィルターを表示したいので選択します。

そして、実行。


できた。


ついでに、DEMOのアプリみたくクリアボタンを追加してみます。
statusStripを追加。
その上に、linkLabelを追加。



private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            DataGridViewAutoFilterColumnHeaderCell.RemoveFilter(this.dataGridView1);
        }

DataGridViewAutoFilterColumnHeaderCellクラスのクラスメソッド(RemoveFilter)の
引数にDataGridViewAutoFilterを使っているDataGridViewのインスタンスを渡せばよいってことですね。


ちなみに、Filterを使用していないのに最初からAllFilterClearのリンクがでてるのもあれです。
そこで、DataGridViewを何かしらの操作を行い、その結果をBindして完了したタイミングで
Filterの状態を調べFilterを使用していなければ linkLabelの表示を消してしまうってのが考えられます。
private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            string filterStatus = DataGridViewAutoFilterColumnHeaderCell.GetFilterStatus(this.dataGridView1);

            if (string.IsNullOrEmpty(filterStatus))
            {
                this.linkLabel1.Visible = false;
            }
            else
            {
                this.linkLabel1.Visible = true;
            }
        }

またもや、DataGridViewAutoFilterColumnHeaderCellクラスのクラスメソッドを利用します。
今度は、GetFilterStatusというメソッドです。引数はもちろん、DataGridViewのインスタンス。

これでステータスがとれます。
フィルターが未選択だと空文字が返ってきます。

選択した後だと"12 of 2155 records found"のような文字列が返ってきます




2015年9月22日火曜日

SQLiteでDataTableを使ってDataGridViewに表示する

■準備

System.Data.SQLite.dllを追加します。




usingを追加します。
using System.Data.SQLite;

適当なデータを用意します。
今回は、都道府県と県庁データとしときます。
テーブルはこんな感じ。

CREATE TABLE [sample] (
  [pref] TEXT,
  [capital] TEXT,
  PRIMARY KEY(pref)
);




フォームにDataGridViewを配置します。
データを読み込みます。


private void Form1_Load(object sender, EventArgs e)
        {
            string dbPath = Application.StartupPath + @"\Data.db";
            using (SQLiteConnection con = new SQLiteConnection("Data Source=" + dbPath))
            {
                //空のテーブルを作ります。
                //この時点では、DataGridViewと紐づいていません。
                DataTable dataTable = new DataTable();

                //DataTableに読み込むデータをSQLで指定します。
                //今回はDataTableを指定していないので、SELECTで表示する列名が
                //のちのち紐づけを行った際のDataGridViewの列名になります。
                SQLiteDataAdapter adapter = new SQLiteDataAdapter("SELECT * FROM sample;", con);
                adapter.Fill(dataTable);

                //データテーブルをDataGridViewに紐づけます。
                this.dataGridView1.DataSource = dataTable;
            }
        }






■表示をクリアする
DataTableとDataGridViewをbindingしていると this.DataGridView1.Rows.Clear() ではクリアできません。
DataGridViewはあくまでもDataTableの内容をただ表示しているだけなので、DataTable自身をクリアしないといけません。



using System;
using System.Data;
using System.Data.SQLite;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private DataTable datatable { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string dbPath = Application.StartupPath + @"\Data.db";
            using (SQLiteConnection con = new SQLiteConnection("Data Source=" + dbPath))
            {
                con.Open();
                
                //空のテーブルを作ります。
                //この時点では、DataGridViewと紐づいていません。
                this.datatable = new DataTable();

                //DataTableに読み込むデータをSQLで指定します。
                //今回はDataTableを指定していないので、SELECTで表示する列名が
                //のちのち紐づけを行った際のDataGridViewの列名になります。
                SQLiteDataAdapter adapter = new SQLiteDataAdapter("SELECT * FROM sample;", con);
                adapter.Fill(this.datatable);

                //データテーブルをDataGridViewに紐づけます。
                this.dataGridView1.DataSource = this.datatable;
            }
        }

        private void BtnClear_Click(object sender, EventArgs e)
        {
            this.datatable.Clear();
        }
    }
}


表示を消したなら、再表示したいとこですね。
Readボタンを追加。



using System;
using System.Data;
using System.Data.SQLite;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private DataTable datatable { get; set; }
        private SQLiteConnection con { get; set; }
        private SQLiteDataAdapter adapter { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string dbPath = Application.StartupPath + @"\Data.db";
            this.con = new SQLiteConnection("Data Source=" + dbPath);
            this.con.Open();
            
            //空のテーブルを作ります。
            //この時点では、DataGridViewと紐づいていません。
            this.datatable = new DataTable();

            //データテーブルをDataGridViewに紐づけます。
            this.dataGridView1.DataSource = this.datatable;

            //データを読み込みます
            this.DataRead();
        }

        private void BtnClear_Click(object sender, EventArgs e)
        {
            this.datatable.Clear();
        }

        private void BtnRead_Click(object sender, EventArgs e)
        {
            this.DataRead();
        }

        private void DataRead()
        {
            //DataGridViewにどんどん追加されるので、一度初期化してから追加する
            this.datatable.Clear();
            adapter = new SQLiteDataAdapter("SELECT * FROM sample;", con);
            adapter.Fill(this.datatable);
        }
    }
}


最後にDataGridViewの内容を変更した結果をDBに反映します。



using System;
using System.Data;
using System.Data.SQLite;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private DataTable datatable { get; set; }
        private SQLiteConnection con { get; set; }
        private SQLiteDataAdapter adapter { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string dbPath = Application.StartupPath + @"\Data.db";
            this.con = new SQLiteConnection("Data Source=" + dbPath);
            this.con.Open();
            
            //空のテーブルを作ります。
            //この時点では、DataGridViewと紐づいていません。
            this.datatable = new DataTable();

            //データテーブルをDataGridViewに紐づけます。
            this.dataGridView1.DataSource = this.datatable;

            //データを読み込みます
            this.DataRead();
        }

        private void BtnClear_Click(object sender, EventArgs e)
        {
            this.datatable.Clear();
        }

        private void BtnRead_Click(object sender, EventArgs e)
        {
            this.DataRead();
        }

        private void DataRead()
        {
            //DataGridViewにどんどん追加されるので、一度初期化してから追加する
            this.datatable.Clear();
            adapter = new SQLiteDataAdapter("SELECT * FROM sample;", con);
            adapter.Fill(this.datatable);
        }

        private void BtnUpdate_Click(object sender, EventArgs e)
        {
            try
            {
                using(SQLiteTransaction trans = this.con.BeginTransaction())
                {
                    SQLiteCommandBuilder builder = new SQLiteCommandBuilder(this.adapter);
                    builder.SetAllValues = false;
                    builder.ConflictOption = ConflictOption.OverwriteChanges;

                    this.adapter.Update(this.datatable);
                    trans.Commit();
                }

                MessageBox.Show("Update完了");
            }
            catch (Exception ex) 
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}

SQLiteCommandBuilderがDataTableの列名や主キーなどの情報と
変更内容から適宜、SQLを組み立てて実行してくれます。

ちなみに、下記のようにBtnUpdate_Clickを書き換えると・・・

private void BtnUpdate_Click(object sender, EventArgs e)
        {
            try
            {
                using(SQLiteTransaction trans = this.con.BeginTransaction())
                {
                    SQLiteCommandBuilder builder = new SQLiteCommandBuilder(this.adapter);
                    builder.SetAllValues = false;
                    builder.ConflictOption = ConflictOption.OverwriteChanges;

                    MessageBox.Show(builder.GetUpdateCommand().CommandText);
                    MessageBox.Show(builder.GetInsertCommand().CommandText);
                    MessageBox.Show(builder.GetDeleteCommand().CommandText);

                    int ret = this.adapter.Update(this.datatable);
                    trans.Commit();

                    string message = string.Format("Update完了\n{0}件変更", ret);
                    MessageBox.Show(message);
                }
            }
            catch (Exception ex) 
            {
                MessageBox.Show(ex.Message);
            }
        }







SQLiteCommandBuilderは、自動的に状況に応じてSQLを発行してくれるのでとても便利ではあるのですが
当然ながら万能なものではないので、あらゆるものがUpdateしたりDeleteしたりできるわけではないです。

そのため、主キーを利用したものに限られます。

あと、this.adapter.Update(this.datatable)で変更のレコード数を返します。
これは、Readするときに使っている
adapter.Fill(this.datatable);
も戻り値としてintの変数で受けるとSelectした行数が取得できます。

2015年9月20日日曜日

DataGridViewにデータを入れる②

今度はバインドさせてみます。


データは、下記に掲載されているxmlデータとしみましょう。
https://msdn.microsoft.com/ja-jp/library/bb387012.aspx

PurchaseOrder.xml
<?xml version="1.0"?>
<PurchaseOrder PurchaseOrderNumber="99503" OrderDate="1999-10-20">
  <Address Type="Shipping">
    <Name>Ellen Adams</Name>
    <Street>123 Maple Street</Street>
    <City>Mill Valley</City>
    <State>CA</State>
    <Zip>10999</Zip>
    <Country>USA</Country>
  </Address>
  <Address Type="Billing">
    <Name>Tai Yee</Name>
    <Street>8 Oak Avenue</Street>
    <City>Old Town</City>
    <State>PA</State>
    <Zip>95819</Zip>
    <Country>USA</Country>
  </Address>
  <DeliveryNotes>Please leave packages in shed by driveway.</DeliveryNotes>
  <Items>
    <Item PartNumber="872-AA">
      <ProductName>Lawnmower</ProductName>
      <Quantity>1</Quantity>
      <USPrice>148.95</USPrice>
      <Comment>Confirm this is electric</Comment>
    </Item>
    <Item PartNumber="926-AA">
      <ProductName>Baby Monitor</ProductName>
      <Quantity>2</Quantity>
      <USPrice>39.98</USPrice>
      <ShipDate>1999-05-21</ShipDate>
    </Item>
  </Items>
</PurchaseOrder>

新しい項目の追加 から データセットを追加します。


追加したデータセットに対し、格納する列の定義をします。
ツールボックスからDataTableをDrag & Dropで追加します。




DataGridViewに表示したいXMLのデータは、1行単位で考えるとAddressタグが1行データに該当します。
じゃ、DataTable名にでもしましょうか。

で、各タグ名を追加します


ついでに、DataSet自信をクリックして、プロパティにある Namespaceを空にします。


今度は、フォームを開いてDataGridViewを追加して、今作ったDataSetをDataGridViewにバインドします。




追加した結果、dataSet12, bindingSource2 として追加されました。


後は、Loadイベントにでも追加すれば表示できます。
using System;
using System.Windows.Forms;

namespace DataGridView
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load_1(object sender, EventArgs e)
        {
            this.dataSet12.ReadXml("PurchaseOrder.xml");
        }
    }
}


2015年9月19日土曜日

セルの値をまとめて取得する

Excelのデータは、1つずつアクセスするとすごい遅いので
2次元配列でごそっととってそれぞれをアクセスするほうが圧倒的に効率的です。


では、サンプルを書いておきます。




今回は、行列の数がわかっている設定としておきます。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Excel.Application xlApplication = null;
            Excel.Workbooks xlWorkbooks = null;
            Excel.Workbook xlWorkbook = null;
            Excel.Sheets xlSheets = null;
            Excel.Worksheet xlWorksheet = null;

            try
            {
                string filePath = Application.StartupPath + @"\Data.xlsx";

                xlApplication = new Excel.Application();
                xlApplication.Visible = true;

                xlWorkbooks = xlApplication.Workbooks;
                xlWorkbook = xlWorkbooks.Open(filePath);

                xlSheets = xlWorkbook.Sheets;
                xlWorksheet = xlSheets[1];

                object[,] cellData = new object[10, 5]; //受け取るデータの個数分(行、列セル数を指定)
                cellData = xlWorksheet.get_Range("A1", "E10").Value; //Cellsは激遅なのでRangeを利用します。
                
                xlWorkbooks.Close();
                xlApplication.Quit();

                //データを出力 要素数は1から格納されています
                for (int r = 1; r <= cellData.GetLength(0); r++)
                {
                    for (int c = 1; c <= cellData.GetLength(1); c++)
                    {
                        Console.Write(string.Format(" {0} ", cellData[r, c]));
                    }

                    Console.WriteLine(string.Empty);
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                if (xlSheets != null)
                {
                    Marshal.FinalReleaseComObject(xlSheets);
                }

                if (xlWorkbook != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbook);
                }

                if (xlWorkbooks != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbooks);
                }

                if (xlApplication != null)
                {
                    Marshal.FinalReleaseComObject(xlApplication);
                }
            }
        }
    }
}

2015年9月6日日曜日

DataGridViewにデータを入れる①

まずは非バインドモードで入れてみます。

入れるデータは、郵便局のCSVデータでも入れてみますか。
http://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html

北海道あたりのデータでも使ってみましょう。
では、先頭の9列でもデータを取り込んでみましょう。

まずは、取り込む先を確保します。
DataGridViewを配置し、9列作成します。
名称も変更せず適当に・・・。



CSVは、DEBUGフォルダ配下にでもおいておきますか。





using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace DataGridView
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.DataLoad();
        }

        private void DataLoad()
        {
            foreach (string line in File.ReadLines("01HOKKAI.CSV", Encoding.Default))
            {
                string[] csv = line.Split(',');
                string[] data = new string[9];
                Array.Copy(csv, 0, data, 0, 9);
                this.dataGridView1.Rows.Add(data);
            }
        }
    }
}

csvをスプリットして先頭の9データを1行ずつDataGridViewに追加しています。

一方で、効率の面から、Rowデータをまとめていれるというやり方もります


using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace DataGridView
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.DataLoad();
        }

        private void DataLoad()
        {
            string[] csvDataAll = File.ReadAllLines("01HOKKAI.CSV", Encoding.Default);
            
            //データの分配列を用意
            DataGridViewRow[] rows = new DataGridViewRow[csvDataAll.Length];

            for(int i = 0; i < csvDataAll.Length; i++)
            {
                string[] csv = csvDataAll[i].Split(',');
                string[] data = new string[9];
                Array.Copy(csv, 0, data, 0, 9);

                DataGridViewRow row = new DataGridViewRow();
                row.CreateCells(this.dataGridView1);
                row.SetValues(data);
                rows[i] = row;
            }

            this.dataGridView1.Rows.AddRange(rows);

        }
    }
}
DataGridViewに直接何度も放り込むと時間がかかるので、 Rowデータを配列に保持して1度に放り込むと速いという話ですね。 参考までに速度を比較しておきます。 8245レコード9列分のデータを追加する場合だと 1レコードずつDataGridViewへ追加
2.4792633
2.5976070
2.4511964
2.9920726
2.8634533
2.3278521
2.7264484
2.5300329
2.3477178
2.4274311
Ave. 2.57秒 1度にDataGridViewへ追加
0.9712624
1.1560973
1.1795451
1.0009344
1.1347776
1.0131825
1.0595981
1.0133348
1.0227687
1.1925171
Ave. 1.07秒



2015年9月1日火曜日

Excel列名の変換(数字⇔文字列)

Rangeによるアクセスの方が大きく速いことを考えると
数字→文字列変換等が必要になるのでメモ。

Range, Cellsのパフォーマンスは以下より

Excel 100×10のデータを取得する方法の比較
Range vs Cells(1セルを取得)
Range vs Cells(範囲を取得)



数字→文字列
public string ToAlphabet(int columnNo)
{
    string alphabet = "ZABCDEFGHIJKLMNOPQRSTUVWXY";
    string columnStr = string.Empty;
    int m = 0;

    do
    {
        m = columnNo % 26;
        columnStr = alphabet[m] + columnStr;
        columnNo = columnNo / 26;
    } while (0 < columnNo && m != 0);

    return columnStr;
}
文字列→数字
public string ToAlphabet(int columnNo)
{
    columnStr = columnStr.ToUpper();

    double result = 0;
    for (int i = 0; i < columnStr.Length; i++)
    {
        int x = columnStr.Length - i - 1;
        int num = Convert.ToChar(columnStr[x]) - 64;

        result += num * Math.Pow(26, i);
    }

    return Convert.ToInt32(result);
}

2015年8月30日日曜日

Excelメモ

■値の取得
セルの値をまとめて取得する

■その他
Excel列名の変換(数字⇔文字列)

■パフォーマンス
Range vs Cells(1セルを取得)
Range vs Cells(範囲を取得)
Excel 100×10のデータを取得する方法の比較

Excel 100×10のデータを取得する方法の比較

A1~J100の範囲のデータを取得する

Case1:Cellsでぐるぐる回しながら取得する
Case2:get_Rangeでぐるぐる回しながら取得する
Case3:範囲のデータを1度に取得する

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Excel.Application xlApplication = null;
            Excel.Workbooks xlWorkbooks = null;
            Excel.Workbook xlWorkbook = null;
            Excel.Sheets xlSheets = null;
            Excel.Worksheet xlWorksheet = null;

            try
            {
                xlApplication = new Excel.Application();
                xlApplication.Visible = true;
                xlWorkbooks = xlApplication.Workbooks;
                xlWorkbook = xlWorkbooks.Add();
                xlSheets = xlWorkbook.Sheets;
                xlWorksheet = xlSheets[1];

                Case1(xlWorksheet);
                Case2(xlWorksheet);
                Case3(xlWorksheet);

                xlWorkbooks.Close();
                xlApplication.Quit();
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                if (xlSheets != null)
                {
                    Marshal.FinalReleaseComObject(xlSheets);
                }

                if(xlWorkbook != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbook);
                }

                if (xlWorkbooks != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbooks);
                }

                if (xlApplication != null)
                {
                    Marshal.FinalReleaseComObject(xlApplication);
                }
            }
        }

        static void Case1(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Excel.Range xlRange = null;
            for (int r = 1; r <= 100; r++)
            {
                for (int c = 1; c <= 10; c++)
                {
                    xlRange = xlWorksheet.Cells[r, c];
                    Marshal.FinalReleaseComObject(xlRange);
                }
            }
            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }

        static void Case2(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Excel.Range xlRange = null;
            string[] column = new string[]{ string.Empty, "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"};
            for (int r = 1; r <= 100; r++)
            {
                for (int c = 1; c <= 10; c++)
                {
                    xlRange = xlWorksheet.get_Range(column[c] + r.ToString());
                    Marshal.FinalReleaseComObject(xlRange);
                }
            }
            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }

        static void Case3(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            object[,] cellData = new object[100, 10];
            cellData = xlWorksheet.get_Range("A1", "J100").Value;

            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }
    }
}
[Case1]00:00:24.1975109
[Case2]00:00:06.7251560
[Case3]00:00:00.0383916

比較するまでもないよな。。。

Range vs Cells(1セルを取得)


A1を1000回取得する

Case1:get_Range
Case2:Cells

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Excel.Application xlApplication = null;
            Excel.Workbooks xlWorkbooks = null;
            Excel.Workbook xlWorkbook = null;
            Excel.Sheets xlSheets = null;
            Excel.Worksheet xlWorksheet = null;

            try
            {
                xlApplication = new Excel.Application();
                xlApplication.Visible = true;
                xlWorkbooks = xlApplication.Workbooks;
                xlWorkbook = xlWorkbooks.Add();
                xlSheets = xlWorkbook.Sheets;
                xlWorksheet = xlSheets[1];

                Case1(xlWorksheet);               
                Case2(xlWorksheet);

                xlWorkbooks.Close();
                xlApplication.Quit();
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                if (xlSheets != null)
                {
                    Marshal.FinalReleaseComObject(xlSheets);
                }

                if(xlWorkbook != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbook);
                }

                if (xlWorkbooks != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbooks);
                }

                if (xlApplication != null)
                {
                    Marshal.FinalReleaseComObject(xlApplication);
                }
            }
        }

        static void Case1(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Excel.Range xlRange = null;
            for (int i = 0; i < 1000; i++)
            {
                xlRange = xlWorksheet.get_Range("A1");
                Marshal.FinalReleaseComObject(xlRange);
            }
            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }

        static void Case2(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Excel.Range xlRange = null;
            for (int i = 0; i < 1000; i++)
            {
                xlRange = xlWorksheet.Cells[1, 1];
                Marshal.FinalReleaseComObject(xlRange);
            }
            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }
    }
}
[Case1]00:00:04.5715318
[Case2]00:00:08.4989196

やはり、get_Rangeが速い模様

Range vs Cells(範囲を取得)


A1:A10の範囲を1000回取得する。現実的にある数値にしてみた。

Case1:get_Range
Case2:Cells 一度Rangeに置き換えてからCells
Case3:Cells 型変換して直接Cellsへ

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Excel.Application xlApplication = null;
            Excel.Workbooks xlWorkbooks = null;
            Excel.Workbook xlWorkbook = null;
            Excel.Sheets xlSheets = null;
            Excel.Worksheet xlWorksheet = null;

            try
            {
                xlApplication = new Excel.Application();
                xlApplication.Visible = true;
                xlWorkbooks = xlApplication.Workbooks;
                xlWorkbook = xlWorkbooks.Add();
                xlSheets = xlWorkbook.Sheets;
                xlWorksheet = xlSheets[1];

                Case1(xlWorksheet);               
                Case2(xlWorksheet);
                Case3(xlWorksheet);

                xlWorkbooks.Close();
                xlApplication.Quit();
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                if (xlSheets != null)
                {
                    Marshal.FinalReleaseComObject(xlSheets);
                }

                if(xlWorkbook != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbook);
                }

                if (xlWorkbooks != null)
                {
                    Marshal.FinalReleaseComObject(xlWorkbooks);
                }

                if (xlApplication != null)
                {
                    Marshal.FinalReleaseComObject(xlApplication);
                }
            }
        }

        static void Case1(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Excel.Range xlRange = null;
            for (int i = 0; i < 1000; i++)
            {
                xlRange = xlWorksheet.get_Range("A1", "A10");
                Marshal.FinalReleaseComObject(xlRange);
            }
            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }

        static void Case2(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Excel.Range stRange = null;
            Excel.Range edRange = null;
            Excel.Range xlRange = null;
            for (int i = 0; i < 1000; i++)
            {
                stRange = xlWorksheet.Cells[1, 1];
                edRange = xlWorksheet.Cells[1, 10];
                xlRange = xlWorksheet.get_Range(stRange, edRange);
                Marshal.FinalReleaseComObject(stRange);
                Marshal.FinalReleaseComObject(edRange);
                Marshal.FinalReleaseComObject(xlRange);
            }
            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }

        static void Case3(Excel.Worksheet xlWorksheet)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Excel.Range xlRange = null;
            for (int i = 0; i < 1000; i++)
            {
                xlRange = xlWorksheet.get_Range((Excel.Range)xlWorksheet.Cells[1, 1], (Excel.Range)xlWorksheet.Cells[1, 10]);
                Marshal.FinalReleaseComObject(xlRange);
            }
            sw.Stop();
            Console.WriteLine("[{0}]{1}", MethodBase.GetCurrentMethod().Name, sw.Elapsed);
        }
    }
}

[Case1]00:00:04.4432538
[Case2]00:00:19.4220595
[Case3]00:00:18.9210169

get_Rangeが速い模様。

2015年6月15日月曜日

stylecopをexpress2010に導入のメモ

StyleCopは、こちらからダウンロード。


[Project名].csproj

<import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />の下に以下のように追加。

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Condition="Exists('$(ProgramFiles)\MSBuild\StyleCop\v4.7\StyleCop.targets') and '$(Configuration)' != 'Debug'"
Project="$(ProgramFiles)\MSBuild\StyleCop\v4.7\StyleCop.targets" />

存在チェックとリリース時のみ実行の書き方。

2015年1月4日日曜日

メモALL

■初期化
・配列
string[] str = {"aaa", "bbb", "ccc"};
int[] num = {1, 2, 3, 4, 5};
List list = new List {1, 2, 3};



・structの初期化
using System.Collections.Generic;

namespace SampleCode {

    class Program {
        static void Main(string[] args) {

            List list = new List();
            list.Add(new MyClass { age = 3, name = "bbb" });
            list.Add(new MyClass { age = 10, name = "aaa"});
        }
    }

    struct MyClass {
        public string name { get; set; }
        public int age { get; set; }
    }
}

LINQメモ

LINQ SampleCode
ど忘れした時のための参考サンプルコード

・メソッド一覧
Aggregate
All
Any
Count
DefaultIfEmpty

・いろいろ
大文字、小文字を無視してDistinct

Distinct

■public static IEnumerable Distinct
(this IEnumerable source);
// 概要:
// 既定の等値比較子を使用して値を比較することにより、シーケンスから一意の要素を返します。
//
// パラメーター:
// source:
// 重複する要素を削除する対象となるシーケンス。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// ソース シーケンスの一意の要素を格納する System.Collections.Generic.IEnumerable
//
// 例外:
// System.ArgumentNullException:
// source が null です。

0 1 2 3 4 5 4 3 2 1 0 の重複を取り除く
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] num = { 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0 };
            
            foreach (var n in num.Distinct()) {
                Console.WriteLine(n);
            }
        }
    }
}

実行結果
0
1
2
3
4
5

2015年1月3日土曜日

DefaultIfEmpty

■public static IEnumerable DefaultIfEmpty(this IEnumerable source);
// 概要:
// 指定したシーケンスの要素を返します。シーケンスが空の場合はシングルトン コレクションにある型パラメーターの既定値を返します。
//
// パラメーター:
// source:
// シーケンスが空の場合に、既定値を返すシーケンス。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// source が空の場合、TSource 型の既定値を格納する System.Collections.Generic.IEnumerable
// オブジェクト。それ以外の場合は source。
//
// 例外:
// System.ArgumentNullException:
// source が null です。

最初に配列の要素数を用意し、あるルールに従ってその数字の要素数に値を格納する時
残った部分が未定義になってしまうのでDefault値を設定してしまおうという場合
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] numList = new int[10];
            numList[3] = 3;
            numList[5] = 5;

            var numDefault = numList.DefaultIfEmpty();

            foreach (var num in numDefault) {
                Console.WriteLine(num);
            }
        }
    }
}
要素番号3,5以外は0になります。


ちなみに、Listに使うとnullが代入されます
using System;
using System.Collections.Generic;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            List strList = new List();
            Console.WriteLine(strList.Count()); //0

            var strDefault = strList.DefaultIfEmpty();
            Console.WriteLine(strDefault.Count()); //0

            foreach (var str in strDefault) {
                Console.WriteLine(str == null); //True
            }
        }
    }
}

Count

■public static int Count
(this IEnumerable source, Func predicate);
// 概要:
// 条件を満たす、指定されたシーケンス内の要素の数を表す数値を返します。
//
// パラメーター:
// source:
// テストおよびカウントする要素が格納されているシーケンス。
//
// predicate:
// 各要素が条件を満たしているかどうかをテストする関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 述語関数の条件を満たす、シーケンス内の要素数を表す数値。
//
// 例外:
// System.ArgumentNullException:
// source または predicate が null です。
//
// System.OverflowException:
// source 内の要素数が System.Int32.MaxValue を超えています。

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            string test = "gooooooooooooooooooooooooooogle";
            
            //LINQで oの個数を調べる
            int count = test.Count(c => c == 'o'); //ラムダ式を渡す
            Console.WriteLine("oの数は{0}個です", count); //oの数は27個です
        }
    }
}

2015年1月2日金曜日

AsParallel

■public static ParallelQuery AsParallel
(this IEnumerable source);
// 概要:
// クエリの並列化を有効にします。
//
// パラメーター:
// source:
// System.Linq.ParallelQuery に変換する System.Collections.Generic.IEnumerable
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// ParallelEnumerable 拡張メソッドにバインドされる System.Linq.ParallelQuery としての
// source。
//
// 例外:
// System.ArgumentNullException:
// source が null 参照 (Visual Basic の場合は Nothing) です。



using System;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] a = new int[100000000];
            a[a.Length - 1] = 1;
            var start1 = DateTime.Now;
            var bySerial = a.Count(c => c == 1);
            Console.WriteLine("Serial  :{0}", DateTime.Now - start1);

            var start2 = DateTime.Now;
            var byParallel = a.AsParallel().Count(c => c == 1);
            Console.WriteLine("Parallel:{0}", DateTime.Now - start2);
        }
    }
}

2コアのPCで実行したとき下記のような差がでました。
Serial :00:00:01.0920000
Parallel:00:00:00.7488000

並列化された場合どの順に処理が完了するか不明なため
順番が保障されなくなる点など気をつける必要が出てきますね。

Any

■public static bool Any
(this IEnumerable source);
// 概要:
// シーケンスに要素が含まれているかどうかを判断します。
//
// パラメーター:
// source:
// 空かどうかを確認する System.Collections.Generic.IEnumerable
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// ソース シーケンスに要素が含まれている場合は true。それ以外の場合は false。
//
// 例外:
// System.ArgumentNullException:
// source が null です。

引数がないと単純に要素を持っているかどうかを判定するだけのようですね。


■public static bool Any(this IEnumerable source, Func predicate);
// 概要:
// シーケンスの任意の要素が条件を満たしているかどうかを判断します。
//
// パラメーター:
// source:
// 述語を適用する要素を含む System.Collections.Generic.IEnumerable
//
// predicate:
// 各要素が条件を満たしているかどうかをテストする関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 指定された述語でソース シーケンスの要素がテストに合格する場合は true。それ以外の場合は false。
//
// 例外:
// System.ArgumentNullException:
// source または predicate が null です。

要素中に少なくとも一つ、引数に渡した関数がtrueであればtrueを返す

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] emptyList = { };
            //要素はありますか?
            Console.WriteLine(emptyList.Any()); //False
            
            int[] numberList = { 1, 3, 5, 7 };
            //要素はありますか?
            Console.WriteLine(numberList.Any()); //True

            //2の倍数はありますか?
            Console.WriteLine(numberList.Any(num => num % 2 == 0)); //False

            //3の倍数はありますか?
            Console.WriteLine(numberList.Any(num => num % 3 == 0)); //True
        }
    }
}

All

■public static bool All
(this IEnumerable source, Func predicate);
// 概要:
// シーケンスのすべての要素が条件を満たしているかどうかを判断します。
//
// パラメーター:
// source:
// 述語を適用する要素を格納している System.Collections.Generic.IEnumerable
//
// predicate:
// 各要素が条件を満たしているかどうかをテストする関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 指定された述語でソース シーケンスのすべての要素がテストに合格する場合は true。それ以外の場合は false。
//
// 例外:
// System.ArgumentNullException:
// source または predicate が null です。

例えば、商品のリストがあるとして負の値があればエラーとしたい時
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            int[] itemList = new int[] { 110, 210, 310, -100 };

            if (itemList.All(num => num < 0)) {
                Console.WriteLine("引数に問題ありません");
            }else{
                Console.WriteLine("引数に問題あります");
            }
        }
    }
}
こんな風にかけると。

Aggregate

各要素に対しアキュームレートするメソッドです。
アキュムレーターとは、演算結果を累積する処理のことを指すようです。

■public static TSource Aggregate
(this IEnumerable source, Func func);
// 概要:
// シーケンスにアキュムレータ関数を適用します。
//
// func:
// 各要素に対して呼び出すアキュムレータ関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// 戻り値:
// 最終的なアキュムレータ値。
//
// 例外:
// System.ArgumentNullException:
// source または func が null です。
//
// System.InvalidOperationException:
// source に要素が含まれていません。

■public static TAccumulate Aggregate
(this IEnumerable source, TAccumulate seed, Func func);
//
// 概要:
// シーケンスにアキュムレータ関数を適用します。指定されたシード値が最初のアキュムレータ値として使用されます。
//
// パラメーター:
// source:
// 集計対象の System.Collections.Generic.IEnumerable
//
// seed:
// 最初のアキュムレータ値。
//
// func:
// 各要素に対して呼び出すアキュムレータ関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// TAccumulate:
// アキュムレータ値の型。
//
// 戻り値:
// 最終的なアキュムレータ値。
//
// 例外:
// System.ArgumentNullException:
// source または func が null です。

■public static TResult Aggregate
(this IEnumerable source, TAccumulate seed, Func func, Func resultSelector);
// 概要:
// シーケンスにアキュムレータ関数を適用します。
// 指定したシード値は最初のアキュムレータ値として使用され、
// 指定した関数は結果値の選択に使用されます。
//
// パラメーター:
// source:
// 集計対象の System.Collections.Generic.IEnumerable
//
// seed:
// 最初のアキュムレータ値。
//
// func:
// 各要素に対して呼び出すアキュムレータ関数。
//
// resultSelector:
// 最終的なアキュムレータ値を結果値に変換する関数。
//
// 型パラメーター:
// TSource:
// source の要素の型。
//
// TAccumulate:
// アキュムレータ値の型。
//
// TResult:
// 結果の値の型。
//
// 戻り値:
// 変換された最終的なアキュムレータ値。
//
// 例外:
// System.ArgumentNullException:
// source、func、または resultSelector が null です。


aggregateメソッドでは名前の通り何かを行って集計するメソッドのようです。

こんなシチュエーションを考えてみました。
レジをイメージして商品のリスト(外税)に対し、お金を渡し消費税を掛けた金額を
差し引いていき、その結果いくら残ったか知りたいというような状況

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int initMoney = 1000;
            int[] itemList = new int[] { 110, 210, 310 };
            
            var resutMoney = itemList.Aggregate(initMoney, (money, num) => (money - (int)(num * tax)));
            Console.WriteLine(resutMoney);
        }
    }
}

商品のリストに3つ格納されているので3回繰り返し処理を行います。
この時ラムダ式内の各変数は以下のようになります。

1回目
money:1000 //initMoneyの値を受け取る
num : 110 //itemListの最初の値を受け取る

money :885 //1個目の値が差し引かれる

2回目
money:885 //1ループ目の結果を受け取る
num : 210 //itemListの2つ目の値を受け取る

money :665 //2個目の値が差し引かれる

3回目
money:665 //2ループ目の結果を受け取る
num : 310 //itemListの3つ目の値を受け取る

money :340 //3個目の値が差し引かれる

340が戻り値となる

これが2引数の場合ですね。
TAccumulate seed
Func func


せっかくなので3引数の場合も試してみます。
上の例では消費税をかけたものを切り捨てして引き算しています。
消費税をかけたのち、切り捨てせずに引き算して一番最後に切り捨てしてみましょう。

確認。
消費税をかけて切り捨てをした結果を引き算すると
340になります。

まずは、切り捨てを辞めて小数で結果を返してみますか。
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int initMoney = 1000;
            int[] itemList = new int[] { 110, 210, 310 };
            
            var resutMoney = itemList.Aggregate((double)initMoney, 
                                                (money, num) => (money - num * tax)
                                                );
            Console.WriteLine(resutMoney);
        }
    }
}
これなら、結果は338.5になります。
338.5に対し切り捨てを行ってみます。
第3引数で切り捨ての処理をしてみます。

using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int initMoney = 1000;
            int[] itemList = new int[] { 110, 210, 310 };
            
            var resutMoney = itemList.Aggregate((double)initMoney, 
                                                (money, num) => (money - num * tax),
                                                money2 => Math.Floor(money2)
                                                );
            Console.WriteLine(resutMoney);
        }
    }
}

(money, num) => (money - num * tax)で集約処理をした結果を
money2 => Math.Floor(money2) の式に渡しその結果を出力しています。
その結果338がかえってきます。

これが3引数の場合ですね。
TAccumulate seed
Func func
Func resultSelector


では、1引数の時はというと最初の要素がmoneyに渡され、第二要素以降がnumに渡されます。
(money, num) => (money - num * tax)

つまり、最初の例は
using System;
using System.Linq;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            double tax = 1.05;
            int[] itemList = new int[] { 1000, 110, 210, 310 }; //所持金を第一要素に格納した
            
            var resutMoney = itemList.Aggregate((money, num) => (money - (int)(num * tax)));
            Console.WriteLine(resutMoney);
        }
    }
}
結果は一緒ですが、商品のリストに所持金をいれるのは解釈的に気持ち悪いですね。
もっと他の最適な例があるのかもしれないのでそういう時に使うとします。

念のためラムダ式内の各変数は以下のようになります。

1回目
money:1000 //itemListの最初の値を受け取る
num : 110 //itemListの2番目の値を受け取る

money :885 //1個目の値が差し引かれる

2回目
money:885 //1ループ目の結果を受け取る
num : 210 //itemListの3番目の値を受け取る

money :665 //2個目の値が差し引かれる

3回目
money:665 //2ループ目の結果を受け取る
num : 310 //itemListの4番目の値を受け取る

money :340 //3個目の値が差し引かれる

340が戻り値となる

LINQ SampleCode

あの書き方どうだったっけというときに参照するようにいろいろコードを書いておく

ってときに見る用にいろんなサンプルコードを置いておく
・特定の文字をカウントする
string test = "gooooooooooooooooooooooooooogle";

//LINQで oの個数を調べる
int count = test.Count(c => c == 'o');

Console.WriteLine("oの数は{0}個です", count.ToString());


・数字を倍にして配列で受け取る
int[] num = { 1, 2, 3, 4, 5 };
num = num.Select(n => n * 2).ToArray();

・最初に現れる偶数は何番目か?
int[] numList = { 3, 8, 15, 18 };
Console.WriteLine(numList.Select((num, indexNo) => new { num ,indexNo})
                         .First(n =>  n.num % 2 == 0).indexNo);
Selectの第二引数を指定することで要素番号を取得する
Select内では、
numをキーにnumListから受け取る数字を値に指定
indexNoをキーに要素数を指定
Firstで2で割った時に0になる物が現れた時終了し
そのオブジェクトのindexNo(要素番号)を返す処理

・動的なAND条件
手っ取り早く、ある文字列の配列に対し、特定の文字全てを含んでいるかチェック
using System;
using System.Linq;

namespace ConsoleApplication1
{   
    class Program
    {
        static void Main(string[] args)
        {
            string[] str = { "睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走"};

            string[] check = { "月", "無" };

            var result = str.Where(s => check.All(c => -1 < s.IndexOf(c)));

            Console.WriteLine(string.Join(", ", result)); //水無月, 神無月
        }
    }
}
・動的なOR条件 手っ取り早く、ある文字列の配列に対し、特定の文字がどれか含んでいるかチェック
using System;
using System.Linq;

namespace ConsoleApplication1
{   
    class Program
    {
        static void Main(string[] args)
        {
            string[] str = { "睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走"};

            string[] check = { "弥", "無" };

            var result = str.Where(s => check.Any(c => -1 < s.IndexOf(c)));

            Console.WriteLine(string.Join(", ", result)); //弥生, 水無月, 神無月
        }
    }
}

2015年1月1日木曜日

LINQを学習してみる

LINQ to Objectsとは
"LINQ to Objects" という用語は、LINQ to SQL [LINQ to SQL] や LINQ to XML などの中間 LINQ プロバイダーまたは API を使用せずに、LINQ クエリを任意の IEnumerable コレクションまたは IEnumerable コレクションと直接組み合わせて使用することを意味します。
http://msdn.microsoft.com/ja-jp/library/bb397919.aspx

IEnumerableを継承していれば使えるということで
例えばstring型なんかも継承しているので使うことができます。

たとえば、下記文字列にoが何個含まれているか調べるとすると
gooooooooooooooooooooooooooogle


using System;
using System.Linq;
using System.Collections;

namespace SampleCode {
    class Program {
        static void Main(string[] args) {

            string test = "gooooooooooooooooooooooooooogle";
            //Inumerableが継承されているか確認する
            Console.WriteLine("Inumerable実装されて{0}", (test is IEnumerable) ? "いる" : "いない");

            //LINQで oの個数を調べる
            int count = test.Count(c => c == 'o'); //ラムダ式を渡す
            Console.WriteLine("oの数は{0}個です", count.ToString());
        } 
    }

}

とても便利ですね。
string型の場合一文字ずつ渡されるのでそれをcという変数で受け取り
カウントするさいの条件式を渡してあげているわけですね。
ラムダ式になじみがないとみづらいかもしれませんが
c => c == 'o' は、javascriptで言えばこれと一緒ですね
function(c){ return c == "o" }


LINQを使わないのであれば下記のようになりそうですね。
//LINQで oの個数を調べる
            int count = test.Count(c => c == 'o'); //ラムダ式を渡す
            Console.WriteLine("oの数は{0}個です", count.ToString());
            ↓
            //LINQで oの個数を調べる
            int count = 0;
            for (int i = 0; i < test.Length; i++) {
                count += (test.Substring(i, 1) == "o") ? 1 : 0;
            }

            Console.WriteLine("oの数は{0}個です", count.ToString());

LINQなら、明示的に1文字ずつ取り出す処理を書かなくてもやってくれるあたりがらくちんなんでしょうね。
と、いうことで使い方がすぐになれないLINQなので少しずつ勉強して使い方を覚えていく予定。