OmniAICreator/Qiita-1.07M huggingface dataset:
【Rails】link_to でPrefix pathへの複数idの渡し方

## はじめに
Railsではlink_toというヘルパーメソッドが用意されており、これを使えばリンクの作成がとても簡単に実装できます。

動的なリンクを作成する場合には、idを適切に渡してあげる必要があるのですが、その書き方について書いていこうと思います。
　idがひとつだけの場合は全く悩むことはないのですが、２つ以上ある場合はどうすればいいの？って思う方もいるかと思うので参考になれば幸いです。

先に今回扱うアプリの概要（動作）を載せておきます。

![54535a5bee0b6c93b58bb929235f0fb2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605568/f7171294-d566-32ce-f0d5-06c17bade798.gif)
投稿詳細へのリンク、投稿に対するコメントの削除リンクに飛んでいます。

## link_toの書き方
まずはlink_toの書き方から確認しましょう。いろいろな書き方があるかと思いますが、ここではprefix pathでの記述とします。（書くのが楽なので個人的にはprefixをオススメします）

```haml:hamlファイル
  = link_to "リンクの表示名", prefix_path(渡すid), httpメソッドの指定（必要な場合）

  -# もしくは以下
  = link_to prefix_path(渡すid), httpメソッドの指定（必要な場合） do
    "リンクの表示名"
```
例えばprefix pathがpost_pathでhttpメソッドがdeleteの場合は以下のように書けます。

```haml:hamlファイル
  = link_to "削除", post_path(post.id), method: :delete
```

link_toはデフォルトのhttpメソッドがGETなので、GETのリンクの場合はメソッドの指定が不要となります。

## Prefix_path と httpメソッドについて
いまいち理解できていない人のために一応書いておきます。

アプリのディレクトリにいる状態でターミナルに```rails routes```と入力するとルーティングの一覧を見ることができます。
<img width="911" alt="7b4835442b00e360b29a706b75b85632.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605568/12a79206-3c77-144c-e644-2f523dbf67d6.png">
一番上の行を見てみると、左から順にPrefix、Verb、URI Pattern、Controller#Actionと書かれていると思います。

Prefixの列に書かれてあるものに「_path」をつけるたものがPrefix_path、Verbの列にあるものがhttpメソッドだと思ってください。

これらを指定することでどのコントローラのどのアクションを動作させるかを決めることができます。

## 複数idとはどんな状況か
link_toに複数のid情報を渡す必要があるのはどんなときでしょうか？

それはルーティングがネストされてる状況です。

冒頭で出したアプリの例だと、コメントは投稿内容に紐づくモデルのため、コメントのルーティングは投稿の情報を持っている必要があります。routes.rbファイルとルーティングは次のようになっています。

```ruby:routes.rb
  resources :posts do
    resources :comments, only: [:create, :destroy]
  end
```
<img width="894" alt="fa05488af695912b6f16a688608540f1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605568/81eb8782-6b5e-0fb6-bb71-28a9527f2833.png">

ルーティングのURIパターン（真ん中）を見てみると、削除のURI（2行目）にはpost_idとidが書かれています。これが複数idをlink_toに渡す必要がある状況です。

## ではどのように記述するか
特に難しいことはなく、**渡すidを２つ書くだけ**です。
順番はURIパターンに登場するものを左から（親のidから）順に書きます。
例えば今回のコメント削除のリンクであれば以下のようになります。

```haml:hamlファイル
  = link_to "削除", post_comment_path(@post.id, @comment.id), method: :delete
```
idはインスタンス変数などを用いて適切な記述をすればOKです。

## 最後に
今回はidが２つの例で説明しましたが、３つ以上でも同じことをするだけです。
Prefix pathでの書き方は最初は慣れないかもしれませんが、慣れれば楽にコーディングできるので怖がらず積極的に使ってみましょう！

最後までご覧いただきありがとうございました。
空白と権限だけが変更されているファイル、新規追加されたファイルだけaddしたい

Gitを使ってくれない / 使えない環境と協業する際に、修正した差分だけじゃなくドカッとファイルを渡されたは良いけど、OSも違って全部diffに上がってくるコトがあったので、何度もファイル参照するのが大変なのでメモ。

## 空白と権限だけが変更されているファイルだけをaddする

結論からすると、下記コマンドで対応しました。

```bash
$ git diff -w --ignore-blank-lines -G"." --name-only | xargs git add
```

### 空白だけが変更されているファイルを除く

まずは空白が変更されているファイルを除くコマンドは、通常のオプションで用意されています。

```bash
$ git diff -w
```

何故かこれでもほぼ全てのファイルがdiffに上がってくるので何故だろうと思っていたら、権限も変わっていたのが原因でした。

### 権限だけが変わっているファイルを除く

よくある情報はconfigを変更する下記のコマンドを実行すると権限のチェックはされないようになります。

```
$ git config core.filemode false
```

ただ、本当に権限を設定しておきたい場合もあるかもしれないのでもう少し調べてみた所、[こんな情報が見つかりました。](https://salferrarello.com/git-diff-ignore-mode-changes/)

> When I’m looking at a git diff (or specifically a git diff --name-only where I’m looking for a list of files that have been changed), I often want to ignore mode changes (a.k.a. permission changes). By adding -G. to the command, mode changes will be ignored.

この記事によると`-G.`を付けると権限だけ変わっている物を除外出来るようです。

```
$ git diff -G.
```

本来の`-G`の用途は、正規表現でマッチした部分だけ表示する様にするオプションで、`.`に指定することで、**一文字でも変化があるファイルのみ一覧に出てくる**様です。

後は、`--name-only`オプションでファイル名だけ出力されるようにし、`xargs`で`git add`に渡しました。

## 新規追加されたファイルだけaddする

結論からすると、下記コマンドで対応しました。

```bash
$ git ls-files --o --exclude-standard | xargs git add
```

### インタラクティブモードで対応する

こちらも調べてみると良く出てくる情報は、下記のようなインタラクティブモードを使う方法でした。

```bash
# インタラクティブモードを起動
$ git add -i
# その後`a` > `*`と入力すると新規追加ファイルを全指定することが可能
```

参考 : [git addで新規ファイルのみ追加する](https://qiita.com/te2u/items/3eeee6a363966118d58d)

ただ、自分はものぐさで物覚えが悪いので、インタラクティブモード内で入力する内容を忘れてしまうと思い、一発で対応できる方法を考えてみました。

### 新規ファイルだけの一覧を取得する

前例に併せてxargsで渡せればと思ったので、新規ファイルだけの一覧を取得する方法を調べてみました。

```bash
$ git ls-files --o --exclude-standard
```

参考 : [git ls-files](https://qiita.com/zimathon/items/d964c4b3cc688cb2d114)

これで、後は`xargs`で`git add`に渡せばOKです。
UIViewのtransform3Dは実はiOS12では使えない

**iOS12からtransform3Dが追加されました。**z軸回転アニメーションも出来る凄いやつです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24979/0f633790-f524-eb37-ddde-1eb858d819bd.png)


https://developer.apple.com/documentation/uikit/uiview/3043565-transform3d

さて、こんなtransform3Dですが**iOS12では使えません。**

え？？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24979/8eeb82bc-da3b-598b-ea95-5a4c16f0e2f9.png)

いやいやいやいや

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24979/54a917c4-96a0-8892-6b1c-0386cb0fd2e5.png)

ビルド通るし…


<img width="1163" alt="スクリーンショット 2020-06-16 12.32.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24979/79810485-aadf-8d49-d885-820fcc90659c.png">

エッ…！？

```
Terminating app due to uncaught exception 'NSInvalidArgumentException'
 reason: '-[UIView setTransform3D:]: unrecognized selector sent to instance 0x7fb8a3404790'
```

意訳：setTransform3Dなんて知らないから例外で落としたよ

# 何が起きてるの？

わからないけど、多分setTransform3Dの実装が公開されてないけどavailableが付けられてしまった…？
前にもSceneKitかなにかでリリース前のiOSバージョンのavailableが付いていたりしたのでこのあたりは結構ガバガバなのかもしれない。

ちなみに内部的には `-(void)_setTransform3D:(CATransform3D)arg1 ;`というのが存在しているので、実装自体はありそう。
https://developer.limneos.net/index.php?ios=12.1&framework=UIKitCore.framework&header=UIView.h

iOS13では問題なく動作します。ランタイムでしか気がつけないバグでした。
もしかしたらiOS12.3とか途中から使えるようになっているかもしれないです。情報お持ちの方はコメントくださいmm


# おまけ

```
view.perform(Selector("_setTransform3D:"), with: CATransform3DIdentity)
```
質問ルール

# 駆け出し自分の用の質問ルールメモ

###・30分悩んで解決できないならすぐに質問をすること。
→質問するときは解決したい問題と期待する動作を伝える
→質問中はメモを必ず取る。
→途中でわからなかったり、早口すぎるときは相手に一旦ストップするように求める。

###・質問するための準備に時間をかけない
→質問するための準備を始めるとそこでまた悩み始め時間をロスするので

