2012年05月08日

JavaScriptの可変長引数

JavaScriptには可変長引数なんて立派な仕組みはありません。というより、引数の受け渡しは(JavaやC#などのきっちり系言語に比べて)かなりいい加減です。



//引数1個の関数があったとして・・・
function doSomething(arg) {
alert(arg);
}

//引数を渡さなくてもエラーになりません。
doSomething();

//引数を余分に渡してもエラーになりません。
doSomething("引数1", "引数2");




とはいえ、可変長引数みたいなことはできます。functionの内部にいつでも存在してるargumentsを使います。



//関数の定義には引数がないけど・・・
function doSomething() {
//argumentsに渡された引数が入ってる!
for (var i = 0; i < arguments.length; i++) {
alert(arguments[i]);
}
}

doSomething("引数0", "引数1", "引数2");




ちなみにargumentsは配列っぽく使えますが、正確には配列(Array)ではありません。Argumentsというコレクションオブジェクトです。なので配列(Array)の関数を持ってなかったりします。たとえば「arguments.slice()」なんて書くと怒られちゃいます。





ラベル:javascript
posted by ぺるたご at 00:05| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする

2012年03月04日

.NETでチェックボックス同士を連動

今回はチェックボックス同士を連動させてみます。絵にするとこちら。
CheckBox.png
「全て」をチェックすると「項目1」〜「項目3」のチェックが切り替わります。また、「項目1」〜「項目3」をチェックすると「全て」のチェックが切り替わります。さらに、「全て」のチェックボックスはThree State(オン、オフ、中間の3状態)です。わりとよくありそうなパターンです。

こいつを素直にCheckedChangedイベントハンドラに実装するとうまくいきません。「全て」が変わったら「項目1」が変わって、「項目1」が変わったら「全て」が変わって、と連鎖してしまい、おかしなことになります。なのでCheckedChangedではなくClickを使いましょう。Clickイベントは、ユーザーがチェックボックスを触ったときだけ発生します。プログラムでチェック状態を変えたときには発生しません。都合がいいですね。ちなみに、スペースキーを押してチェックを変えてもClickイベントが発生します。サンプルはこちら。

Clickイベントによるチェックボックス連動
//めぼしいプロパティの設定(実際はデザイナで設定)
checkBoxAll.AutoCheck = false;
checkBoxAll.ThreeState = true;

//「全て」チェックボックスのClickイベントハンドラ
private void checkBoxAll_Click(object sender, EventArgs e)
{
checkBoxAll.Checked = !checkBoxAll.Checked;
checkBox1.Checked = checkBoxAll.Checked;
checkBox2.Checked = checkBoxAll.Checked;
checkBox3.Checked = checkBoxAll.Checked;
}

//「項目1」〜「項目3」チェックボックスのClickイベントハンドラ
private void checkBox_Click(object sender, EventArgs e)
{
if (checkBox1.Checked && checkBox2.Checked && checkBox3.Checked)
{
//全部チェックされてたら「全て」をオンにする。
checkBoxAll.CheckState = CheckState.Checked;
}
else if (!checkBox1.Checked && !checkBox2.Checked && !checkBox3.Checked)
{
//全部チェックされてなかったら「全て」をオフにする。
checkBoxAll.CheckState = CheckState.Unchecked;
}
else
{
//中途半端にチェックされてたら「全て」を中間状態にする。
checkBoxAll.CheckState = CheckState.Indeterminate;
}
}


もう一つのコツとしては、AutoCheckプロパティをfalseにしていることです。というのも、ユーザーが「全て」を触るときはオンまたはオフの2値ですが、プログラムが「全て」を触るときはオン、オフ、中間の3値になります。なので「全て」のチェック状態をプログラムで制御する必要があります。とはいっても、「全て」のClickイベントハンドラ内の
checkBoxAll.Checked = !checkBoxAll.Checked;
の1行だけです。







ラベル:.net
posted by ぺるたご at 00:51| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする

2012年02月26日

.NETでDataTableの比較

今回は二つのDataTableの比較を行ってみます。二つのDataTableの中身が同じかどうか調べるというのは稀によくある事。まずはダメな例と、何の工夫もない例を挙げてみます。なお、以下で挙げる例は全て各セルの値だけを調べるものです。実際に使うときはDataTableがNULLじゃないか、列数や行数が同じかどうかを事前に調べる必要があります。

例1:ダメな例
private bool DataTableEqual(DataTable table1, DataTable table2)
{
return table1.Equals(table2);
}

例2:何の工夫もない例
private bool DataTableEqual(DataTable table1, DataTable table2)
{
var rowCount = table1.Rows.Count;
var columnCount = table1.Columns.Count;
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < columnCount; j++)
{
if (!table1.Rows[i][j].Equals(table2.Rows[i][j]))
{
return false;
}
}
}
return true;
}


例1のダメな例では、参照を比較しているのでダメです。普通は各セルの値を比較したいですよね。
例2は素直なロジックです。プロパティへのアクセスを少しでも減らすため、列数(columnCount)や行数(rowCount)は変数に逃がしておきましょう。僅かですが速くなります。


ここからが本題です。
.NET Framework3.5からは以下のように簡単にかけます。おそらく現状ではこれがベストだと思います。

例3:ベストな例
private bool DataTableEqual(DataTable table1, DataTable table2)
{
return table1.AsEnumerable().SequenceEqual(table2.AsEnumerable(), DataRowComparer.Default);
}


DataRowComparerが.NET Framework3.5から追加されたクラスで、DataRowを値で比較してくれます。

更に一歩進めて、「全列を比較しないで特定の列だけ比較したい」場合のロジックも用意してみました。まあ、あまりそんな状況はなさそうですが・・・。例3:ベストな例のところでDataRowComparer.Defaultの代わりに自作のComparerクラスを用意すればいいんですけど、面倒なときは以下のような感じで。

例4:特定の列だけ比較する例
private bool DataTableEqual(DataTable table1, DataTable table2)
{
Func<DataRow, IEnumerable<object>> selector = row => new[] { row["Column0"], row["Column1"], row["Column2"], row["Column3"], row["Column4"], row["Column5"], row["Column6"], row["Column7"], row["Column8"], row["Column9"] };
return table1.AsEnumerable().SelectMany(selector).SequenceEqual(table2.AsEnumerable().SelectMany(selector));
}


SelectManyメソッドで行と列を1次元のデータ構造にばらし、SequenceEqualメソッドで比較します。row["Column0"]〜row["Column9"]のあたりに比較したい列を指定してください。


一応、実行速度を測定してみました。10列×10万行のテーブルを比較したときにかかる時間で、単位はミリ秒です。私の環境では以下のようになりました。

何の工夫もない例:434
ベストな例:47
特定の列だけ比較する例:272

「LINQを使うと遅くなるんじゃね?」って懸念も裏腹に、圧倒的なスピードです。DataRowComparerはそのために用意されたのでしょうから速いのも当たり前かもしれませんけど。





ラベル:.net LINQ
posted by ぺるたご at 15:08| Comment(0) | TrackBack(0) | 日記 | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。