ぷろぐらみんぐ帳

C#とかJavaScriptとか

linq.jsをVisualStudioで使う

C#に慣れているとJavaScriptでもLINQを使いたくなることがある。linq.js自体の使い方はいろいろ探すと出て来るが、JavaScriptIDEとしてVisualStudioを使うことができるのでVS内で完結させてみた。IntelliSenseのおかげでほとんどC#と同じような感覚で扱える。

以下、VisualStudio2015での例。linq.jsはNuGetから取れる

  1. 開始→新しいプロジェクトから、テンプレート→Visual C#→Web→「ASP.NET Web Application」を選択。(例として、ソリューション名はWebApplication1とする)
  2. New ASP.NET Projectの設定が出てきたらとりあえず「Empty」を選択 f:id:enjyuu:20170709160424p:plain
  3. 「Configure Microsoft Azure Web App」が画面が出てきたらとりあえずキャンセル
  4. ソリューションができたら、ソリューションエクスプローラーから、プロジェクト「WebApplication1」を右クリック→追加→新しい項目→「VisualC#→Web→HTMLページ」(名前はmain.htmlとする) f:id:enjyuu:20170709160428p:plain
  5. ソリューションエクスプローラーの「main.html」を右クリック→スタートページに設定
  6. プロジェクト「WebApplication1」を右クリック→NuGetパッケージの管理→参照→「linq.js」で検索しインストー(いろいろ確認ダイアログ出てくるがOKで) f:id:enjyuu:20170709160432p:plain
  7. インストールが終わるとこのようにScriptsのフォルダができている。あとは煮るなり焼くなり f:id:enjyuu:20170709160736p:plain

導入

main.htmlのの中を次のようにする。以下、サンプルデータとしてprefsを使う。

    <script type="text/javascript" src="Scripts/linq.js"></script>
    <pre>
    <script type="text/javascript">
        var prefs = [
          {
              "index": 15, "name": "新潟県", "prefecturalCapital": "新潟市",
              "highestPeak": "小蓮華山", "highestPoint": 2766, "designatedCities": ["新潟市"]
          },
          {
              "index": 16, "name": "富山県", "prefecturalCapital": "富山市",
              "highestPeak": "立山", "highestPoint": 3015
          },
          {
              "index": 17, "name": "石川県", "prefecturalCapital": "金沢市",
              "highestPeak": "白山", "highestPoint": 2702
          },
          {
              "index": 18, "name": "福井県", "prefecturalCapital": "福井市",
              "highestPeak": "越前三ノ峰", "highestPoint": 2095
          },
          {
              "index": 19, "name": "山梨県", "prefecturalCapital": "甲府市",
              "highestPeak": "富士山", "highestPoint": 3776
          },
          {
              "index": 20, "name": "長野県", "prefecturalCapital": "長野市",
              "highestPeak": "奥穂高岳", "highestPoint": 3190
          },
          {
              "index": 21, "name": "岐阜県", "prefecturalCapital": "岐阜市",
              "highestPeak": "奥穂高岳", "highestPoint": 3190
          },
          {
              "index": 22, "name": "静岡県", "prefecturalCapital": "静岡市",
              "highestPeak": "富士山", "highestPoint": 3776, "designatedCities": ["静岡市", "浜松市"]
          },
          {
              "index": 23, "name": "愛知県", "prefecturalCapital": "名古屋市",
              "highestPeak": "茶臼山", "highestPoint": 1415, "designatedCities": ["名古屋市"]
          }
        ];
    </script></pre>

具体的なコードはprefs以下に。

linq.jsのどこがいいの?

→明らかに書く量が減る。例えばindexが偶数のデータだけ取り出してJSON文字列に直せとすると、linq.jsを使わない場合、

        var evens = [];
        for (var i = 0; i < prefs.length; i++) {
            if (prefs[i].index % 2 == 0) evens.push(prefs[i]);
        }
        document.writeln(JSON.stringify(evens));

これがlinq.jsの場合こうじゃ↓

        document.writeln(Enumerable.From(prefs).Where("$.index%2==0").ToJSON());

これで終わり。余計なループを書かなくてよくなってコードの可読性が上がるのもポイント(少なくとも自分は下のほうが見やすい)。

