ページ

2006年12月26日火曜日

JavaScript で LINQ 風のことをやる

Script and LINQ? より

いやぁ、おもしろいです。「JavaScript で LINQ 風の書き方なんて簡単にできるんじゃね?」 という話が紹介されています。以下、理解したことを自分なりにまとめておきます。 

まず JSON を使えば ※1 Anonymous Type と Initializer みたいなことができます。
(※1 「JSON を使えば」 っていうのもなんか変な言い方ですね。「JSON で使っている記法を使えば」 と言ったらいいのかな?)

// JavaScript
var person = { Name: "Hoge Taro", Age: 20, Tel: "111-1111" };

これだけで C# の

// C#
class Person
{
public string Name;
public int Age;
public string Tel;
}
Person person = new Person();
person.Name = "Hoge Taro";
person.Age = 20;
person.Tel = "111-1111";

とほとんど同じような意味になるわけです。
そして、配列だって

// JavaScript
var persons = [
{ Name: "Hoge Taro", Age: 20, Tel: "111-1111" },
{ Name: "Hoge Jiro", Age: 18, Tel: "222-2222" },
{ Name: "Hoge Saburo", Age: 15, Tel: "333-3333" }
];

と書けます。


さて、次に

// C#
var result =
from p in persons
where p.Age <= 18
select new {
Name = p.Name,
Age = p.Age
};

といういかにも LINQ という書き方についてです。
さすがに標準の JavaScript でこういう書き方はできません。しかし、

// JavaScript
var result =
persons.filter(function(p) { return p.Age <= 18; })
.map(function(p) {
return { Name: p.Name, Age: p.Age };
});

と書けます。LINQ を Extension Method を呼び出す形で書いたときとよく似てますね。
JavaScript では配列は Array クラスですが、上記の filter とか map とかは Array クラスのメソッドです。どうやら Mozilla ではこういったメソッドがサポートされているようです。しかし、IE では 「サポートされていないメソッド」 となってエラーになってしまいます。


「んじゃあ、使えないじゃん」 となるんですが、もともと JavaScript では実行時にメソッドとかを足していってクラス (みたいなもの) を作るわけです。ならば、Array クラスに filter とか map とかを自分で足してやればいいわけですね。


というわけで、以下のコードは IE6 でちゃんと動きました。
ちなみに、以下の filter とか map とかは一から自分で書いたわけじゃありません。Script# に含まれている sscorlib.js から拝借したものです (ちょっとだけ変更してますが)。
いやぁ、JavaScript ってすごいですね。この記事を読んだときは感動しちゃいました。

<script>
Array.prototype.filter = function(filter_function)
{
var result = [];
for (var i = 0; i < this.length; ++i)
{
if (filter_function(this[i]))
{
result[result.length] = this[i];
}
}
return result;
}

Array.prototype.map = function(map_function)
{
var result = [];
for (var i = 0; i < this.length; ++i)
{
result[result.length] = map_function(this[i]);
}
return result;
}

var persons = [
{ Name: "Hoge Taro", Age: 20, Tel: "111-1111" },
{ Name: "Hoge Jiro", Age: 18, Tel: "222-2222" },
{ Name: "Hoge Saburo", Age: 15, Tel: "333-3333" }
];

var result =
persons.filter(function(p) { return p.Age <= 18; })
.map(function(p) {
return { Name: p.Name, Age: p.Age };
});

for (var i = 0; i < result.length; ++i)
{
alert(result[i].Name + "," + result[i].Age);
}
</script>

0 件のコメント:

コメントを投稿