###・質問するのを恐れない！
→ここが一番大事。自分のプライドとか過去の経験則とか一切忘れて素直に聞くこと。
初めてのGit〜SourceTreeでリモートからデータを持ってきてコミットする

##はじめに
現場がSVNからGitに移行して自分が初めてGitに触る際、概念やCUIでのgitについての記事が多く、ひとまず今すぐGUIでデータを持ってきてコミットする手順が知りたいなと思ったので、自分が困ったことも含めてメモしておきたいと思います。
すでにソースがGit上にある前提で進みます。

##1.インストール
####Gitのインストール
インストールされていない場合インストールする
[windowsはこちら](https://gitforwindows.org/)
[macはこちら](https://gitforwindows.org/)
####SourceTreeのインストール
SourceTreeはGitをGUIで管理できるソフトです。
以下のサイトからSourcetreeをダウンロード
https://www.atlassian.com/ja/software/sourcetree
アカウントを登録する必要があります

##2.リモートからローカルにデータをコピーする＝クローン
SVNでいうチェックアウト
(1)プロジェクトのgitのURLを貰い、新規よりURLからクローンを選択
(2)ソースのURL、保存先パス名、ファイル名を記入してクローン
<img width="808" alt="１.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/bd80ef78-8eb6-2290-31b7-dad6f69e8f19.png">
##3.リモートからローカルにブランチをコピーする＝チェックアウト
最初「うっかりmaster直接にコミットせずに〇〇以下にコミットしてください」と言われて自分のローカルに同じようにブランチを切ってみたりしたのですが上手くいかず、困りました。

まずは〇〇のブランチをリモートからコピーしてきます。
####リモートの下の対象のブランチを右クリックして、チェックアウト
<img width="1056" alt="２.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/2d6691c6-2bd8-941e-d3a7-9b9c085d4c30.png">
<img width="365" alt="３.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/edbfd825-8fc1-8fc2-959c-14bb95611a28.png">
<img width="753" alt="４.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/c094c713-d0d6-7d6f-0272-c8e7f5f0a186.png">
##4.データを更新する
ブランチと書いてあるところがローカル上にあるブランチとなります。
####既存のブランチのデータを更新する場合
更新したいブランチ名をクリックし、更新したいブランチ名に二重丸がついて太字になっている状態でデータを更新します。
<img width="183" alt="５.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/e2277960-aacf-ab6a-6074-af4c0deb640e.png">

####新規ブランチを作成してデータを更新する場合
まずローカル上にブランチを作成します。
元となるブランチが選択された状態で、ブランチをクリックし、新規ブランチを作成します。
今回はprojextX下にhogeブランチを作ります。
<img width="749" alt="10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/0508a927-4403-bf00-5c7a-b77c70714da3.png">
作成されたブランチ名に二重丸がついて太字になっている状態でデータを更新します。
<img width="177" alt="11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/988ad654-e003-b13f-f641-3a966800c47d.png">


##5.データをコミット・プッシュする
ファイルを開いたり、データを変更したりすると、履歴の一番上に「コミットされていない変更があります」や「Uncomitted changes」という風に表示されます
<img width="776" alt="６.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/6a0f2c85-4e11-3d9f-b7ab-fe49b569171c.png">
ファイルステータスをクリックし、コミットしたいデータにチェックが入っていることを確認し、コメントを入れてコミットします。
いらないファイルや不要な行の変更があった場合ここで破棄することができます。
プッシュするまでは、リモート上のデータは変更されていない状態になります。ここですぐにプッシュするを選択することもできます。
<img width="860" alt="7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/3c900692-3e4e-d03b-758c-6935af251fa3.png">

プッシュを押してプッシュ先を確認してからOKを押します。
<img width="171" alt="8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/399558a4-80e8-7f3a-f298-0e15d3b0fe4c.png">
<img width="655" alt="9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/2e626a63-fa9d-3107-7335-bf72689a70c8.png">

##おわりに
ひとまずデータをコミットできました。
詳しいことは[サル先生のGit入門](https://backlog.com/ja/git-tutorial/)を読んでください。
また、ローカル上にブランチを作って練習してみると慣れてくると思います。
基本情報技術者試験レポ

##平成31年春期試験
申込みを忘れて受験できず・・・
申込日は受験日よりもかなり前なので、ここが最初の難関です。
######スケジュール
|時期|申込　　       |試験日　   |
|:--|:------------|:----------|
|春|1月半ば〜2月半ば|4月第3日曜日|
|秋|7月半ば〜8月半ば|10月第3日曜日|

ということで本記事は令和元年秋期基本情報技術者試験を受験した際のレポです
##受験当時の自己紹介（？）
・文系新卒１年目
・会社の研修で一通り勉強済み（参考書：徹底攻略　基本情報技術者教科書）
##結果
午前：63.75
午後：65.00（表計算選択）
ギリギリ合格でした
##勉強方法

暇な時にネット上の問題集（過去問道場・アプリ）で午前問題を解いてました。

[過去問道場](https://www.fe-siken.com/fekakomon.php)
ネット上で簡単にアカウントが作れてデータを残せたり、計算問題を出さない、計算問題だけ出すなど問題の出し方が工夫できるのが良かったです。

[【令和2年春対応】基本情報技術者試験 午前問題集](
https://apps.apple.com/jp/app/%E4%BB%A4%E5%92%8C2%E5%B9%B4%E6%98%A5%E5%AF%BE%E5%BF%9C-%E5%9F%BA%E6%9C%AC%E6%83%85%E5%A0%B1%E6%8A%80%E8%A1%93%E8%80%85%E8%A9%A6%E9%A8%93-%E5%8D%88%E5%89%8D%E5%95%8F%E9%A1%8C%E9%9B%86/id716625312)
オフラインで解けるので電車の中で活用していました。分野別で解けるところや、UIがシンプルで使いやすいところが良かったです。

直前に２、３年分ほど答え見つつな感じで午後問題を解きました。
午前問題を一通り解いていたので思ったよりは全然わからないという感じではなかったですが、もっと早くやっておけば良かったな（後悔）という感じでした。

##当日の感想
#####午前
当日見たことがない高校の数学的な問題がありビビりましたが、過去問で見たことのある問題も多かったです。
選択問題なので、なんとなく選択して当たったという問題が結構ありました。
#####午後
表計算は大学の卒論でエクセルを使ってデータを分析したりしてたので「なんとかなるやろ」と思ってたのですが、問題のクセが強いというか、出方があるのでもっと過去問を勉強しておけば良かったと思いました。
なんとなく問題文が読みやすかったのでネットワークを選択したのですが、勉強不足でほぼ不正解でした。
当日は結構時間が余るので、問題文をじっくり読めば多少当たりそうな分野の方を選択しておけば良かったなと思いました。
知識が無くてもゆっくり考えるとなんとなくわかるような問題もあるなと感じました。

#####その他
当日立教大学で受験をしたのですが、案内が難しい＆混んでおり、自分の番号に該当する部屋がどこにあるのかがわからずかなり迷ってしまいました。思っているより１５分くらい余裕を持って到着しておくと良いと思います。
後知らない場所でお昼食べに行ったり買いに行くのが面倒なので、おにぎりを持って行ったのがすごく良かったなと思いました。

##おわりに
自分は文系出身だったので、会社の研修で一通り教わっていたのが大きかったと思います。
でももっときちんと過去問を解いておけば良かったと思いました。
手応え・速報の点数的に落ちたかなと思ったのですが意外と受かることもあるので、数ヶ月前にきちんと申し込みをして、当日の朝起きて、試験を受けることが一番大切だと思いました。

基本情報の知識が後から、「ああそれ基本情報で出てきたやつ！」という進研ゼミの漫画みたいなことがあるので、受けておいて良かったなと思っています！
【ABC169(C問題)】PHPで浮動小数点数の丸め誤差を回避する

##はじめに
友人の影響で競技プログラミングに参加しています。
全然解けないのですが、問題が素晴らしいので、自分のような初心者にとっても勉強になることが多いなと感じています。
最近全然C問題が解けず・・・（白目）、169回目のC問題の復習をしたので載せます。

##問題
[問題はこちら](https://atcoder.jp/contests/abc169/tasks/abc169_c)
大体の内容：A（整数）＊B（少数）の小数点以下を切り捨てて表示する

簡単簡単！と↓で出してWA（不正解）でした

```php
<?php
	//入力読み込み
	fscanf(STDIN, "%d %f", $a,$b);
	//A×B
	$n = $a * $b;
	//切り捨て
	$n = floor($n);
	//文字で出ないようにする（気休め）
	printf("%.0F\n", $n);
     
?>
```

### 0.1 や 0.7 は二進数の二進数の浮動小数点数として正確に表現できない
[PHPマニュアルサイト　浮動小数点数](https://www.php.net/manual/ja/language.types.float.php)より
浮動少数点数の精度は有限なので、少数のまま計算を行い、後から小数点以下を切り捨てると誤差が出ます。

###解説を読んで
解説で100倍にして整数に直してから積を計算して、最後に100で割ることで誤差なく計算できるとありましたがいまいちうまくいかず。。。
以下通らなかった処理↓

```php
<?php
	fscanf(STDIN, "%d %s", $a, $b);
	$b = str_replace('.', '', $b);
	$n = floor(($a * $b) /100);
	printf("%.0F\n", $n);
?>
```

追記：コメントの方で、一度でも浮動小数点数になると誤差が出てしまうため、以下のように書くと良いというご指摘をいただきました。

```php

<?php
    fscanf(STDIN, "%d %f", $a, $b);
    echo intdiv($a * intval($b * 100 + 0.5), 100);
?>
```
###通った処理
他の方の回答を参考にして、以下通った処理です
######１.intdiv()を使うパターン
（%では余りの方を出しますが、その逆に割れた方の数を出すやつです。）

```php
<?php
	fscanf(STDIN, "%d %s", $a, $b);
	$b = str_replace('.', '', $b);
	echo intdiv($a * $b, 100);  
?>
```
######２.bcmul()を使うパターン
更に、bcmul()という今回の問題のためにあるような関数がありました
（ 2つの任意精度数値の乗算を行う、３つ目のパラメータに小数点以下の桁数を指定しない場合は小数点以下を切り捨てて表示する）

```php
<?php
	fscanf(STDIN, "%d %f", $a, $b);
	echo bcmul($a,$b);
?>
```
なんてシンプルなの・・・泣

##おわりに
一応浮動小数点数についても調べたのですが、全然理解できず・・・
ひとまず今回は

- 少数の計算は誤差が出る可能性があるので気を付ける
- intdiv()
- bcmul()

の３つについて勉強できました。

復習は大切。。。。
再帰クエリで木構造のデータを取得する

##はじめに
フォルダ機能を作る際にSQLserverで再帰クエリを書いたので、メモとして残しておきたいと思います。


##再帰関数
関数の中で自分自身を呼び出すものを再帰関数といいます。
無駄に多用するとコードが見づらくなりますが、何重になるかわからない時などループでやりづらい場合に再帰関数を使うと簡単に書けたりします。

```php5
function funcA(n){
	if(n == 0){
		return 0;
	}else{
		return n + funcA(n-1);
	} 
}
```

##木構造のデータ
以下のような構造のデータを木構造と言います。
![木構造.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370171/6f8f6732-cd4c-fb0f-74a3-cb5d5f6e3585.png)
以下のように各ノードが親のノードを持つだけのデータ構造を隣接リストモデルといいます。

| parentid         | id               |
|:-----------------|:-----------------|
| 0                | 1                |
| 1                | 2                |
| 1                | 3                |
| 2                | 4                |
| 2                | 5                |

他には経路列挙モデル、入れ子モデル、閉包テーブルモデルなどがあります。
隣接リストモデルはシンプルですが、RDBMSが再帰クエリに対応していない場合は子孫ノードを取得しづらいなどの問題があり、アンチパターンとなります。
SQLserverは再帰クエリに対応しているので大丈夫です。

##WITH句を使って再帰クエリを書く
#####１.id：２よりも上の階層の情報を取得する場合
→取得できるid １、２

```sql
WITH r AS (
--起点となるselect文
SELECT
*
FROM
tbl1
WHERE
tbl1.id = ２

UNION ALL

--再帰
SELECT
*
FROM
tbl2 AS r
WHERE
tbl2.id = r.parentid
)

SELECT
*
FROM
r
```

#####２.id：２よりも下の階層の情報を取得する場合
→取得できるid ２、４、５

```sql
WITH r AS (
--起点となるselect文
SELECT
*
FROM
tbl1
WHERE
tbl1.id = ２

UNION ALL

--再帰
SELECT
*
FROM
tbl2 AS r
WHERE
--ここのidとparentidが逆になる
tbl2.parentid = r.id
)

SELECT
*
FROM
r
```

##おわりに
SQLアンチパターンという本があり、その中の2章 Naive Trees(素朴な木)をまとめてある[こちら](https://qiita.com/hirashunshun/items/06adf4f42f03a9f3b63d)の記事が設計の時に参考になりました。
自分も読んでみたいと思います。
phpでzip圧縮、パスワード付与、メール送信

## zip圧縮

```php
function zip($zipname, $file, $password) {
        $zip = new ZipArchive;
        $zip->open($zipname , ZipArchive::CREATE|ZipArchive::OVERWRITE);
        // パスワード設定
        $zip->setPassword($password);
        $zip->addFile($file);
        $zip->setEncryptionName($file, ZipArchive::EM_AES_256);
        $zip->close();
}

zip("a.zip", "sample.txt", "uoGhie6s");
```


## メール送信

```php
function sendMail( $to=null, $subject=null, $text=null, $file=null){

        //初期化
        $res = false;

        //日本語の使用宣言
        mb_language("ja");
        mb_internal_encoding("UTF-8");

        if( $to === null || $subject === null || $text === null ) {
                return false;
        }

        // 送信元の設定
        $sender_email = 'from@localhost';

        // ヘッダー設定
        $header = '';
        $header .= "From: ".$sender_email."\n";
        $header .= "Content-Type: multipart/mixed;boundary=\"__BOUNDARY__\"\n";

        // テキストメッセージを記述
        $body = "--__BOUNDARY__\n";
        $body .= "Content-Type: text/plain; charset=\"ISO-2022-JP\"\n\n";
        $body .= $text . "\n";
        $body .= "--__BOUNDARY__\n";

        // ファイルを添付
        $body .= "Content-Type: application/octet-stream; name=\"{$file}\"\n";
        $body .= "Content-Disposition: attachment; filename=\"{$file}\"\n";
        $body .= "Content-Transfer-Encoding: base64\n";
        $body .= "\n";
        $body .= chunk_split(base64_encode(file_get_contents($file)));
        $body .= "--__BOUNDARY__--";

        //メール送信
        $res = mb_send_mail( $to, $subject, $body, $header);

        return $res;
}

$result = sendMail("to@localhost", "subject", "bodyです", "a.zip");
```
subprocessでPATHが必要な外部プログラムを動かす時，実行エラー

# 結論
PATHが通ってないと動かない様なプログラムをsubprocessから実行するにはshell=Trueが必要?

# やりたかったこと
pythonからfortranのプログラムを動かしたかった．
具体的にはこんな感じのものを副次的に呼び出す．
```subprocess.run('./hoge.out')```

# 動かしてみると
- 正常に動作してくれない...
- 出力を見てみるとifort(コンパイラ)がfile not foundうんたらかんたら
- PATHが通ってない時によくみる症状
- シェルから動かすと普通に動くのでPATHは通っているはず...
- 試しに副次的じゃなく直接叩くpythonプログラムを書いてみても動いた...

# 原因
```subprocess.run(['echo' '$PATH'])```
とすると
```$PATH```
とそのまま返ってきて何も参照していないのが原因？
調べてみるとこんな記述が...
>「PATHを参照したかったらshell=Trueにしろよ!」
>- [stackoverrunより](https://stackoverrun.com/ja/q/11729859)

>「でもシェルインジェクションの危険もあるから気を付けろよ！」
>- [pythonドキュメントより](https://docs.python.org/ja/3/library/subprocess.html#security-considerations)

とのこと．
```subprocess.run('echo $PATH', shell=True)```
とすると無事にPATHが帰ってきた！
```subprocess.run('./hoge.out', shell=True)```
もちゃんと動作する様になりました！
bqコマンドでschema_update_optionを指定すると、カラム追加の運用が楽な話

# サマリ
- schema_update_option に `ALLOW_FIELD_ADDITION` を指定すると、 カラムが追加された場合でも、別途tableのschema update作業をしなくても良いという話。
- 日付別パーティショニングテーブルに、日次でCSVデータを投入していくようなシステムを想定している。
- 公式ドキュメントの記載は[こちら](https://cloud.google.com/bigquery/docs/managing-table-schemas?hl=ja)
- 今回BQコマンドでやっているが、当然APIとして提供されているので、各種SDKを使ったLoadでも同じことができます（Embulkとかでも）
    - Embulkの例は[こちら](https://qiita.com/shase428q/items/bf47d678e72058d66490)

# 実験した環境

テーブル名

- foobar

テーブル作成コマンド

```bash
bq mk --table --require_partition_filter --time_partitioning_type=DAY 'project_id:dataset_id.foobar'
```


# Day 1

通常以下のようにLoadされているとします。

テーブルスキーマ

```foobar.schema1.json
[
  {
    "mode": "NULLABLE",
    "name": "a",
    "type": "STRING"
  },
  {
    "mode": "NULLABLE",
    "name": "b",
    "type": "INTEGER"
  }
]
```

投入データ

```foobar1.csv

"a","b"
"abc",1
"xyz",9
```

Loadコマンド

```bash
bq load \
--source_format=CSV --skip_leading_rows 1 \
'project_id:dataset_id.foobar$20200610' ./foobar1.csv ./foobar.schema1.json
```

# 途中からカラムが増える

途中から読み込むCSVのカラムが増えたと想定します。
この場合、

- schemaファイルの変更
- bq update（table schemaのupdate）

の2つが必要に思いますが、schema_update_option に `ALLOW_FIELD_ADDITION` を指定することによって、  `bq update（table schemaのupdate）` が不要になります。

# Day 2

テーブルスキーマ

- cというカラムが追加されている

```foobar.schema2.json
[
  {
    "mode": "NULLABLE",
    "name": "a",
    "type": "STRING"
  },
  {
    "mode": "NULLABLE",
    "name": "b",
    "type": "INTEGER"
  },
  {
    "mode": "NULLABLE",
    "name": "c",
    "type": "STRING"
  }
]
```

投入データ

- cというカラムが追加されている

```foobar2.csv
"a","b","c"
"abc",1,"zzz"
"xyz",9,"zzz"
```

Loadコマンド

- 新スキーマを指定している

```bash
bq load \
--source_format=CSV --skip_leading_rows 1 \
--schema_update_option ALLOW_FIELD_ADDITION \
'project_id:dataset_id.foobar$20200611' ./foobar2.csv ./foobar.schema2.json
```

load結果

<img width="147" alt="スクリーンショット.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147174/74c0a2f0-53e3-ca5a-2074-1ec41ba54dea.png">

期待したとおり、Day2で増えたカラムに、過去分にはnullが入っています。

# 再実行時の注意点

rerunする場合、loadする前にパーティションを削除しないと2重にデータが入ってしまうので注意

再実行前にパーティションを消す

```bash
bq rm --force 'project_id:dataset_id.foobar$20200611'
```

と、これを書いたとに、 `--replace` オプションで同様のことができることがわかりました。Day2のクエリに `--replace` をつけるだけで良さそうです。

```bash
bq load \
--replace \
--source_format=CSV --skip_leading_rows 1 \
--schema_update_option ALLOW_FIELD_ADDITION \
'project_id:dataset_id.foobar$20200611' ./foobar2.csv ./foobar.schema2.json
```

# まとめ
というわけで、実際の運用上は、日次で以下のようなbqコマンドを回せばやりたいことができそうです。

```bash
export LOAD_FILE="./foobar2.csv"
export TARGET_TABLE="'project_id:dataset_id.foobar$20200611'"
export SCHEMA="./foobar.schema2.json"
bq load \
--replace \
--source_format=CSV --skip_leading_rows 1 \
--schema_update_option ALLOW_FIELD_ADDITION \
${TARGET_TABLE} ${LOAD_FILE} ${SCHEMA}
```
予約受付システム各社比較

#まえがき
イベントを開催することになり、クラウドの予約受付システムの導入を検討。
やりたいことはごくシンプルで以下の内容。
・時間帯を区切ってイベントを作成
・それぞれのイベントに人数制限をかける
・申込時に氏名／e-mailアドレス／人数を入力
・受付確認メールを自動送信
この条件で各社サービスの検討を実施。

#各社の比較
##AirRESERVE
提供会社：リクルート

機能　　　　　　：★★★★
価格　　　　　　：★★★★★
ユーザビリティー：★

無料枠で要件をすべて網羅しているが、予約受付時のユーザビリティに難あり。
・不要な項目が消せない
・「カレンダーをクリックして予約」という操作がわからず予約をあきらめてしまう恐れがある
<img width="400" alt="AirRESERVE.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317488/91f7a9f4-9874-623e-966a-5f1721782f90.png">
問い合わせを行ったが、有料版にしてもこの問題は解決しないとのこと。

##RESERVEN
提供会社：ウェブタ

機能　　　　　　：★★★
価格　　　　　　：★★★★
ユーザビリティー：★★

広告収入モデルの運営を行っているので価格は安い。
管理画面にまで広告が出る。
最上位の有料プラン(2,980円/月)であれば広告が消えるらしい。
動作がもっさり。
システム会社目線でのデザイン、ユーザビリティー。
<img width="400" alt="RESERVEN.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317488/e0ed2abd-d405-0894-0422-c42cbd7e7115.png">

##RESERVA
提供会社：コントロールテクノロジー

機能　　　　　　：★★★★
価格　　　　　　：★★
ユーザビリティー：★★★★

機能は豊富だが、予約枠を多くとりたい場合だと価格が非常に高い。
予約画面は一覧形式で一目で使い方がわかる。
が、予約画面に入る前に人数選択をしないといけないのがやさしくない。
メニュー画面に説明を入れれない、予約受付完了画面の出力がカスタマイズできないなどの欠点あり。

<img width="400" alt="RESERVA.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317488/1e89c378-cc8c-3e79-6444-90ef2feb2822.png">

#さいごに
やりたいことは単純なのに、どこも微妙におしい。
LaravelでのUnitテストで1つのメソッドだけ実行する

- Windows 10
- PHP 7.4.5
- Laravel Framework 7.9.2

開発中（コード実装中）にUnitTestを使って試行錯誤している場面など、テストケース全部ではなく実装中のこのテストだけ実行したいなと思った時の対応方法となります。

## 実装中のテストケースに印（アノテーション）を付ける

**`@group`**アノテーションで実行中のメソッドに印をつける。
今回はtestingという名前にしました。

```php:SampleUnitTest.php
    /**
     * @test
     * @group testing
     */
    public function create_success()
    {
        $response = $this->get('/api/card/create');
        $response->assertStatus(200);
    }
```

## 実行時にグループを指定する

```powershell
php .\artisan test --group=testing
```

もしくは

```powershell
.\vendor\bin\phpunit --group=testing 
```

実装が終わったらアノテーションを削除するのを忘れずに
$.ajax(jQuery.ajax)の非同期処理をasync awaitの入れ子なし同期処理で記述する

# 目的
`$.ajax`を`async` `await`を用いた同期処理で書く。
Ajax通信を待ってから処理に進む。
同期処理が入れ子にならず、クリーンに書くことができる。
IEには対応していない。注意。

# 経緯
保守の際、既存コードがjqueryのajaxで記述されていた。
jquery自体は別に良い。
だが、大量のコールバックによる入れ子で非常に見通しが悪くなっていた。
それを`async` `await`を用いた同期処理にリファクタリングをした際の備忘録である。

# 書き方
フルーツID🔑からフルーツ名🍒を取得する。

```javascript:hoge.js
/**
 * フルーツ名を取得する
 *
 * @param {string} fruitId
 */
async function getFruitName(fruitId) {
  const fruitRequest = {id: fruitId}
  const fruitResult = await ajaxGetFruit(fruitRequest);
  return fruitResult.name;
}

/**
 * フルーツのajax[GET]を実施する
 * 
 * @param {object} request
 * @returns
 */
function ajaxGetFruit(request) {
  return new Promise((resolve, reject) => {
    $.ajax({
      url: '/fruit/name',
      type: "GET",
      async: true,
      contentType: "application/json",
      data: JSON.stringify(request),
      dataType: "json",
    }).then(
      function (result) {
        // 正常終了
        resolve(result);
      },
      function () {
        // エラー
        reject();
      }
    )
  }
  )
}
```

# かんたんな解説
### 記述場所
- `async`は関数定義に記述する。
- `await`は呼び出す関数の前に記述する。
- IEには対応していない。注意。

### 動き
- `await`は`async`で定義された関数内でしか使用できない。
- `await`で呼び出しされた関数がresolve、またはrejectするまで待つ。
IBActionのsenderはAnyでなく具体的な型を指定しよう(Swift)

`UIButton` タップ時の処理などを `IBAction` で紐付けるとき、 型は `Any` がデフォルトなので、そのままにする人も多いのではないでしょうか。
<img width="292" alt="スクリーンショット_2020-06-16_11_39_03.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/363f2d56-49fa-e9e8-e052-3811330e0c6d.jpeg">

```swift:×
final class FooViewController: UIViewController {
    @IBAction private func didTapCloseButton(_ sender: Any) {
    }
}
```

それだとキャストしないと型に応じた処理を実行できないので、具体的な型を選択して紐付けるのが望ましいです。
<img width="296" alt="スクリーンショット_2020-06-16_11_43_16.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/89cd82d5-2750-f229-f012-be3643aa75e4.jpeg">

```diff_swift:○senderを具体的な型にすると、キャストせずに型に応じた処理を実行できる
final class FooViewController: UIViewController {
-     @IBAction private func didTapCloseButton(_ sender: Any) {
+     @IBAction private func didTapCloseButton(_ sender: UIButton) {
    }
}
```

デフォルトが `Any` なので、それを選択したほうがいい理由もあるのかと思っていたのですが、どうやらObjective-Cの仕様なだけのようなので、 `Any` を選択するメリットはなさそうです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">具体的な型入れて方がsenderに応じた処理書きやすいです；Anyになってるのは単純にObj-Cのselectorの仕様なだけです</p>&mdash; 星野恵瑠#今日も1日フレンズ㌠ (@lovee) <a href="https://twitter.com/lovee/status/1272711756733837314?ref_src=twsrc%5Etfw">June 16, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## おまけ1: そもそも `sender` とは？

「send + er」で、一言でいうと「対象アクションのトリガーとなったUI」です。

例えば上記の `@IBAction private func didTapCloseButton(_ sender: UIButton)` だと、 `sender` にはタップされたボタン（メソッド名から「閉じる」ボタンだと推測できる）が格納されています。

メソッド名は自由に付けられますが、私はわかりやすいように、ボタンのタップなら `didTap○○Button(_:)` と命名しています。

## おまけ2: 他の考え方

Twitterなどを見ていると、他にもいろいろな考えを持っている方がいたので紹介します。

- `sender` を使わないときは特に意識せず（ `Any` のまま）、使うときのみ具体的な型に変更する
- `sender` を使わないときは引数ごと削除し、使うときに引数を追加する

使うときに具体的な型を指定するのを忘れなければ、どちらの考えもいいと思います。
ただ、 `IBAction` の仕様がわからなくて `sender` を `Any` のまま使われるのを避けるため、私は常に具体的な型の `sender` を引数に持たせるのがいいと考えています。

具体的な型の `sender` があると、どのUIに紐付いたアクションなのかコードから読み取りやすくなるのもあります。

```swift:○senderを使わなくても具体的な型にしておくと、どのUIに紐付いているかわかりやすくなる
final class FooViewController: UIViewController {
    // 「これは「閉じる」ボタンタップ時の処理だな！」と `sender` の型とメソッド名からパッと判断できる
    @IBAction private func didTapCloseButton(_ sender: UIButton) {
    }
}
```
metaflowでパイプラインのデバッグ

#Metaflow
[Metaflow | https://metaflow.org/](https://metaflow.org/)
<img width="1667" alt="スクリーンショット 2020-06-16 11.32.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620435/82d4ceb0-0114-507b-9a36-85859e3ab02a.png">

#パイプラインの実装

```python:train.py

from metaflow import FlowSpec, step, Parameter

class TrainingPipeline(FlowSpec):
    param_config_str = Parameter('config',
                             help='Training config json str.',
                             default='{}')

    @step
    def start(self):
        self.config = json.loads(self.param_config_str)
        self.a = 0
        self.next(self.step1)

    @step
    def step1(self):
        self.a = 1
        self.next(self.step2)

    @step
    def step2(self):
        self.a = 2
        self.next(self.end)

   @step
    def end(self):
        pass

if __name__ == '__main__':
    TrainingPipeline()
```

###実行
```bash
python train.py
```

#デバッグ
実行すると、実行フォルダ内に「.metaflow」ディレクトリが生成されているのがわかります。
.metaflowディレクトリがある階層に以下のスクリプトを用意します。

```python:debug.py
from metaflow import Flow, namespace, Step

namespace(None)
data_start = Step('TrainingPipeline/[RUN_ID]/start').task.data
print('Step start : a -> {}'.format(data_start.a))

data1 = Step('TrainingPipeline/[RUN_ID]/step1').task.data
print('Step step1 : a -> {}'.format(data1.a))

data2 = Step('TrainingPipeline/[RUN_ID]/step2').task.data
print('Step step2 : a -> {}'.format(data2.a))
```

###実行
```bash
python debug.py
```

###結果
```
Step start : a -> 0
Step step1 : a -> 1
Step step2 : a -> 2
```

ちなみにDataFrameなどもちゃんと保存しておけます。
競技プログラミング練習記 No.12【ABC145練習】



初回 -> [はじめに](https://qiita.com/easychoco/items/a9ae28aca97cfe47091f)

[ABC145](https://atcoder.jp/contests/abc145)を解きました。
4完です。

|問題|時間|アルゴリズム|実行時間
|:-:|:-:|:-:|:-:|
| A |  1min |-|1ms|
| B |  2min |-|1ms|
| C | 30min |全探索|2ms|
| D | 60min |組み合わせ|11ms|

## A
```c++
	int r;
	cin >> r;
	cout << r * r;
```
入力値の2乗が答えですね。

## B
``` c++
	int n;
	string s;
	cin >> n >> s;

	string sub = s.substr(s.length() / 2);
	cout << (s == sub + sub ? "Yes" : "No");
```
やるだけ。

## C
``` c++
int n;
double dist[8][8];

double dfs(int visited, int now, double total_dist)
{
	if (visited == ((1 << n) - 1))
	{
		return total_dist;
	}

	double ret = 0.0;
	for(int i = 0; i < n; ++i)
	{
		if((visited & (1 << i)) == 0)
		{
			ret += dfs(visited | (1 << i), i, total_dist + dist[now][i]);
		}
	}

	return ret;
}

void solve()
{
	cin >> n;
	vector<int> x(n);
	vector<int> y(n);
	for(int i = 0; i < n; ++i)
	{
		cin >> x[i] >> y[i];
	}

	for(int i = 0; i < n; ++i)
	{
		for(int j = i; j < n; ++j)
		{
			dist[i][j] = dist[j][i] = sqrt(pow(x[i] - x[j], 2) + pow(y[i] - y[j], 2));
		}
	}

	double ans = 0.0;
	for(int i = 0; i < n; ++i)
	{
		ans += dfs(1 << i, i, 0);
	}
	double factor = 1.0;
	for(int i = 1; i <= n; ++i)
	{
		factor *= i;
	}
	printf("%.7f\n", ans / factor);
}
```
全通りの組み合わせの列挙に時間がかかりました。
TSP（巡回セールスマン問題）かと思って必要以上にしっかり考えてしまった。反省。


## D


``` c++
// a^n % mod を計算する O(log n)
ll powmod(ll a, ll n, ll mod) {
	ll ret = 1;
	while (n > 0) {
		if (n % 2 == 1) { ret = ret * a % mod; }
		a = a * a % mod;
		n /= 2;
	}
	return ret;
}

// nCr % mod を計算する O(r)
ll combmod(ll n, ll r, ll mod) {
	ll x = 1, y = 1;
	r = min(n - r, r);
	for(ll i = 0; i < r; ++i) { x = x * (n - i) % mod; }	// 分子
	for(ll i = 1; i <= r; ++i) { y = y * i % mod; }			// 分母
	return x * powmod(y, mod - 2, mod) % mod;	// nCr = (分子) * (分母)^-1 mod M
}

void solve()
{
	ll x, y;
	cin >> x >> y;
	
	if ((x + y) % 3 != 0)
	{
		cout << 0;
		return;
	}

	if (x > 2 * y || y > 2 * x)
	{
		cout << 0;
		return;
	}

	ll a = (2 * y - x) / 3LL;
	ll b = (2 * x - y) / 3LL;

	cout << combmod(a + b, min(a, b), MOD);
}
```
組み合わせの問題です。
`(x+1, y+2)`への移動をA
`(x+2, y+1)`への移動をB
として、Aを`a`回、Bを`b`回やります。
全体で`a+b`回の移動があり、AとBをそれぞれどの順番でやるかが答えになります。
つまり、以下の式ですね。

```math
\begin{eqnarray*}
  && {}_{a+b} \mathrm{C} _a
\end{eqnarray*}
```
`a,b,x,y`については、手元で試していると以下の連立方程式が出てきました。
x座標に関する式とy座標に関する式ですね。

```math
\left\{
\begin{array}{ll}
x = a + 2b \\
y = 2a + b
\end{array}
\right.
```
これを`a`と`b`について解いて
`a = (2y - x) / 3`
`b = (2x - y) / 3`
となるので、combinationを計算して答えが出ます。

ちなみに、
`powmod`関数は高速な累乗の計算。
`combmod`関数はフェルマーの小定理による組み合わせ計算です。


以上です。ありがとうございました。
C++からFortranでHello Worldする

# はじめに
普段はC++でコードを書いていますが、Fortranの遺産も使いたくなったので入門してみました。

# 環境
- OS: Mac OS X Mojave 10.14.6
- gfortran: Homebrew GCC 9.3.0_1
- g++(g++-9): Homebrew GCC 9.3.0_1 

# コンパイラのインストール
MacでHomebrewを使用する場合、下記コマンドで一発です。

```bash
$ brew install gcc
```
Windowsの場合、MinGWなどを使用すると良いでしょう。

# サンプルコード
```c++:main.cpp
#include <iostream>

extern "C" {
	void hello();
}

int main(int argc, char *argv[]) {
	hello();
	return 0;
}
```

```fortran:hello.f90
subroutine hello() bind(c)
  implicit none
  print *, 'Hello World!'
end subroutine
```

ポイント 

- hello関数をFortranのサブルーチンとして作成します。
- extern "C"することで、C言語とFortranの連携を使います。

# コンパイル
```bash
$ gfortran -c hello.f90
$ g++ -lgfortran main.cpp hello.o
$ ./a.out
 Hello World!
```
Macの場合、付属のclangではなくgccを使います。
g++をg++-9などで置き換えることで使用できるはずです。
数字はバージョンに応じて変えてください。
-lgfortranをつけることを忘れずに。
なお、WindowsでMinGWを使用した場合、この方法だとうまくいきませんでした。
一度dllを経由するのが良いようです。

```shell:Windows用
$ gfortran --shared -o hello.dll hello.f90
$ g++ -L ./ main.cpp -lhello
$ a.exe
 Hello World!
```
## CMakeを使用してのコンパイル
環境に応じて書き換えるのも面倒なので、CMakeに任せてみました。こちらもMacの場合インストールにHomebrewが使用できます。

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
enable_language(Fortran)
project(hello_world CXX Fortran)

add_executable(
    a.out
    hello.f90 
    main.cpp
)
```
ポイント

- enable_languageでFortranを有効にします。
- 使用言語にCXXとFortranを指定します。

### ビルド
```bash
$ mkdir build
$ cd build
$ cmake ..
$ make
$ ./a.out
 Hello World!
```
ビルド用のディレクトリを作ってCMakeにMakefileを作ってもらい、makeします。
Windowsの場合は、cmakeの際に -G "Unix Makefiles" などのオプションを付けてください。
付けないとVisual Studio用のファイルを作ろうとして詰まります。

# 終わりに
FortranのサブルーチンをC++から呼び出すことができました。
C言語とFortranの連携をC++から使用しているだけなので、C言語との連携について調べることでより高度な連携も可能なはずです。

# 参考文献
- [C から Fortran を呼び出してみる - 雑食性雑感雑記](https://kazuki-nagasawa.hatenablog.com/entry/memo_20140715_c_fortran)
- [CMakeによるFortranビルドの省力化 - Qiita](https://qiita.com/ijknabla/items/05270ae5e597705d0dae)
- [gcc（gfortran） で C から Fortran77 のサブルーチンを呼ぶ - potass' blog](https://pota.hatenablog.jp/entry/2015/02/21/193857)
Mithril.js + webpack + mitts で ファイル分割、動的ロード。 code splitting!

# Mithril.js + webpack + mitts で ファイル分割、動的ロード。 code splitting!

SPA (Single Page Application) では ページ数が増えると 最終的なファイルサイズもどんどん増えていきます。
出来上がった アプリケーションを最初にロードさせるときにファイルサイズが大きくて、使う人たちが イライラするのはよろしくありません。

[Mithril.js](https://mithril.js.org/) + [webpack](https://webpack.js.org/) + [mitts](https://github.com/bmartel/mitts) を使って必要なときに必要なぺージを動的に読み込むようにしてみましょう。


以下の例は、`Page/Page1` は静的に読み込むページですが `Page/Page2` は実際に必要になった時に動的に読み込まれます。


```tsx
import m from 'mithril';
import stream from 'mithril/stream';
import mitts from "mitts";

import Page_Index from "Page/Index";
import Page_Page1 from 'Page/Page1';		// 静的にロードされるページ

// ロード開始～完了までの間に一時的に表示されるページ
const Loading = {
	view(vnode) {
		const { error, retry, pastDelay } = vnode.attrs;
		if (error) {
			return <div>Error! <button onclick={ retry }>Retry</button></div>
		} else if (pastDelay) {
			return <div>Loading...</div>;
		} else {
			return null;
		}
	}
};

// 動的にロードされるページ
const Loadable_Page2 = mitts({
	loader: async ()=>{
		const module = await import(/* webpackChunkName: "Page2" */ 'Page/Page2'); // !!!
		return new module.default();
	},
	loading: Page_Loading,
	m : m,
	stream : stream,
});

m.route(document.body, '/', {
	'/' : { render: ()=>{ return <Page_Index />; } } ,
	'/page1' : { render: ()=>{ return <Page_Page1 />; } } ,
	'/page2' : { render: ()=>{ return <Loadable_Page2 />; } } ,
}

```

## Webpack の webpackChunkName のコツ

`import(/* webpackChunkName: "Page2" */ 'Page/Page2')` の部分ですが、import() での引数のファイルパスは Webpack でのトランスパイル後のファイルパスではなりません。
そもそも、Webpack で1つのファイルにまとめられてしまえば意味が無い。だから分割  code splitting する。
仮に code splitting で分割できたとしてもトランスパイル後の実際のファイル名は分からない。
この悩みを解決するために webpackChunkName を利用しています。

こうすれば Webpack で 1つのファイルにまとめられずに、 `Page/Page2` だけは単体で Webpack され `dist/Page2.bundle.js` のように出力されます。
同時に パスは `/* webpackChunkName: "Page2" */ 'Page/Page2'` の部分は 実際のパス `dist/Page2.bundle.js` 等に Webpack により置換されます。

## tsconfig.json では removeComments : false を忘れずに

TypeScript を使用しているときに `tsconfig.json` では `"removeComments" : false` にしておく必要が有ります。
そうしないと `/* webpackChunkName: ... */` などのパラメータが TypeScript が除去してしまい、Webpack に渡らないから、それを防ぐ必要があります。

```tsconfig.json
{
	"compilerOptions": {
		"removeComments" : false,
	}
}
```


<hr />

環境

- "mithril": "2.0.4"
- "webpack": "4.26.0"
- "typescript": "4.26.0"
[Python] Matplotlibで３Dグラフのlimit axis

Matplotlib で３Dグラフを作成するとき、グラフの軸のサイズを設定しつつ軸の比率を統一したいが ```plt.axes().set_aspect('equal')``` のような関数が 3D plot には存在しないので、その解決方法を。
（軌道のプロットなどで便利）

例えば、このプロットについて

```Python
# plot in 3D of candidate branches
fig = plt.figure(figsize=(12,10))
plt.rcParams["font.size"] = 20
ax = fig.gca(projection='3d')
# mass
ax.plot_wireframe(xperilune_m2, yperilune_m2, zperilune_m2, color="k", label='m2', alpha=0.1)
# LPO trajectory
ax.plot(dynmatLPO["x_arr"]*Lstar, dynmatLPO["y_arr"]*Lstar, dynmatLPO["z_arr"]*Lstar, c='m', linewidth='1.5', label='Halo')
```

軸のサイズを固めたいとする。$x$軸を$320000$から$470000$、$y$軸を$-60000$から$60000$、$z$軸を$-1000$から$1000$で止めたい場合、これらの中から最大のrangeをもつ範囲を ```max=range``` として計算し、これをもとにボックスを作成している。

```Python
def set_equal_axis(ax, xlims, ylims, zlims):
    """Helper function to set equal axis
    
    Args:
        ax (Axes3DSubplot): matplotlib 3D axis, created by `ax = fig.add_subplot(projection='3d')`
        xlims (np.array): array containing min and max value of x
        ylims (np.array): array containing min and max value of y
        zlims (np.array): array containing min and max value of z
    """
    # compute max required range
    max_range = np.array([xlims.max()-xlims.min(), ylims.max()-ylims.min(), zlims.max()-zlims.min()]).max() / 2.0
    # compute mid-point along each axis
    mid_x = (xlims.max() + xlims.min()) * 0.5
    mid_y = (ylims.max() + ylims.min()) * 0.5
    mid_z = (zlims.max() + zlims.min()) * 0.5
    # set limits to axis
    ax.set_xlim(mid_x - max_range, mid_x + max_range)
    ax.set_ylim(mid_y - max_range, mid_y + max_range)
    ax.set_zlim(mid_z - max_range, mid_z + max_range)
    return


# create plot
fig = plt.figure(figsize=(12,10))
plt.rcParams["font.size"] = 20
ax = fig.gca(projection='3d')

# mass
ax.plot_wireframe(xperilune_m2, yperilune_m2, zperilune_m2, color="k", label='m2', alpha=0.1)
# LPO trajectory
ax.plot(dynmatLPO["x_arr"]*Lstar, dynmatLPO["y_arr"]*Lstar, dynmatLPO["z_arr"]*Lstar, c='m', linewidth='1.5', label='Halo')

# set array for max/min boxing
xlims = np.array([320000, 470000])
ylims = np.array( [-60000, 60000])
zlims = np.array([-1000, 1000])
set_equal_axis(ax, xlims, ylims, zlims)

ax.grid()
plt.title(f'Targeting from {num_branch} manifolds')
ax.legend(loc='best')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
```

参考：
https://matplotlib.org/3.2.1/api/_as_gen/mpl_toolkits.mplot3d.axes3d.Axes3D.html
https://stackoverflow.com/questions/30196503/2d-plots-are-not-sitting-flush-against-3d-axis-walls-in-python-mplot3d/41779162#41779162
BootstrapをSassで編集するための手順

#はじめに
簡単そうで意外と面倒だったSassの使い方を備忘録として書いておきます。
※この記事は既にBootstrapが導入されていて、これからSassを使ってレイアウト編集することを想定しています。

Sassを使おうと思ったきっかけは[現場で使える Ruby on Rails 5速習実践ガイド](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4%E5%AF%A7%E5%AD%90/dp/4839962227)を使ってタスク管理アプリを作成中に細かいレイアウトを編集したいなと思ったのですが、参考書ではBootstrapだけを使った装飾しか使われていなかったためです。

そもそもSassって何？という人は以下のリンクを参照
[これからはcssはSassで書こう。](https://qiita.com/m0nch1/items/b01c966dd2273e3f6abe)
[Bootstrap 4を使うならSassを使って3倍幸せになろう](https://qiita.com/tonkotsuboy_com/items/1855734522bfe7ef7dad)

#Sassの使い方(簡易版)
とりあえずレイアウト編集できればいい！という方は`application.scss`に編集内容を記述すればviewに反映されます。

ただおすすめはしません。

理由は以下のような問題があるためです。
- どの`view`ファイルに対してどのscssを適用させているのかわからない。
- `application.scss`ファイルが必要以上に肥大化する。
- 保守性が著しく低下する。（特にチーム開発の場合）

例）`app/views/session.html.slim`中の`session_form`クラスと
　　`app/views/post.html.slim`中の`post_form`クラスと
　　`app/views/user.html.slim`中の`user_form`クラスに
　　各々の`padding`を適用させたい場合

```app/assets/stylesheets/application.scss
@import 'bootstrap';

.session_form {
  padding: 10px 100px;
}
.post_form {
  padding: 20px 200px;
}
.user_form {
  padding: 30px 300px;
}
```

#Sassの使い方(viewファイルごとに管理したい場合）
管理しやすい使い方として`view`ファイルごとに`scss`ファイルを用意し`application.scss`に読み込ませることをおすすめします。

手順は
１）`scss`ファイルを入れるためのディレクトリ（ここではpartialディレクトリ）を用意する。
２）`application.scss`ファイルにpartialディレクトリを読み込ませるため`@import 'partial';`を記述する。
3)partialディレクトリ内に`_(アンダーバー)`から始まるscssファイルを作成し編集内容を記述する。
４）partialディレクトリ内に`_index.scss`ファイルを作成し３）で作成したファイル名から`_`を取り除いた形式で記述する。
**ここで大事なことが2点あります。
1点目はアンダーバーを除いた形式で記述しないと`scss`ファイルが読み込まれないことがあるため要注意。
2点目は`_index.scss`ファイルがないと`application.scss`ファイルがpartialディレクトリを読み込んでくれません。**

例）
１）`scss`ファイルを入れるためのディレクトリ（ここではpartialディレクトリ）を用意する。
２）`application.scss`ファイルにpartialディレクトリを読み込ませるため`@import 'partial';`を記述する。

```app/assets/stylesheets/application.scss
@import 'bootstrap';
@import 'partial';
```

3)partialディレクトリ内に`_(アンダーバー)`から始まるscssファイルを作成し編集内容を記述する。

```app/assets/stylesheets/partial/_sessions.scss
.login_form {
  padding: 10px 100px;
}
```
```app/assets/stylesheets/partial/_posts.scss
.post_form {
  padding: 20px 200px;
}
```
```app/assets/stylesheets/partial/_users.scss
.user_form {
  padding: 30px 300px;
}
```

４）partialディレクトリ内に`_index.scss`ファイルを作成し３）で作成したファイル名から`_`を取り除いた形式で記述する。
**ここで大事なことが2点あります。
1点目はアンダーバーを除いた形式で記述しないと`scss`ファイルが読み込まれないことがあるため要注意。
2点目は`_index.scss`ファイルがないと`application.scss`ファイルがpartialディレクトリを読み込んでくれません。**

```app/assets/stylesheets/partial/_index.scss
// _は記述しない!`_sessions`はだめ。
@import 'sessions', 'posts', 'users';
```

#参照
わからないことがあったら公式ドキュメントを読むのが一番手っ取り早いです
[Sass公式](https://sass-lang.com/guide)
[ruby-sass](https://github.com/sass/ruby-sass)
QGIS3.10 インストール on Windows10

## はじめに
GISのフリーソフトの中でも高機能と言われている **Quantum GIS（QGIS）** について、基本的な操作方法などをまとめていきたいと思います。
今回はPCへのインストール手順をまとめました。


## インストール環境
Windows 10 Pro


## インストール手順
####下記のサイトより、インストーラをダウンロード。
　 https://www.qgis.org/ja/site/forusers/download.html
![Qiita-no001_img01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/68bd233d-32cb-bdec-4a16-6acc06439af9.jpeg)
　※今回は最新版ではなく、長期リリースされているver3.10をインストール。（自分の場合、ダウンロードに３分くらいかかった。）

#### インストール実行

**ダウンロードした.exeファイルを管理者権限で実行。**
**「セットアップウィザードへようこそ」の画面が出てくるので、『次へ』をクリック。**
![Qiita-no001_img02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/6f97128d-6f64-4e43-dd38-13049c4884db.jpeg)


**ライセンス契約書の内容を確認して、『同意する』をクリック。**
![Qiita-no001_img03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/d3363be8-2c40-6626-7372-69f26f0b8e54.jpeg)


**インストール先を選んで『次へ』をクリック。**
![Qiita-no001_img04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/297ea372-db0f-7d54-829d-3bb4c6fcb2bb.jpeg)
※特にドライブ分けなどはしていないので、デフォルトのProgramFileにインストール。


**インストールコンポーネントを選択して『インストール』をクリック**
![Qiita-no001_img05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/01eb0171-3486-2c36-0b95-48eeb2a66ad9.jpeg)
※DataSetのコンポーネントをインストールすると時間がかかるらしいので、メインのQGISのみインストール。


**以下の画面が表示されればインストール完了！**
![Qiita-no001_img06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/bd1e2946-67d4-026d-881b-b89b005d260b.jpeg)


## 起動確認
**デスクトップに『QGIS 3.10』フォルダができているので、その中の『QGIS Desktop 3.10.6』をクリック。**（スタートメニューに追加されているので、そこから起動もできる。）
![Qiita-no001_img07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/50371736-faed-afe5-eac9-e8adf8999973.jpeg)


**初回の起動は以下の画面が出るため『はじめましょう』をクリックする。**
![Qiita-no001_img08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/2d32d959-9551-7bbf-88a5-e9b1e714886b.jpeg)


**以下のGUIが開けば、起動確認完了！**
![Qiita-no001_img09.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/58be881c-439d-32d1-1cab-1be7c00bb7e5.jpeg)


## まとめ
今回はWindows10へのQGISのインストール手順をまとめました。
次はGoogleMapなどの地図情報の取り込みを試してみたいと思っています。
（Windows10へのPostGISのインストールは[こちら](https://qiita.com/smiler5617/items/800627a5f6f39317d6b5)）
Apple iPhone と Google Pixel の解像度 (dp) メモ

# iPhone (iOS)

320px 端末そろそろ切られそうっすね (元祖 SE は 2016 年発売だし)

<img width="626" alt="Apple iPhone" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5542/b5e293cc-7c5f-79a7-b429-8368fc194ef2.png">

# Pixel (Android)

アップデートで数十 dp づつ伸びる Pixel くん

<img width="653" alt="Google Pixel" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5542/bedbe40e-4404-f50f-2206-8bc0311654ad.png">

# 解像度

| 端末 | 解像度 |
|:--|:--:|
| iPhone 11 Pro Max/XS Max/11/XR | 414x896 |
| iPhone 11 Pro/XS/X | 375x812 |
| iPhone 8+/7+/6s+/6 | 414x736 |
| iPhone SE2/8/7/6s/6 | 375x667 |
| iPhone SE/5c/5s/5 | 320x568 |
| Pixel 4XL/4 | 411x869 |
| Pixel 3XL/3a | 411x846 |
| Pixel 3a XL/3/2 XL | 411x823 |
| Pixel 2/XL/無印 | 411x731 |
| Android Mobile [^1] | 360x640 |

[^1]: Nexus 5 とか Samsung Galaxy S7 とか Sony Xperia Z5 とか、一昔前の Android 端末は割とこのサイズだった。最近の Samsung Galaxy A20 とか Sony Xperia 1 II とか最近の機種はまた違ってきていて、Pixel のサイズとも違うみたい。

 * [Adaptivity and Layout - Visual Design - iOS - Human Interface Guidelines - Apple Developer](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/)
 * [Device Metrics - Material Design](https://material.io/resources/devices/)
 * [Screen sizes, viewport size and CSS media queries for Devices | Viz Devices by YesViz.com](https://yesviz.com/)
AlfredからPhpStormのプロジェクトとして開けるWorkflowを作成した

## はじめに

PhpStormを使っていて複数のプロジェクトを開きたいときは、FileメニューからOpenするなりRecent Projectから開いたりするなりするのですが、個人的には使いづらくもっとシャッて開きたいなと思ってました。

そこでAlfredからプロジェクトを検索して開ければいいな〜とおもって検索すると、[alfred-jetbrains](https://github.com/bchatard/alfred-jetbrains)といったワークフローを見つけました。

しかし、なぜか検索に引っかかるプロジェクトとそうでないプロジェクトがあったり（open your projects with your favorite JetBrains product.って書いてあった）、プロジェクトが所属しているディレクトリ名でも検索したいなと思ったりしたので自作することにしました。

alfred-jetbransの仕組みは、PhpStormが最近開いたプロジェクトなどの情報をxmlファイルで保持しているのでそのxmlファイルを読み込んで検索結果に出すということをしていたので、それだったら自分で検索対象にしたいディレクトリの設定を書けばいいじゃんとなりました。

## こんな感じ

![open_phpstorm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431346/b2ec789d-7e31-edf9-e3f9-ebc165398ed5.gif)

## Install

https://github.com/wim-web/alfred_open_phpstorm にREADMEもあるのでここから雑に使ってください。

## 使用技術

- Alfred Workflow

最初はとっつきずらいなと感じてましたが触ってみるとそうでもなかったです。

プログラムを書かなくてもできることもあったり、プログラミング言語もPHPとか使えたりよっぽど複雑なことをしようとしない限り難しくなさそうです。

- Deno

このワークフローを使うにはDenoをインストールする必要があります。

TypeScriptで書きたいけど環境揃えるのもめんどくさいな〜と思ったので、デフォルトでTypeScriptが使えるDenoを使いました。
Googleショッピングから商品情報をスクレイピングする方法

新型コロナウイルスの影響でオンラインショッピングの需要が高まるにより、4月21日、Googleは「Googleショッピング」への商品登録を無料化することと発表しました。まずはアメリカで4月末までに実施し、年内に世界に拡大する計画があります。

ネット販売を始めようと考えたとき、楽天市場への出店がまず頭に思い浮かぶ方は多いのではないでしょうか。この発表はネットショップ運営者にとってはいいチャンスだと思われます。無料リスティングを通じて、Googleショッピングを利用する数百万のユーザーに商品を見つけてもらえるようになります。

ただし、初心者の場合は、[Webスクレイピングツール](https://www.octoparse.jp/)などを使用して、競合他社がどのようにGoogleショッピングを通じて自社の製品をアピールおよびマーケティングするの学ぶことが不可欠です。



#Googleショッピングを始める理由

Googleショッピングは、ユーザーがWeb上で商品を見つけて購入しやすくすることをサポートできます。登録した商品はGoogleショッピングのサイトに表示されるだけでなく、検索キーワードとの関連性が高い場合はGoogleの検索結果に表示されることもあります。ですから、Googleショッピングのコンバージョン率は非常に高い、Googleショッピングへの商品の掲載は、ECサイト・ネットショップ運営には不可欠とも言えます。


#Googleショッピングの仕組み

Googleショッピングキャンペーンを最適化するには、まずその仕組みを理解する必要があります。新プログラムでは、商品を提供する事業者が無料でMerchant Centerを通じてリスト登録を行えます。無料リスティングを通じて、Googleショッピングを利用する数百万のユーザーに商品を見つけてもらえるチャンスが全てのマーチャントにもたらされ、Googleショッピング利用者はより多くのオンラインストアと商品を検索対象に目的の商品を探せるようになります。これまでGoogleショッピング広告を活用してきたマーチャントは、広告による有料キャンペーンを展開することで、無料リスティングに加えて商品をより戦略的にアピールできます。とはいえ、Googleショッピングで自社商品が上位に掲載されるには、次のことを行う必要があります。

**1.競合他社より高い価格で入札**
**2.広告のクリック率（CTR）を改善する**

ほとんどの初心者にとって、どのような広告を出稿するのか手がかりがありません。その場合、一番簡単な方法は、他の運営者、特に競合他社がGoogleショッピングで販売状況を監視することです。 

 


#なぜWebスクレイピングが必要なのか？

｢友を近くに置け、敵はもっと近くに置け｣という言葉を聞いたことがありますか？実はGoogleショッピングには、マーケティングキャンペーンに役立つ貴重な情報がたくさんあり、もしこれらの情報を全部入手できれば武器に変えます。もちろん、市場には多くの価格・商品監視のツールがありますが、マーケティング戦略の効果が出る前に、こんな高いツールに投資すると、お金無駄使いになるかもしれません。ほとんどの監視ツールは、さまざまな成熟したオンラインビジネス向けに設計されています。

そのため、Webスクレイピングが機能します。Webサイトからすべてのデータを自動的に抽出し、Excelにインポートて分析できます。

 


#Googleショッピングをスクレイピングする方法

Webスクレイピングには、高度な技術スキルが必要です。幸いなことに、クリックするだけで誰でもWebデータを抽出できる視覚的なWebスクレイピングツールがあります。Octoparseは、初心者にとって一番使いやすいWebスクレイピングツールとして、業界で評判が高いです。誰でもデータ抽出のクローラーを設定し、ほとんどのWebサイトからデータを取得できます。

[Octoparse](https://www.octoparse.jp/)を使ってGoogleショッピング製品情報を抽出するには、まずは取得先のURLを入力する必要があります。内蔵ブラウザでページが表示され、取得したいデータを自由に選択できます。新バージョンのv8.1は、Webページを読み取り、データを自動的に識別します。設定を保存したらすぐ実行でき、抽出したデータをExcelまたはCSVにダウンロードできます。
![Google shopping.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262932/10387173-bea6-6c95-8e61-5ffa12f2c0a2.png)
十分な情報が得られたら、先に進んで結果を分析できます。また、グラフを作成してデータを可視化できます。

 

 

#競合他社から学べること 

- クリック率(CTR)を上げる
クリック率が高いか低いかで、CV率(コンバージョン率)やCPC(クリック単価)は大きく変わってきます。競争力のある価格設定により、常にクリック率が高くなるため、Googleショッピングで特定の検索商品に商品を表示する頻度が高くなります。そのため、競合他社の価格を把握しておくと、競争力を維持するのに役立ちます。また、広告の品質が高くなるほど、広告を表示するための最小入札価格は低くなり、クリック率(CTR)も上げられます。

 

- 卵を以て石に投ず
Amazon、楽天市場ような主要なECサイトに注意を払います。特定の製品に多くの大手企業がいる場合は、入札単価を上げて自社商品を上回ろうとしないでください。代わりに、彼らが絶対に入札しない製品を選ぶことが重要です。

 

- 画像を最適化する
商品画像は、商品のイメージを大きく左右する要素となりますので、十二分に意識しなければなりません。品質のいい画像は、より多くのユーザーにクリックしてもらうことができます。商品画像はもちろん撮影が必要になってくる領域ですが、画像加工の領域で、Photoshopなどで美しい仕上げをする必要があります。

 

- 宣伝内容に注意を払う
クリック率の良し悪しは、商品の宣伝内容に大きく左右されます。「ユーザーのニーズに合うか」、「興味が惹かれる内容かどうか」などがCTRを向上するためのポイントとなり、より魅力的な内容は、購買意欲を高めることもできます。また、ユーザーの検索内容や意図との関連性の高いキーワードを使うのも大事です。

 

 

#まとめ

Googleショッピングは簡単に始められますが、ECサイトやネットショップ運営者にとっては、より積極的に行動し、常に注意を払う必要もあります。Webスクレイピングは、手間をかけずに競合他社の情報を自動的に抽出できる、まだ活用されていない方は、この機会に活用を検討されてみてはいかがでしょうか。

元記事：https://www.octoparse.jp/blog/how-to-scrape-google-shopping/
OUに適用されているGPOの名前を取得したい。ただし、AD DSツールに頼らずに。

# そんなの、gpmc.mscで見りゃいいじゃん。

いやいや、ADDSツール入れなくても見れた方が良いんです。
何よりテキストで取得できるメリットは盛り沢山。
差分比較やら、バックアップやら。

# やり方
以下の通りPowerShellで取得できます。
スクリプトとして保存して、対象OUを引数に渡してあげられるようにすると、なお使い安いかも知れませんね。

```powershell:powershell
# DirectorySearcherのインスタンスを生成します。
# これなら、対象OUのDNを知らなくても良いので。
$adsch = New-Object -TypeName DirectoryServices.DirectorySearcher

# 検索フィルタを設定
# "OU=WebServers"のように任意のフィルタを設定します。
$adsch.Filter = "OU=[対象OUの名前]"

# フィルタにヒットするオブジェクトを一つだけ引っ張ります。
# FindAllとforeachを使って、結果を一個ずつ処理するのもありでしょう。
$ou = $adsch.FindOne()

# ouに適用されたgpoのパスを一個ずつ処理します。(何故か配列になってないので、splitしてこねくり回します。)
$ou.Properties["gplink"][0] -split "¥]" | foreach {
  $temp = $_ -replace "¥[", ""
  $gpoentry = $temp.Split(";")
  $gpo = [adsi]$gpoentry[0]
  $gpo.displayName
}

```
JavaScriptでx === 'a' || x === 'b' || x === 'c' || ...をシュッとさせる

JavaScriptで次のような`||`演算子が繰り返し使われたコードを、より簡潔に書く方法を紹介します。

```js
if (
  x === 'a' || 
  x === 'b' || 
  x === 'c' || 
  x === 'd' || 
  x === 'e'
) {
  console.log('ok')
}
```

このような文字列比較の繰り返しは、`Array.prototype.includes()`を使うと、簡潔に書くことができます:

```js
if (['a', 'b', 'c', 'd', 'e'].includes(x)) {
  console.log('ok')
}
```

`Array.prototype.includes()`はES2016で導入されました。

## 参考文献

* [Array.prototype.includes() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)

---

最後までお読みくださりありがとうございました。Twitterでは、Qiitaに書かない技術ネタなどもツイートしているので、よかったらフォローしてもらえると嬉しいです:relieved:→[Twitter@suin](https://twitter.com/suin)