サンプルと実行結果

        //県名列挙
        Enumerable.From(prefs).WriteLine("$.name");
        /* -実行結果-
        新潟県
        富山県
        石川県
        福井県
        山梨県
        長野県
        岐阜県
        静岡県
        愛知県*/
        document.writeln();

        //indexが偶数番目の県+JSON化
        document.writeln(Enumerable.From(prefs).Where("$.index%2==0").ToJSON());
        /* -実行結果-
        [{"index":16,"name":"富山県","prefecturalCapital":"富山市","highestPeak":"立山","highestPoint":3015},{"index":18,"name":"福井県","prefecturalCapital":"福井市","highestPeak":"越前三ノ峰","highestPoint":2095},{"index":20,"name":"長野県","prefecturalCapital":"長野市","highestPeak":"奥穂高岳","highestPoint":3190},{"index":22,"name":"静岡県","prefecturalCapital":"静岡市","highestPeak":"富士山","highestPoint":3776,"designatedCities":["静岡市","浜松市"]}]*/
        document.writeln();

        //○○を最高峰とする県で最初にヒットしたもの
        var fuji = Enumerable.From(prefs).Where("$.highestPeak=='富士山'").First();
        document.writeln(JSON.stringify(fuji));
        /* -実行結果-
        {"index":19,"name":"山梨県","prefecturalCapital":"甲府市","highestPeak":"富士山","highestPoint":3776}*/
        fuji = Enumerable.From(prefs).Where("$.highestPeak=='富士山'").FirstOrDefault();
        document.writeln(JSON.stringify(fuji));
        /* 実行結果は同じ*/
        //var kita = Enumerable.From(prefs).Where("$.highestPeak=='北岳'").First();//ヒットしない→これは例外になる
        var kita = Enumerable.From(prefs).Where("$.highestPeak=='北岳'").FirstOrDefault();//ヒットしないが例外にはならない(undifinedになる)
        document.writeln(kita);
        /* -実行結果-
        undefined */
        document.writeln();

        //LINQ+ループ
        fuji = Enumerable.From(prefs).Where("$.highestPeak=='富士山'").ToArray();//ToArrayをしないとEnumerableのメソッドが渡される
        for (var f in fuji) {
            document.writeln(fuji[f].name);
        }
        /* -実行結果-
        山梨県
        静岡県*/
        document.writeln();

        //Dictionary化 (linq.jsで.NETのDictionaryを模して作られたコレクションなので、JavaScriptの連想配列とは異なる)
        Enumerable.From(prefs).ToDictionary("$.index", "$.name").ToEnumerable().WriteLine("'key:'+$.Key+' value:'+$.Value");
        /* -実行結果-
        key:15 value:新潟県
        key:16 value:富山県
        key:17 value:石川県
        key:18 value:福井県
        key:19 value:山梨県
        key:20 value:長野県
        key:21 value:岐阜県
        key:22 value:静岡県
        key:23 value:愛知県*/
        document.writeln();

        //政令指定都市がある県とその都市の一覧
        Enumerable.From(prefs).Where("$.designatedCities").SelectMany("$.designatedCities", "f,m => f.name+m").WriteLine();
        /* -実行結果-
        新潟県新潟市
        静岡県静岡市
        静岡県浜松市
        愛知県名古屋市 */
        document.writeln();

        //最高標高で降順ソート
        Enumerable.From(prefs).OrderByDescending("$.highestPoint")
            .Select("$.index+' '+$.name+' '+$.highestPeak+' '+$.highestPoint").WriteLine();
        /* -実行結果-
        22 静岡県 富士山 3776
        19 山梨県 富士山 3776
        21 岐阜県 奥穂高岳 3190
        20 長野県 奥穂高岳 3190
        16 富山県 立山 3015
        15 新潟県 小蓮華山 2766
        17 石川県 白山 2702
        18 福井県 越前三ノ峰 2095
        23 愛知県 茶臼山 1415*/
        document.writeln();

        //最高標高で降順、IDで昇順、最初の4つを取る
        Enumerable.From(prefs).OrderByDescending("$.highestPoint").ThenBy("$.index").Take(4)
            .Select("$.index+' '+$.name+' '+$.highestPeak+' '+$.highestPoint").WriteLine();
        /* -実行結果-
        19 山梨県 富士山 3776
        22 静岡県 富士山 3776
        20 長野県 奥穂高岳 3190
        21 岐阜県 奥穂高岳 3190*/
        document.writeln();

        //最高標高で昇順、IDで昇順、3つを取る(関数表記もできる:入力補間が働くので好みで)
        Enumerable.From(prefs)
        .OrderBy(function (x) { return x.highestPoint })
        .ThenBy(function (x) { return x.index })
        .Take(3).Select("$.index+' '+$.name+' '+$.highestPeak+' '+$.highestPoint").WriteLine();
        /* -実行結果-
        23 愛知県 茶臼山 1415
        18 福井県 越前三ノ峰 2095
        17 石川県 白山 2702*/

特に政令指定都市の列挙や、ソートの条件が複数の例は、linq.jsを使わないとかなり面倒なことになると思う。さあみんなもLINQを崇めよう。 linq.js Reference