OmniAICreator/Qiita-1.07M huggingface dataset:
フッターの位置を最下部に固定したいのに、重なってしまう時の対処法


Web制作中、フッターの位置を最下部に固定するつもりが、お問い合わせ部分と重なってしまいました。

<img width="1299" alt="スクリーンショット 2020-04-27 22.31.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584233/1c507237-3ce2-84dd-4242-b40f758c2425.png">

色々調べた結果、**解決した**ので記していきます。




#フッターをウインドウ最下部に固定する

HTMLは下記の通りです。

```ruby:html

<body>
 <div class="wrapper">
　　　　　＊
　　　　　＊
       #省略
　　　　　＊
　　　　　＊
    <footer>
     <p>©︎2020-2020 Goingo and Toigo.</p>
    </footer>

 </div>
</body>

```


↓CSSで位置を最下部にする。

```ruby:css
body {
  width: 100%;
  height: 100%;
}

.wrapper{
  min-height: 100vh;/*①高さの最小値*/
  position: relative;/*②相対位置*/
}

footer {
  width: 100%;
  height: 200px;
  position: absolute;/*②絶対位置*/
  bottom: 0; /*下に固定*/
}

```
①`vh`…画面の大きさを基準にした単位
②`.wrapper`を相対位置とする。`footer`に`position: absolute;`を書いて、位置を最下部に決めるようにする。

ここでページを更新してフッターの位置が最下部にあるか確認。

冒頭の画像のようにお問い合わせ部分と重なったままであれば、以下の方法を試してみてください。
↓
#フッターとコンテンツがネガティブマージンで重なるのを防ぐ

```ruby:html

<body>
 <div class="wrapper">/*フッター以外のすべての要素をdiv要素で囲む*/
　　　　　＊
　　　　　＊
       #省略
　　　　　＊
　　　　　＊
    <div class="push"></div>/*コンテンツ内に空の要素(.push)を加える*/
    <footer>
     <p>©︎2020-2020 Goingo and Toigo.</p>
    </footer>

 </div>
</body>

```

```ruby:CSS
footer {
  width: 100%;
  height: 200px;
  position: absolute;
  bottom: 0;
  margin-bottom: -200px;/*フッターの高さと等しいネガティブマージン*/
}

.push {
  height: 200px;/*フッターと同じ高さに指定*/
}
```
参考にさせていただきました→ https://coliss.com/articles/build-websites/operation/css/css-sticky-footer.html

<img width="1419" alt="スクリーンショット 2020-04-28 1.33.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584233/8ba62553-b626-bc82-81bd-ba067b640b41.png">

最下部に固定することが出来ました！
PHP ~ちょっと忘れがちな基礎文法集~




# 概要
ちょっと忘れがちな基礎文法をまとめました！
随時、更新していきます。


## ファイルの初めに書くやつ<?P ?>

```sample1.php
<?php

// htmlを埋め込む時は, 閉じ ?> が必要

?>
```
```sample2.php
<?php

// htmlを埋め込まない時は、閉じ ?> は必要ない！ルールです。

```
## 文字列に変数を埋め込む時
文字列に変数を埋め込む時、「' '」と「" "」とでは埋め込み方が違う！

#### sample1.php「' '」の場合 : 「.」ドットを変数の前につける

```sample1.php
<?php

$animal = 'ねこ';

echo '野生の' . $animal . PHP_EOL;        // . PHP_EOL(改行)

// 野生のねこ

```
#### sample2.php「" "」の場合 : 文字列の中に変数をそのまま埋め込む事ができる

```sample2.php
<?php

$animal = 'ねこ';

echo "野生の$animal" . PHP_EOL;

// 野生のねこ
```

## 改行や字下げを保持して長めのテキストを表現する時

長い文字列を変数に代入する場合、 **nowdoc** または　**heredoc**  を用いて記述します。

#### nowdoc(なうドキュメント)：

```sample01.php
<?php
$sample = <<<'ST'
こんにちは！
  今日は私の愛猫、タマのお誕生日です。
よかったら一緒にお祝いしませんか。
  お待ちしております！
ST;

echo $sample;

/* 
こんにちは！
  今日は私の愛猫、タマのお誕生日です。
よかったら一緒にお祝いしませんか。
  お待ちしております！
*/
```
$sample = <<<'ST' の `ST`　部分は、`終端記号`と良い、好きな名前で大丈夫です。
今回は、sample.php に textを記述しているので、頭文字を取って`ST`としています。わかりやすいものでok!

そして、変数を埋め込んだ場合：

```sample01.php
$animal = 'タマ';

$sample = <<<'ST'
こんにちは！
  今日は私の愛猫、$animal のお誕生日です。
よかったら一緒にお祝いしませんか。
  お待ちしております！
ST;

echo $sample;

/* 
こんにちは！
  今日は私の愛猫、$animal のお誕生日です。
よかったら一緒にお祝いしませんか。
  お待ちしております！
*/
```
このように変数を埋め込んでも変数展開されません。。。泣

そんな時は、、、!!!

「heredoc」を使います！！


#### heredoc(ひあドキュメント):

```sample02.php
<?php

$animal = 'タマ';

$sample = <<< ST
こんにちは！
  今日は私の愛猫、$animal のお誕生日です。
よかったら一緒にお祝いしませんか。
  お待ちしております！
ST;

echo $sample;

/* 
こんにちは！
  今日は私の愛猫、 タマ のお誕生日です。
よかったら一緒にお祝いしませんか。
  お待ちしております！
*/

```
無事、変数展開されましたね^^
heredoc　は　$sample = <<< ST の `ST`の部分に「' '」「" "」はつけません！

・nowdoc 変数展開しない
・heredoc 変数展開する


~随時更新~
外部exeからC#のexeを読んだときにapp.exe.configが開けないときの対処法

app.exe.configに設定をsave、loadするapp.exeを作成したが、外部exeからこのexeを開くとconfigを開けなかったので、対処方法を記す

GetDirectoryNameでapp.exeのあるディレクトリを取得する

```C#
using System.Reflection;

string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var configFile = path + "\\" + @"app.exe.Config";
```
ImageMagickで文字を描画する際に効果をつける

#概要
ImageMagickを用いて文字を描画する際にちょっとでも装飾をつけたい時があります。
今回はいくつかサンプルをお見せしながら使えるTipsを紹介していきます。

#サンプル
今回はUnityちゃんの上に文字を描画してみて視認性がどの程度あがるかを確認します。
### そのまま絵の上に赤文字を表示
![out1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4541/2e91a90a-d81d-7719-f980-5a18745d3b18.png)

文字を描画するにはfontの指定が必要で今回はosakaフォント使ってます。あとはサイズの指定、色の指定、テキストの位置やテキストの内容を指定しています。


```
$ convert -font Osaka.ttf -gravity center -pointsize 32 -fill red\
-draw "text 0,0 大鳥こはく"\
I_am_Unity-Chan.png out.png
```
シンプルに赤文字だけだと大分文字の視認性が悪いですね。


### 黒のアウトラインを表示
![out2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4541/5072076f-4979-2ff9-8eff-c6aa97a7457e.png)

strokeを指定すると線の色を指定できます。

```
$ convert -font Osaka.ttf -gravity center -pointsize 32 -fill red\
-stroke black -strokewidth 1 -draw "text 0,0 大鳥こはく"\
I_am_Unity-Chan.png out.png
```

アウトラインがあると少しだけ視認性があがりますがまだまだ見にくいですね。

### 黒のアウトラインの上に白枠も追加
![out3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4541/6709664e-7411-023b-603a-ebf259ef006f.png)

1コマンドで文字を2回書いています。一つは白の太字で書いてその上に先ほど描いた黒のアウトラインの文字を描いています

```
$ convert -font Osaka.ttf -gravity center -pointsize 32 -fill red\
-stroke white -strokewidth 7 -draw "text 0,0 大鳥こはく"\
-stroke black -strokewidth 1 -draw "text 0,0 大鳥こはく"\
I_am_Unity-Chan.png out.png
```
これで大分見やすくなったかと思います。

### 文字にグラデーションをつける
![out4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4541/a5a15a56-b6a2-5a6c-b512-2bc3e07b793a.png)
コマンドを３つに分けてます。
1番目のコマンドがグラデーションの文字画像を最初に作っています。
2番目は先ほどのコマンドと一緒です。
3番目で2つの画像を合成しています。
ちなみにグラデーションの画像を作る際に使っているtileというオプションですがキャンバスの色とオーバーレイすることによる表現。なのでキャンバスサイズによって見え方は違うのでその部分の調整は難しかったりします。また一緒にstrokeなどを使うとグラデーションの色が変化してしまうのでコマンドを分けていたりします。

```
$ convert -size 300x40 xc:white -transparent white -font Osaka.ttf\
-gravity center -pointsize 32 -tile gradient:blue-red\
-draw "text 0,0 大鳥こはく" gradients_font.png

$ convert -font Osaka.ttf -gravity center -pointsize 32 -fill red\
-stroke white -strokewidth 7 -draw "text 0,0 大鳥こはく"\
-stroke black -strokewidth 1 -draw "text 0,0 大鳥こはく"\
I_am_Unity-Chan.png out.png

$ convert out.png gradients_font.png -gravity center -composite out.png
```
ここまですると視認性もあがり色も単調じゃなくデザインにも遊びが持たせるようになります。

ちなみに以下の参考にあるサイトに行くとこれ以外にも様々な効果の例が乗ってるのでより豪華に表示したい方は参考にしていただければと思います。

#参考
[Font Effects -- IM v6 Examples - ImageMagick](https://www.imagemagick.org/Usage/fonts/)

#ライセンス
© Unity Technologies Japan/UC
・Address already in useの解決方法

#はじめに
PHPを学習していた時に、サーバーを起動したら...
以下のように出たので、対処法をまとめておきます
初めて書いて読みにくいかもしれませんが誰かの助けになれば、幸いです

```
[vagrant@localhost]$ php -S 192.168.33.10:8000
Failed to listen on 192.168.33.10:8000 (reason: Address already in use)
```

#原因
PHPのサーバーを切らすにputtyのターミナル閉じてしまったことで、
サーバーは使われている状態になり、起動時にエラーが起きてしまった

解決してからもこれをやるとコマンドを打つのがなかなか面倒くさいので、
サーバーは切ってから閉じることを強くおすすめします

#解決方法
```
#rootユーザーでログインしてプロセスの確認
//rootログインがめんどくさい方はコマンドを打つ時にsudoを付けてください
[vagrant@localhost ~]$ ps -a


#portがわかる場合は以下で確認
[vagrant@localhost ~]$ lsof -i:8000


#lsofできない場合は以下でインストール
[vagrant@localhost ~]$ yum -y install lsof


#上記のコマンドを打つと以下のようにPIDを含んだ起動しているサーバー情報が表示されます
COMMAND  PID    USER     FD   TYPE  DEVICE  SIZE/OFF NODE  NAME
php      10721  vagrant  3u   IPv4  20710     0t0    TCP   192.168.33.10:irdmi (LISTEN)


#kill (PID番号)を入力してプロセスを終了
[vagrant@localhost ~]$ kill 10721


#改めてコマンドを打つと起動します
[vagrant@localhost ~]$ php -S 192.168.33.10:8000 
PHP 5.6.40 Development Server started at Mon Jan  6 12:21:24 2020 Listening on http://192.168.33.10:8000


#killしても解決できない場合は kill -9 (PID番号)で強制終了してください
[vagrant@localhost ~]$ kill -9 10721　

```

#おわりに
今回は[<font color="Lime">kill</font>](https://eng-entrance.com/linux-command-kill)コマンドについて書きましたがもっと知りたい方は
killの文字をクリックしてもっと詳しい情報を確認してください
ご指摘がございましたら、コメントしていただければ幸いです！！
最後まで読んで頂きありがとうございました
DockerのElasticsearchにSudachiを入れて検索するだけ

この記事は余計なことを考えず、さくっとローカルで全文検索をやりたいって人のための簡易手順書です。

# Elasticsearchとは

既に色々な所での解説があるので詳細は省略しますが、要はオープンソースの全文検索エンジンです。

# Sudachiとは

いい感じの形態素解析。

# この記事でやること、やらないこと

Docker環境が既に入っているローカルのPCにElasticsearchの環境を立ち上げ、文章を投入して検索してみるだけ。細かい設定などはしません。１年に一回くらい、手元にあるドキュメントに対して検索をかけたい瞬間があると思いますが、そんな時に以下の手順をなぞるだけ。全然大したことはやらないです、ほぼ自分用メモ。色々使う場合は、[この記事](https://qiita.com/nskydiving/items/1c2dc4e0b9c98d164329)を読みましょう。

# 導入手順

ここから、Dockerファイルを持ってきましょう。設定ファイルの内容は省略。

```
$ git clone https://github.com/peisuke/elasticsearch-docker.git
$ cd elasticsearch-docker
$ docker-compose build
$ docker-compose up -d
```

以上。Dockerコンテナを立ち上げてしばらく待ちましょう。立ち上がったかどうかは以下のコマンドで確認。

```
curl -X GET http://localhost:9200/
```

# 使ってみる

まずはサンプルの文章を投入してみましょう。投入はJSON形式で行います。ここでは`mytest`がDB、`test`がテーブル、`1`がドキュメントの名前みたいなノリです。なおElasticsearch界ではIndex/Type/Documentって呼びます。

```
curl -X PUT http://localhost:9200/mytest/test/1 -H 'content-type: application/json' -d '
{
  "title" : "memo",
  "text"  : "hogehoge"
}'
```

次に検索をしてみましょう。

```
curl -X GET http://localhost:9200/mytest/test/_search -H 'content-type: application/json' -d '
{
  "query": {"match": {"title":"memo"}}
}'
```

結果として以下のように返ってきます。簡単ですね。

```
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.2876821,
    "hits": [
      {
        "_index": "mytest",
        "_type": "test",
        "_id": "1",
        "_score": 0.2876821,
        "_source": {
          "title": "memo",
          "text": "hogehoge"
        }
      }
    ]
  }
}
```

# 片付け

停止する。

```
docker-compose down
```

全削除

```
docker-compose down --rmi all
```

# その他

`http://localhost:5601`にアクセスすればkibanaで管理画面が見えたりするぞ
MacのSkype for Businessでマイクが使えない問題の解決法

## 状況

Skype for Businessでマイクが使えない．
多くのMacユーザーが同じ問題となっている.

> Mac (MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports), Mac OS 10.15.3)
にSkype for business (16.28.175)を入れて試してみましたが，カメラは使えましたがマイクはダメでした．
Skype for businessの環境設定からオーディオデバイスの設定を開く時に，以下のようにカメラのアクセス権限については要求されますがマイクについては要求されず，
Macのシステム設定からも，マイクにアクセスできるアプリとしてSkypeforBusinessは表示されないようです
イヤホンマイクを使用してもダメでした．

↓のように，Macの `システム環境設定 > セキュリティーとプライバシー` のプライバシータブ内の マイクの項目に， Skype for Businessが表示されない．これによってマイクの使用が許可されない模様．

![SkypeForBusinessがない](https://i.imgur.com/ay67Hic.png)


## 解決策

[こちら](https://answers.microsoft.com/en-us/msoffice/forum/all/skype-for-business-microphone-not-working/bdafc0a0-cf31-4f3b-bc31-b9e9bec1ece1)のMicrosoftのQ&Aを参考にしたところ，解決したのでここにまとめる．
記事の手順の実施によって発生した問題や不具合については，自己責任でお願いします．

### MacシステムのSIPプロテクションを解除

Macのシステム整合性保護機能(SIP)を解除する．リカバリーモードでMacを起動したりする必要がある． 
[こちらの記事](https://support.intego.com/hc/ja/articles/115003523252-%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%95%B4%E5%90%88%E6%80%A7%E4%BF%9D%E8%AD%B7-System-Integrity-Protection-SIP-%E3%81%AE%E7%84%A1%E5%8A%B9%E5%8C%96) を参考．

> まず、macOSをリカバリーモードで起動します。リカバリーモードで起動させるには、お使いのコンピュータを再起動させ、その直後から起動音が鳴ってしばらくの間はCommandキー（⌘）とRキーを同時に押し続けます。

リカバリーモードでメニューバーからターミナルを開き，

```
# csrutil disable
```

を実行して再起動．これでSIPが解除されているはず．

### `TCC.db` のバックアップ

再起動したMacでターミナルを起動し，以下のコマンドを実行して `TCC.db` のバックアップをホームディレクトリにおいておく

```
cp ~/Library/Application\ Support/com.apple.TCC/TCC.db ~/TCC.db
```

### `TCC.db` を開きデータベースを書き換え

ターミナルから以下のコマンドで `TCC.db` を開く．この時，PCのパスワードを聞かれるので入力してEnter.

```
sudo sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db
```

すると， ターミナルに`sqlite>` と表示され，Sqliteのコンソールが起動されている．ここに，以下のコマンドを入力してEnter．

```
INSERT INTO access
VALUES('kTCCServiceMicrophone','com.microsoft.SkypeForBusiness',0,1,1,NULL,NULL,NULL,'UNUSED',NULL,0,1541440109);
```

この時何かエラーが出たときは， ↑のコマンドの`INSERT` 部分を `REPLACE` に変えて試す．
何も言われなかった場合はおそらく成功． `.exit` コマンドを入力して `sqlite` コンソールを抜ける
うまく行っていれば，
Macの `システム環境設定 > セキュリティーとプライバシー` のプライバシータブ内の マイクの項目に， 以下のように Skype for Businessが表示されているはず．
これでマイクが使えるようになるはずです．

![](https://i.imgur.com/PDBL8fU.png)
Gatsby １つのページで複数のクエリを実行する方法

１つのページでお知らせやブログなど複数のコンテンツを表示したい場合はクエリを複数作成することができる。

サイドバー用のカテゴリ一覧など複数ページで共通のデータを使う場合はstaticQueryを使ってコンポーネントごとにクエリを実行する。

## 複数クエリを１つのページで実行する
複数クエリの実行は頭に名前をつけるだけ。名前は自由。

```js
post: allMarkdownRemark(){}
```

```jsx
import React from "react"
import { graphql } from "gatsby"

const MultipleQuery = ({ data }) => {
  const post = data.post
  const portfolio = data.portfolio
  const siteTitle = data.siteMetaData.siteMetadata.title

  console.log(data)

  return (
    <h1>test</h1>
  )
}

export default MultipleQuery

export const query = graphql`
  query {
    post: allMarkdownRemark(
      limit: 3
      sort: { order: DESC, fields: [frontmatter___date] }
      filter: { frontmatter: { type: { eq: "blog" } } }
    ) {
      edges {
        node {
          frontmatter {
            title
            path
          }
        }
      }
    }
    portfolio: allMarkdownRemark(
      sort: { order: DESC, fields: [frontmatter___date] }
      filter: { frontmatter: { type: { eq: "portfolio" } } }
    ) {
      edges {
        node {
          frontmatter {
            title
            path
          }
        }
      }
    }
    siteMetaData: site {
      siteMetadata {
        title
      }
    }
  }
`;
```
Railsで簡単なレベル機能

#Railsで簡単なレベル機能を作ってみた

今回はこれまで作ってきたSNSサービスにレベル機能を追加しました。
簡易的なものなのであくまで参考程度にして頂ければと思います。

＜開発環境＞
1. ruby 2.6.3
2. Rails 5.1.6
3. AWS Cloud9
4. GitHub
6. sqlite3(develop環境)

## 設計構想
・一定の経験値(exp)を貯めるとレベルが上がっていく機能。

・投稿と紐づけて自分の食べたラーメンの種類によって得られる経験値が変わる
→postテーブルにexpカラムを設置して投稿するとexpが得られる、postテーブルのuser_idと紐づけてexpの合計値を出す。expの合計値によってレベルに反映されていく。

・userにpostで得られたexpの合計(exp_sum)がlevelとして表示される。

## モデル
今回用意するのはpostテーブルにexp（経験値）カラム、userテーブルに経験値の合計であるexp_sumカラム、levelカラムの三つです。

```
rails g migration add_exp_to_posts exp: integer
```

```
rails g migration add_exp_sum_to_users exp_sum: integer
```

```
rails g migration add_level_to_users level: integer
```

この三つを追加したのちにrails db:migrateをします。

##　経験値を得るまでの流れ
上記にもある通り、投稿時に経験値が得られるような仕組みをつくっていきます。

最初に投稿時にどのラーメンを食べたか、そしてラーメンの種類によって得られる経験値が変わるようにselectを使用して選択欄を作りました。

```ruby:index.html.erb
<div class="form-group">
 <%= f.select :exp, [["選択して下さい", 0],["小ラーメン・小豚", 50],["小豚ダブル・大ラーメン", 100],["大豚", 150],["大豚ダブル", 200]] %>
</div>
```

次にcontrollerで経験値を得られるようにしていきます。

```ruby:posts_controller.rb
def create
    @post = current_user.posts.new(post_params)
    @user = User.find(current_user.id)
    @user.exp_sum +=  @post.exp.to_i
    @user.level = @user.exp_sum.to_i / 50
    current_user.update(exp_sum: @user.exp_sum, level: @user.level)
.
.
.
```

@user.exp_sum += @post.exp.to_iで投稿時に得られるexpの合計値をuserテーブルのexp_sumカラムに送り、exp_sumを50で割った値をレベルとして表示されるようにしました。


##　今後の改良点
このままではレベルが上がるための値が一定値となっているので、改善させていく予定です。
定期的なパスワード変更をなくす(上司を説得する)には

# パスワードは、肉やパンのように腐ったりしない
だから「消費期限」なども必要ない。

とは少々乱暴な意見かも知れませんが、これには裏付けがあって、
2017年米国国立標準技術研究所 (NIST) による刊行物の、パスワードのベスト・プラクティスに以下のように記載されています。

*認証コードの漏えいの証拠がある場合、または加入者から変更の要求があった場合を除き、
「検証者は、記憶された秘密情報を恣意的（定期的など）に変更することを要求してはならない」
また、「大文字や小文字を混ぜたパスワード」を推奨してきたが、これらも「無意味だった」*

としています。

Microsoft社でも、この刊行物に従って
Windows 10 v1903およびWindows Server v1903のセキュリティベースライン（DRAFT）において、**定期的なパスワード変更を必要とするパスワード有効期限ポリシーを削除する**とのこと。

Microsoft社は、なぜパスワードの有効期限ポリシーを削除するのですか？
という疑問に対して、
**パスワードが盗まれることがない場合は、期限切れにする必要はありません。
また、パスワードが盗まれたという証拠がある場合は、有効期限が切れるのを待って問題を修正するのではなく、ただちに行動することになります**

という答え等を用意しており、NISTの主張に同意するようにMicrosoft セキュリティガイダンスのブログで公開しています。

> 参考）
[Windows 10と Windows Serverのベースライン設定においてパスワードの有効期限設定を廃止する](https://docs.microsoft.com/ja-jp/archive/blogs/secguide/security-baseline-draft-for-windows-10-v1903-and-windows-server-v1903)



# 定期的な変更を実施することのリスク
また、日本の総務省も国民のための情報セキュリティサイトで、
**実際にパスワードを破られアカウントが乗っ取られたり、サービス側から流出した事実がなければ、パスワードを変更する必要はありません**
と謳っており、
**むしろ定期的な変更をすることで、パスワードの作り方がパターン化し簡単なものになることや、使い回しをするようになることの方が問題となります**
とも書いています。

その考えに至った経緯は前述のNISTからきており、Microsoft社と同様、これからのパスワード管理の考え方を明示しています。

> 参考）
[安全なパスワード管理（総務省）](https://www.soumu.go.jp/main_sosiki/joho_tsusin/security//business/staff/01.html)

## 定期的にパスワード変更をするときの人間のさぼり方

パスワードの変更を強制された大半のユーザは、類似のパスワードを使いまわすことが多い。
末尾の一文字を変更するだけ。
大文字、小文字を変更するだけ。
数字の前後を入れ替えるだけ等々。
しかもその新しいパスワードを忘れてしまったりする。

考案者の思惑通りにはならず、脆弱なパスワードを生み出す結果になってしまったと、ルールの考案者であるビル・バー氏が告白した。
NISTが2017年6月に発表した冊子「800-63」は全面改訂され、パスワード有効期限に関する項目は撤廃された。



# パスワードの有効期限設定ポリシーはオワコン、時代遅れ

そうです。パスワードの有効期限など今どきではないのです。
この提案がNISTから発表されたのは2017年6月。
2020年現在、既に3年近く時が過ぎていますが、周りでパスワード有効期限なんて設定しなくていいよ、なんて声が聞こえているでしょうか。

少なくとも私の周りではそんなことを言えばギョッとした目で見られます。

システム管理者がセキュリティ関連の設定（設計）をしているときに、
上司らの言葉で、「それで監査通るの？ 説明できる？」と言われたことってないでしょうか。

セキュリティに説明できないような事項が発生したときは、その理由を求められ（誰にでも分かりやすく）説明する必要があります。

が、本来は監査する側がそんな設定は必要ないと判断してもらうことが正解のはずです。
今、監査を受けて通るかどうか不明なら念のため設定する方針になったりする時点で、まだまだ浸透していない話なのだと思います。

> 参考）
[パスワード「90日ごとに変更」は間違い？　ルール提唱者が「後悔」](https://www.itmedia.co.jp/news/articles/1708/18/news072.html)
[パスワードの有効期限の設定はもはや時代遅れかもしれません](https://www.ibm.com/blogs/security/jp-ja/the-expiration-date-on-passwords-has-expired/)



# 定期的なパスワード変更をなくす(上司を説得する)には
もし今後の設計で私が聞かれたときは、Microsoft社や、総務省も言ってますよ、
と日本人的な回答を用意しておいて、エビデンス(笑)を求められた際は、総務省のリンクを突き付けてやろうかと考えています。

上司などを説得するスキルは貴方が上司とそれまでに築いた関係性と裁量によるとは思いますが、明らかに上司より偉い人達が言っているから大丈夫だと思わせればいいはず。理論ではなく安心を求めている場合に限りですけど。

※貴方がMicrosoft社や総務省にお勤めの方ならこの手は通用しませんけどね(´Д｀)

根本から改善するには、世の中の意識を変更しなければいけないので、まずは定期的にパスワード変更を求める銀行のサイトらへんが追従してくれれば、話は早くなるのにな、と思います。
テストのためのシステム分析手法 ACC (Attributes-Components-Capabilities)

[テストから見えてくるグーグルのソフトウェア開発](https://www.amazon.co.jp/dp/B00IE3B522)という書籍の中で、Googleが実際に使用している（していた？）ACCというテストに関するメソッドを紹介していました。
以下のページに概要が載っています。
https://code.google.com/archive/p/test-analytics/wikis/AccExplained.wiki

## ACCとは何か
* Attributes-Components-Capabilitiesの略です。
* テストのためのシステム分析手法です。
* システムのテストすべき重要な箇所を洗い出すことができます。
* 簡単です。規模によりますが20分から1時間くらいで完了します。
* 簡単故に、維持も低コストです。必要に応じて何回でも行い、内容をアップデートし続けられます。
* ACCの結果をもとに、システムのリスク分析、テスト計画の設計、カバレッジ計測を行うことができるようになるでしょう。

## ACCの手順

対象システムのAttributes、Components、Capabilitiesをそれぞれ書いていきます。

### 1. Attributesを定義する
* Attributesは、製品の性質や特徴、他製品との違いです。
* 例えば以下のようなものです。
  * セキュア
  * 高速
  * 導入しやすい
  * ユーザーフレンドリー
  * ソーシャル
* 製品の売り文句としてHPのトップに書いている場合も多いのでは。

### 2. Componentsを定義する
* Componentsは、システムを構成する部品です。
* 例えば以下のようなものです。
  * データベース
  * API
  * ログイン
  * メール送信
  * 検索機能
  * ショッピングカート

### 3. Capabilitiesを書く
* Capabilitiesは、システムが実際にできる動作、あるいはユーザーが要求できる動作です。
* AttributesとComponentsを組み合わせてCapabilitiesを記述します。
* 例えば以下のようになります。
  * 検索機能は高速である：常に1秒以内に検索結果を返す
  * ショッピングカートはユーザーフレンドリーである：どんな端末からでも操作できる、あるいは商品選択後○ステップで購入が完了する
* テスト可能な内容にすることが重要です。

## ACCを使った分析等
* Attributes x Componentsのマトリクスでヒートマップを作りリスク分布を分析する。
* テストの優先順位付けを行う。
* テストケースと対応付けてカバレッジ計測。


## 最後に
以上、ACCの紹介でした。「品質は開発プロジェクトの最初から作り込んでいくもの」「品質を上げる努力をしなければ品質を上げる力は身につかない」を胸に刻み、日々積み重ねていきましょう。
Google Tag ManagerでPV数をCookieに保持しておくととっても便利な話

## PV数を保存しておくと美味しい

広告の最適化等でかなり役に立ちます。マイクロコンバージョンとして送るも良し、リマーケティングタグの発火条件に設定するも良し。このあたりの広告の最適化方法はまた後日紹介します。


## 設定概要

Google Tag Managerを使って、CookieにPV数を保存しておきます。Cookieを使う理由は期限があるため。期限を30分後にしておくと、Google Analyticsのセッションに近い形で値を保持しておくことが出来ます。もし期限付けず、ブラウザ単位での累積のPV数が良ければlocal Storageを使う事をおすすめします。

## 設定方法

新規タグで、カスタムHTMLを選択して以下のコードを挿入します。

```javascript
<script>
(function () {
  var count = 0;
  var cookies = document.cookie; 
  var cookiesArray = cookies.split(';'); 
  for　(var i = 0; i  < cookiesArray.length; i++)　{ 
    var cArray = cookiesArray[i].split('='); 
    if( cArray[0].trim() == 'pvCounter'){ 
      count = Number(cArray[1]);
    }
  }
  var halfHourAfter = new Date(+new Date() + (30 * 60 * 1000));
  document.cookie = 'pvCounter=' + String(++count) + "; path=/; expires=" + halfHourAfter;
}());
</script>
```

発火のトリガーは基本「All Page」で良いと思いますが、商品ページのPV数をカウントしたい場合等は、このタグが商品ページのみで発火させるようにトリガーにすると良いです。

![スクリーンショット 2020-04-27 23.52.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293892/d2dc71ba-7a1d-0a26-a5d5-073ddafe75dd.png)

## 動作確認

Google ChromeのデベロッパーツールからCookieを調べます。
ちゃんと値が入っており、パスと期限が設定した通りになっていればOKです。

![スクリーンショット 2020-04-27 23.53.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293892/b15f8717-1f9e-699e-a2b3-92ff72ad49b6.png)
※試しにQiitaのページのコンソールでJSを走らせてテストした結果のスクショ。

## 使い方
switch文と三項演算子

何かと評判の良くない三項演算子の可能性についてちょっと試してみたのでメモ。
まずは典型的な三項演算子。

~~~CSharp
int age = 18;
Console.WriteLine(age < 20 ? "未成年です" : "成人です");
~~~

`Console.WriteLine`行のかっこ内が三項演算子で、`age`が20歳未満であれば`未成年です`が表示され、20歳以上であれば`成人です`が表示されます。
このように三項演算子は最初の式を評価して`true`であれば`:`の前の値が引き渡され、`false`であれば後の値が引き渡されます。

マルかバツかのような、わざわざif文を書くほどでもないような簡単な処理に重宝します。

では、選択肢が2つ以上ある場合どうなるでしょうか。
普通はif文をたくさん書くか、switch文を書くことになります。
このようなテーブルデータを例に取ると。

| id | item |
|:-:|:-:|
| 0 |りんご   |
| 1 |みかん   |
| 2 |バナナ   |

こんな感じのswitch文を書くことができます。

~~~CSharp
int fruitId = 0
switch (fruitId)
{
	case 0:
		Console.WriteLine("りんご");
		break;
	case 1:
		Console.WriteLine("みかん");
		break;
	case 2:
		Console.WriteLine("バナナ");
		break;
	default:
		Console.WriteLine("不明です");
		break;
}
~~~

これを三項演算子で書くとこうなります。

~~~CSharp
int fruitId = 0
Console.WriteLine(
           fruitId == 0 ? "りんご" 
        : (fruitId == 1 ? "みかん" 
        : (fruitId == 2 ? "バナナ" 
                        : "不明です")));
~~~

書けなくはないけど、めっちゃ怒られそうですね。

悪乗りしてもう一つ。

~~~CSharp
int month = 3;
Console.WriteLine(month > 0 && month < 4 ? "春です" 
	: (month < 7 ? "夏です" 
	: (month < 10 ? "秋です" 
	: (month < 13 ? "冬です" 
	: (month % 12 == 0 ? "たぶん冬" 
	: (month % 12 < 4 ? "たぶん春" 
	: (month % 12 < 7 ? "たぶん夏" 
	: (month % 12 < 10 ? "たぶん秋" : "たぶん冬")))))));
~~~

月を入れると季節が返ってくる三項演算子です。おまけで13月とかオーバーフローした場合でもちゃんと季節が返ってくるようにしました。
前の配列だったり、次の配列だったり

前の配列を参照したり、次の配列を参照したり。
本来は同処理は吸収したほうが良いがとりあえずメモとして...。

<pre>
	
	var statusDatas = {
		'list' : [{
			'data' : 'first',
			'css'  : 'firstCss'
		},{
			'data' : 'second',
			'css'  : 'secondCss'
		},{
			'data' : 'third',
			'css'  : 'thirdCss'
		},{
			'data' : 'fourth',
			'css'  : 'fourthCss'
		}],
		'next' : function(status){
			var list = this.list,
				max = list.length,
				key = 0;
			for (var i = 0; i < max; i++) {
				if (status !== list[i].data) continue;
				key = i === (max-1) ? 0 : (i + 1);
				//console.log("NEXT :", i, max, key);
				return this.list[key];
			}
			return "";
		},
		'prev' : function(status){
			var list = this.list,
				max = list.length,
				key = 0;
			for (var i = 0; i < max; i++) {
				if (status !== list[i].data) continue;
				key = i === 0 ? max-1 : i-1;
				//console.log("PREV :", i, max, key);
				return this.list[key];
			}
		}
	};
	$(function(){
		$(document).on('click', 'ul.list li', function(){
			var $this = $(this),
				status = $this.attr('data-change-test'),
				next,
				prev;
			next = statusDatas.next(status);
			prev = statusDatas.prev(status);
			
			
			console.log("NEXT:",next, "PREV:",prev);
		});
		
		
	});

</pre>
M5Stackでセンサーライト作ってみた（その２ 省電力にしようとしてみた、、、が）

前回の投稿で[M5Stackでセンサーライトを作ってみた](https://qiita.com/moritakuya/items/d9daca2d68fe3ae4579a)。
今回はをこれを省電力化してみようとの試み。

きっかけは前回作成したセンサーライトもどきを玄関に設置してみたのだが、コンセントが近くに無く、SONYのモバイルバッテリー（CP-F10L）を繋いで動かしていた。
このバッテリー、買ってから3、4年経っているので劣化しているとはいえ、10,000mAhの大容量なのでそこそこ持つものと思っていた。（2週間くらいもってくれたらなー、と期待してた）
が、蓋を開けてみれば3日と持たないではないか！

これはきっと消費電力が大きいのだろうと思い、レッツ省電力化。

まずはネットから先人の知恵を拝借するのが常であろうが、面倒くさいので、自分で調べもせず同僚に相談。
そしたら「クロック数を下げてみたら？」とのこと。
なるほどと思い下げてみた。

```.cpp
// 消費電力を下げるためクロック数を下げる
setCpuFrequencyMhz(10);
```
これで良し、と思いきやなんと、数分すると動かなくなった。
調べてみるとどうやら消費電力が少なすぎるとディープスリープに入ってしまうもよう。
https://github.com/m5stack/M5Stack/issues/34

しかたないので定期的にクロック数を上げてディープスリープに入らないように改造。改造したのが以下。
点灯していない間は30秒間隔でクロック数を上げている。

```.cpp
#include <M5Stack.h>
#include "timer.h"
#define WAKE_INTERVAL_MSEC 30000

#define PIR_PIN 2

bool isLedOn = false;
Timer sleepTimer;

void setup() {
  M5.begin();

  // 消費電力を下げるためクロック数を下げる
  setCpuFrequencyMhz(10);

  pinMode(PIR_PIN, INPUT);// set pir sensor pin as input

  // 画面を白くすることにより照明の代わりにする
  M5.Lcd.fillScreen(WHITE);

  // スリープ状態にしておく
  sleep();
}

void loop() {

  int value = digitalRead(2);// read the pin(0: not detectd 1: detected)
  
  if (value == 0)
  {
    if (isLedOn == true)
    {
        isLedOn = false;
        // スリープ状態にしておく
        sleep();
    }
    else
    {
      // スリープ状態でも定時間に一回起動する（32秒でのスタンバイ対策）
      if (sleepTimer.GetElapsed() >= WAKE_INTERVAL_MSEC)
      {
        // クロックを上げてスタンバイ状態になってしまうのを回避する
        M5.Lcd.wakeup();
        setCpuFrequencyMhz(240);
        delay(1000);
        // クロック数を元にもどしてスリープ
        setCpuFrequencyMhz(10);
        sleep();
      }
    }
  }
  else
  {
    if (isLedOn == false)
    {
      isLedOn = true;
      // スリープ状態を解除して点灯状態にする
      wakeup();
    }
  }

  delay(500);
}

void sleep()
{
  sleepTimer.Start();
  M5.Lcd.sleep();
  M5.update();        
}

void wakeup()
{
  M5.Lcd.wakeup();
  M5.Lcd.setBrightness(255);
  M5.Lcd.fillScreen(WHITE);
  M5.update();  
}
```
※timer.hおよびTimerクラスは自作したもの。ただのストップウォッチ。

これでヨシ！かと思いきや1～2時間くらいでやはり動かなくなってしまう。

なぜ？と思ったが、どうやらモバイルバッテリーからの給電がストップしてたもよう。
消費電力が少ないと給電がストップしてしまうのだろうか？

省電力化しようとしてるのに省電力化したら止まるという、、、ままならない。
良い解決策も思いつかないのでとりあえず不貞寝。
「この接続ではプライバシーが保護されません」と出るページをSeleniumWebDriverで操作する

![この接続ではプライバシーが保護されません.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/a2cefeec-8656-ff29-a332-4b57209f263d.png)

SeleniumWebDriverでブラウザの操作を自動化しようとして、上記のように

> この接続ではプライバシーが保護されません

というメッセージが出てしまうとき。このままだと自動操作が止まってしまうので、対応が必要です。

ブラウザの設定や証明書の設定等々で解決できる場合は良いのですが、何らか事情があって設定できない場合は自動テストスクリプト側で対応します。

## Desired Capabilitiesで設定する

以下、PythonでChrome動かす場合のサンプルです


``` python
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

class TestClass():

    def setup_method(self):
        dc = DesiredCapabilities.CHROME.copy()
        dc['acceptSslCerts'] = True
        self.driver = webdriver.Chrome("path/to/driver/chromedriver.exe", desired_capabilities=dc)
```

上でやっているのは、Chromeを起動する際に`desired capabilities`というのを設定しています。

[Capabilitiesについて](https://www.seleniumqref.com/api/java/about/About_capabilities.html)
の表にあるような項目について、ブラウザを好みの設定で起動することができます。

今回は`acceptSslCerts`という設定をtrueにして＝SSL証明書を許可する設定にして起動しています。

## 参考

- [How to Handle SSL Certificate in Selenium WebDriver](https://www.guru99.com/ssl-certificate-error-handling-selenium.html)
- [Capabilitiesについて](https://www.seleniumqref.com/api/java/about/About_capabilities.html)
- [DesiredCapabilities_set-Python](https://www.seleniumqref.com/api/python/browser/Python_DesiredCapabilities_set.html)
SwiftのOptional型をアンラップする方法を３分で理解　超初心者向けメモ

# Optional型とは
Swiftの通常の型はnilを許容しません。
nilを許容した型をOptional型と言います。
例　Int? String?など

Optional<Wrapped>型は値を持っていない可能性があるため、Wrapped同士の変数や定数と同じように扱う事が出来ません。例えばInt?どうしの四則演算はコンパイルエラーになります。

```swift

let a: Int? = 1
let a: Int? = 1
a + b = コンパイルエラー
```
 
# Optional型から?を取り除くには、三つの方法があります。
## if-let

```swift
let a: Int? = nil

if let b = Int? {
  // 値が存在する場合に実行される文
  print("aはIntです")
} else {
  // 値が存在しない場合に実行される文
  print("aはnilです")
}
```

## ??演算子
値が存在しない場合のデフォルトの値を指定する演算子

```swift

let numberOrNil: Int? = nil
let number = numberOrNil ?? 3
print(number) // 3
```

## !
強制アンラップ（forced unwrapping）は、強制的に値を取り出す方法です。
ただし値が存在しなければ実行時エラーになる事を意味します。

```swift

let a: Int? = 1
let b: Int? = 1

a! + b! // 2
```

## 参考
Swift実践入門　[著] 石川洋資　西山優世
型を明確にしながらaxiosを使う

この記事では、APIへの問い合わせ時にJSONの各項目の型を明確にしながらaxiosを利用してみます。
（`get`, `post`などの基本的なメソッドに絞った内容です。）


## JSみたいに利用する
JSと同じようにaxiosを利用すると、成功時のレスポンス・エラー時のレスポンスそれぞれがanyになります。

```TypeScript
import axios from 'axios'

axios.post(
  'https://reqres.in/api/register',
  {
    email: 'eve.holt@reqres.in',
    password: 'pistol',
  }
)
.then(res => {
  // res.dataはanyになる
  console.info('token: ' + res.data.token)
})
.catch((e) => {
  if (e.response !== undefined) {
    // e.response.dataはanyになる
    console.error(e.response.data.error)
  }
})
```



## 型の情報を伝える
インターフェイスを定義して、ジェネリクスで伝えてあげることで応答に関する型を明確にすることができます。
[定義](https://github.com/axios/axios/blob/master/index.d.ts#L144)を見る感じでは、リクエストデータについてはジェネリクス経由で型情報を伝えることができないみたいなので、注釈付きで変数を作り、それを引数に渡します。（他に良い方法があれば是非教えていただけると！）

```TypeScript
import axios, { AxiosError } from 'axios'

interface IPostRequest {
  email: string
  password: string
}

interface IPostResponse {
  id: number
  token: string
}

interface IErrorResponse {
  error: string
}

// 項目に欠けがあった場合でもエラーが出るよう外部で定義
const requestData: IPostRequest = {
  email: 'eve.holt@reqres.in',
  password: 'pistol'
}

// サーバからの応答の形式を渡す
axios.post<IPostResponse>(
  'https://reqres.in/api/register',
  requestData,
)
.then(res => {
  // res.data.tokenはstring
  console.info('token: ' + res.data.token)
})
// エラー応答の構造を明示する
.catch((e: AxiosError<IErrorResponse>) => {
  if (e.response !== undefined) {
    // e.response.data.errorはstring
    console.error(e.response.data.error)
  }
})
```

デモ
https://codesandbox.io/s/typed-axios-0lgsy?file=/src/index.ts



## おわりに
次の各メソッドで、レスポンスの形式については前述の内容と同じ指定方法でTypeScriptの恩恵を受けられそうです。
`request`, `get`, `delete`, `head`, `options`, `post`, `put`, `patch`
文字列に埋め込める特殊文字が存在する!?

# 本日の学び
本日よりドットインストールのscala入門に入門した。
そこで学んだことをアウトプットします。

## 型について

##　数字の型
Byte、Short、Int、Longと4種類の型が存在するが
Long型の書き方が特殊で


```scala
val l: Long = 5555555555L
```
定義する際にはこのように大文字のLをつけてあげる必要がある。

## 浮動小数点数
小数点がある場合には
FloatとDoubleの2種類があり
Floatの定義の際には

```scala
val f = 234.34F
```
このように末尾にFをつけてあげる必要がある。

## 文字の型
CharとStringがある
Charは''で囲む

```scala
val c = 'c'
```

Stringは""で囲む

```scala
val s = "Hello World"
```

## 特殊文字
文字列に埋め込むことのできる特殊文字が存在する
改行: \n
タブ: \t 
で表現することができる。
実際の挙動はこう

```scala
println("Hello\nWorl\td")
```
とすると出力がこうなる

```scala
Hello
Worl	d
[success] Total time: 0 s, completed 2020/04/27 23:17:24
[info] 12. Monitoring source files for scala/run...
[info]     Press <enter> to interrupt or '?' for more options.
```

型によって定義の際に文字を入れることや、特殊文字を知ることができたのは嬉しいですね。
【質問】Swiftで２Dスクロールのゲームを作りたい

２Dスクロールのゲーム作りに挑戦しようと思っています。
ただ当方全くのど素人で何冊かの本を買って
読みながらジタバタしてみるものの
どうにも知りたい情報にたどり着けなくて
理解が追いつきません
なんとか助けていただきたく投稿します

どこから手をつけて良いかすらわからないので
そこから教えていただける方お願いします

作ろうとしているゲームは
①２D縦スクロール
②自機装備画面、クエスト選択画面、ショップ画面、ガチャ画面、プレイ画面がある
③装備によって自機の画像が変わる
④レベルアップシステムがある

と言った感じで考えています

Sprite Kitを使うということはなんとか分かったのですが
自機の装備情報をどう扱うのかとか
各ビューをどう組み合わせて使うのが
良いのかとか
全くわかりません

誰か助けてください…
ViewModel と相互作用処理

## はじめに

オレオレ解釈の覚え書き　その６

今回から ViewModel のお話です。ここでは相互作用処理についてまとめます。

## 本文

MVVM における相互作用処理とはユーザとの対話が発生する処理を指します。ダイアログを表示して Yes/No の選択を促す場合などがこれにあたります。こういった処理ではユーザの判断を待たなければなりませんが、直接 View にアクセスできない ViewModel はどのように実装すればよいでしょうか？

MVVM を学び始めたころの私は、このパターンで構築するにあたり、なんとしても ViewModel だけで処理を実現させようと躍起になっていました。当時は参考になる文献が少なく、思い通りに実装できない状態が続き、MVVM への苦手意識を持ったポイントにもなりました。

現在は強力なフレームワークがいくつも登場し、相互作用処理を容易に実現できる環境が整っています。Microsoft 製の Prism では InteractionRequest と InteractionRequestTrigger を組み合わせてこれを実現しています。InteractionRequest クラスには Raise メソッドが実装されており、これを実行するとインターフェースで定義された Raised イベントが発生します。このイベントを購読するためのトリガーが InteractionRequestTrigger になります。イベント引数に内包されるオブジェクトにメッセージを格納することで View に情報を伝播させます。

実装例を見てみましょう。ViewModel に InteractionRequest クラスのインスタンスを宣言し、View に InteractionRequestTrigger を配置してイベントを購読します。授受されるオブジェクトのクラスは Prism に用意されている Notification です。アクションには Notification の情報をダイアログにマッピングして表示する処理を実装し、View でトリガーに紐づければ完成です。

```cs:ViewModel
namespace TestApp.ViewModels
{
    public class MainWindowViewModel 
    {
        public InteractionRequest<Notification> MessageRequest { get; }
            = new InteractionRequest<Notification>();

        // このメソッドを呼び出せばダイアログを表示できる
        public void ShowMessage()
            => this.MessageRequest.Raise(new Notification { Title = "タイトル", Content = "メッセージ" });
    }
}
```

```cs:View(Action)
namespace TestApp.Views.Behaviors
{
    public class MessageAction : TriggerAction<DependencyObject>
    {
        protected override void Invoke(object parameter)
        {
            if (!(parameter is InteractionRequestedEventArgs args))
                return;
            if (!(args.Context is Notification context))
                return;

            var owner = this.AssociatedObject as Window ?? Window.GetWindow(this.AssociatedObject);
            var message = context.Content?.ToString() ?? string.Empty:
            MessageBox.Show(owner, message, context.Title, MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }
}
```

```xml:View(Trigger)
<Window x:Class="TestApp.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:p="http://prismlibrary.com/"
        xmlns:behaviors="clr-namespace:TestApp.Views.Behaviors"
        p:ViewModelLocator.AutoWireViewModel="True">
    <i:Interaction.Triggers>
        <p:InteractionRequestTrigger SourceObject="{Binding MessageRequest, Mode=OneTime}">
            <behaviors:MessageAction/>
        </p:InteractionRequestTrigger>
    </i:Interaction.Triggers>
</Window>
```

上記で ViewModel は間接的に画面を操作していますが、View を直接参照していないため、独立性を保っています。Raised イベントをとらえて結果を返すようなテストロジックを組めば、View を介さずに ViewModel のロジックテストを実施できます。このように仲介者を挟んで双方が通信する方法は Messenger パターンと呼ばれたりもします。

## おわりに

InteractionRequest を使った相互作用処理についてまとめました。
今回は Prism での実装でしたが、Livet などほかのフレームワークでは、これらのやり取りをさらにシンプルに実現したものもあります。それぞれ良さがあるので、自分の作り方にあった基盤を取り入れましょう。

実は今回ご紹介した方法は、執筆時点での最新版である Prism 7.2 で非推奨とされました。（使用できないわけではありません。）ただし、この考え方自体は有益であり、汎用性も高く、理解しておいて損はない内容だと思います。次回は新たに採用された相互作用処理の実現方法である DialogService についてまとめます。
[Online] potatotips #69 iOS/Android開発Tips共有会 備忘録

# はじめに
補欠から繰り上がり、[[Online] potatotips #69 iOS/Android開発Tips共有会](https://potatotips.connpass.com/event/171532/)に参加できたので、備忘録を残します。

connpass のイベントページにも記載がありますが、
[github](https://github.com/potatotips/potatotips/wiki/potatotips-69)で今までのイベントのテーマや資料を管理していて、取り組みもとても参考になります。

# 各発表に対する感想
どれも面白い発表でしたが、今回は自分がメインで聞いていたテーマに絞ります:bow:

ML Kit （Auto ML Vison Edge）で写真から家族を検出する
--
[ブログ(スライドは末尾)](https://techblog.timers-inc.com/entry/2020/04/24/143803)

**やりたいこと**

- 写真アプリで家族を検出できるようにする（今回は、大人・子供・猫・犬の区別）
- 使ったのは Firebase ML（ML自体はβ版）

**発表のメモ**

- はじめにやったことは、画像を集める必要がある、とりあえずで25000...
- アップロードする写真は1回は1GB以下に抑える必要がある
- ローカルモデルとリモートモデルで運用を選択できる
- 今回は両刀で運用したが、ローカルとリモートは一長一短があり、ローカルの方が単純な使い方は楽になる
- 学習モデルのサイズは25000枚の画像で4MB程度、18000なら3MB未満だった、レイテンシでサイズの調整もできる

資料にコードのサンプルやFirebaseのキャプチャもあり、
かなり丁寧に画像認識でやること、気をつける事を理解できました。

Troubleshooting on Build & Release Flutter apps
--
[資料](https://t.co/NwcC20y2PL?amp=1)

Flutter で TODO アプリをリリースした話でした。

強く印象に残ったのは1点で...**Social Login 機能がある場合は、sign in with apple が必要**です...

Tips on Databinding in RecyclerView
--
[資料](https://speakerdeck.com/aosa4054/tips-on-databinding-in-recyclerview)

今年の新卒の方が書いて、発表しているということだけで相当刺激を受けました！
内容もさることながら、資料が本当にわかりやすかったです。
AndroidのMVVMあるあるというか、困った時にたどり着きそうです..

ContextThemeWrapperでThemeをより賢く
--
[資料](https://speakerdeck.com/rmakiyama/contextthemewrapperdethemewoyorixian-ku)

Themeの設定はよく使いますが、Themeの差し替えをContextWrapperでやったことはなかったので、
引き出しになりました。

SwiftUIでNeumorphism!!!
--
Neumorphismという単語自体初めてききました...
個人開発も色々やっている方で Neumorphism もそうですが、
自作で様々なアプリやライブラリを作っていることがすごく印象的でした。

# まとめ
69回続く人気のイベントですが、今回初めて参加させていただきました！
iOS / android と違う領域のエンジニアが集まることで普段聞かない話や
いつも聞いている話とはちょっと違う話を聞けて刺激的でした。
ECサイト　商品ランキング機能　実装方法



商品の購入された回数に応じてランキングを表示する方法。

## 前提
・ECサイトの製作
・商品のテーブルをitemとしている
・注文された商品をOrderItemとしている


## コントローラー

```controller.rb
def index
  @rank_items = OrderItem.find(OrderItem.group(:item_id).order('count(quantity) desc').limit(3).pluck(:id))
end
```


```@rank_itemsの詳細説明.rb

OrderItem.group(:item_id) #OrderItemのitem_idが同じものをグループに分ける。
.order('count(quantity) desc') #ここでのorderは順に並べるという意味。descが大きい順にという意味。
.limit(3) #表示する最大数を3個に指定。
.pluck(:id)) #idを取り出す。

OrderItem.find() #pluckで取り出された数字をOrderItemのIDとする。
```


取り出した３つのidをviewで表示しましょう。


## viewページ例

```app/view/index.html.erb

<% @rank_items.each do |rank_item| %>
  <%= attachment_image_tag rank_item.item, :image, size: '200x200',format: "jpeg",fallback: "cake.png",class:"img-responsive" %>
  <p><%= rank_item.name %></p>
  <p><%= rank_item.price %>円</p>
<% end %>
```

each文を使って３つの商品を表示させてます。
#gRPC: vol1:  【 なぜgRPCなのか？ 】

# #gRPC: vol1:  【 なぜgRPCなのか？ 】
https://grpc.io/

gRPCは、リクエスト/レスポンスおよび（非永続的）ストリーミングのユースケース用のトランスポートメカニズムです。

- スキーマファーストのRPCフレームワーク
- プロトコル: [Protocol Buffer](https://developers.google.com/protocol-buffers)
- リクエストとレスポンスはHTTP / 2接続を介してストリーミング

## メリット
- スキーマファーストの設計で、明確に定義され、分離されたサービスインターフェイス
- 互換性のあるスキーマ実現可能
- HTTP / 2
- ストリーミングのリクエストとレスポンスはファーストクラスです。
- さまざまな言語がサポートされている

## 利用目的

- 内部サービス間の接続
- gRPC APIを公開する外部サービスへの接続（他の言語で書かれたものでも）
- Webまたはモバイルのフロントエンドへのデータの提供
【Day63】積み上げ日記

##予定
■Udemy（セクション２０）
－FirebaseとFacebookの連携を理解する

##完了
■Udemy
－FirebaseとFacebookの連携を理解する（40％理解）

##学習時間
５h

##学習ポイント
■ライブラリのインストール方法
cd の後に空白を開ける！

pod init を実行する前の
「 cd コマンドの実行が間違っているので、ディレクトリ移動ができていない」状態。
原因は「 cd の後に 半角空白が無いため 」
 
【❌】 cd/Users/※中略/Facebook
【⭕】 cd /Users/※中略/Facebook
久しぶりにDockerを触るときに忘れているコマンドやオプションたち

# Docker

## docker pull

DockerHub と呼ばれるサービス上で様々なOS・ソフトウェアを公開しており、`pull`コマンドを用いてローカル環境にインストールする

```
$ docker pull ruby
$ docker pull ubuntu
```

などなど...


## docker images

```
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              1d622ef86b13        3 days ago          73.9MB
```

## dockerfile

`docker build`するための手順を書いたファイル

```
#       :[コメント]
FROM    :[イメージ:バージョン]      ... ベースイメージの指定
ENV     :[key] [value]         ... 環境変数の定義
WORKDIR :[作業ディレクトリ]         ... コマンドを実行する際に基準となるディレクトリを設定
COPY    :[ソース指定] [保存先指定] ... Docker内へホストのファイル/ディレクトリをコピー
ADD     :[ソース指定] [保存先指定] ... Docker内へホストのファイル/ディレクトリをコピー DL&解答
RUN     :[コマンド]               ... build時に実行 「yum」や「apt」などのコマンドを実行することが多い
CMD     :[コマンド]               ... コンテナ立ち上げ完了(docker run)時に実行する
VOLUME  :[ホストdir]:[コンテナdir] ... コンテナを実行しているホストのディレクトリを、コンテナ内のディレクトリとして使えるようにする
EXPOSE  :[ポート番号]             ... ポートを解放する アクセスするには docker run 時に-pフラグを使ってポートの公開範囲を指定するか、-Pフラグで全ての露出ポートを公開する必要がある
```

### example) Railsアプリケーション

1. `ruby`イメージからコンテナを作成
2. ポート`3000:3000`を開放し、ボリュームを指定してコンテナを起動
3. コンテナ内でRailsプロジェクトを作成
4. コンテナに`node.js`をインストール
5. `Rails server`を実行

`dockerfile`を書くということは、これらの作業を文書化していくということ

```dockerfile
FROM ruby:2.6.0

ENV APP_PORT /usr/src/sample_app

WORKDIR ${APP_PORT}

RUN apt-get update && \
    apt-get install -y nodejs

COPY Gemfile ${APP_PORT}
COPY Gemfile.lock ${APP_PORT}

RUN bundle install

EXPOSE 3000

CMD bundle exec rails server
```



## docker build

Dockerfile から独自のイメージをビルドするためのコマンド

```
$ docker build -t 名前:タグ Dockerfileのあるディレクトリ
$ docker build -t name:1.0 .
```

`.`はCOPYコマンドを実行する際にどのディレクトリを起点とするかを指定する


## docker run

```
$ docker run -P nginx
```
nginx イメージを起動し、設定されているポートを公開する

```
$ docker run ubuntu cat /etc/issue
```
ubuntu イメージを起動し、`cat /etc/issue` コマンドを実行する

```
$ docker run -it ubuntu bash
```
bash のような対話的な入力が必要なプロセスも実行する

```
$ docker run -it --name TEST -v "$PWD":/usr/src/app -p 3000:3000 ruby /bin/bash
```
ruby イメージからコンテナを起動

### オプション

```
-i     :ホストの入力をコンテナの標準出力をつなぎ、対話するようにシェルなどでコンテナ内で操作ができる
-t     :コンテナの標準出力とホストの出力をつなげる （`-it`のようにまとめて使うことが多い）
-d     :デタッチモードでコンテナを起動する　コンテナ内に入らず、バックグラウンドで動作させることができる
-p     :ホスト側のポート番号:コンテナのポート番号
-P     :設定されているポートを公開
-v     :ホスト側のディレクトリパス:コンテナ側のディレクトリパス　dockerコンテナとホスト間でファイルを共有する
-link  :コンテナ識別子:エイリアス　dockerコンテナ同士を繋ぐ
-rm    :コンテナ内でのコマンドの実行が終わったらコンテナを自動で削除する
--name :コンテナに任意の名前をつける

/bin/bash :コンテナ起動後に実行するコマンド　今回はシェルを指定
cat /etc/issue :コンテナ後に実行するコマンド　OS名やマシンタイプ、OSのリリースなどを確認するコマンド
```

## docker ps

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
1a487d1d8e0b        nginx               "bash"              9 seconds ago       Up 8 seconds        80/tcp              xenodochial_mendeleev
```

## docker exec

起動したコンテナの中に入る

```
$ docker exec -ti 1a487d1d8e0b bash
root@1a487d1d8e0b:/# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var
```
C#でWin10の壁紙を変更する方法

# 概要
以前、デスクトップの背景画像をC#から変えたくなったことがあるので備忘録としてメモ

# 検証環境

- Windows 10 Pro
- Visual Studio Enterprise 2019
- .NET Core 3.1

# ソースコード
初めにWin32 APIを読み込むために`DLLImport`で`user32.dll`を呼びましょう

~~~ Program.cs

using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace TestProject
{
    class Program
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool SystemParametersInfo(uint uAction, uint uParam, string lpvParam, uint fuWinIni);

        private const uint SPI_SETDESKWALLPAPER = 0x0014;
        private const uint SPIF_UPDATEINIFILE = 1;
        private const uint SPIF_SENDWININICHANGE = 2;

        // 壁紙にしたい画像のパス
        private readonly static string exefolder = Directory.GetCurrentDirectory();
        private static readonly StringBuilder img = new StringBuilder(@$"{exefolder}\test.png");

        static void Main(string[] args)
        {
            SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, img.ToString(), SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
        }
    }
}

~~~

`SystemParametersInfo`の引数の詳細については[SystemParametersInfoA function](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa#parameters)に記載されています

**Note** Windows 8 以降のOSは画像を指定する際に絶対パスを指定しなければいけないらしい（すいません確認とれてません）

# 参考

[SystemParametersInfoA function](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa#parameters)
git commit vimじゃない vimに変更

git commit 時にvimじゃないエディタが開いたらびっくりしますよね(ついさっきした)
その際の簡単な対処法メモを残しておきます

## `vim` じゃないエディタの終了方法
- nano: `ctrl + x` と入力

- (emacs: `ctrl + x` `ctrl + c` と入力)

## `git commit` 時に開くエディタを `vim` に変更
### コマンド
下記コマンドで`vim`に変更できます

```
git config --global core.editor "$(which vim)"
```

### 簡単な説明
`git config --global core.editor "/path/to/editor"`
Git のデフォルトエディタを設定する

`which COMMAND`
COMMAND のフルパスを表示する

`$(COMMAND)`
COMMAND をサブシェルで実行し、その実行結果が文字列(コマンド)として出力/実行される

## 参考ページ
https://shellscript.sunone.me/variable.html
vtkXMLUnstructuredGridReaderまとめ（随時更新）

# vtkXMLUnstructuredGridReaderについて

classリファレンス 
https://vtk.org/doc/nightly/html/classvtkXMLUnstructuredGridReader.html
欲しい機能がない場合は親クラスである`vtkXMLUnstructuredDataReader`も調査するとよいでしょう。


`vtkXMLUnstructuredGridReader`を用いることで、`vtm形式`のファイルをvtkライブラリで読みこむことができます


今回はOpenFOAMの結果を用いてvtuファイルを作成します

``` terminal:terminal
$ git clone https://github.com/matsubaraDaisuke/vtk-data-set.git
$ cd openfoam_case
$ foamTovkt -ascii
```

`openfoam_case`の直下に`VTK`が作成され、`OpenFOAM`の出力時間ごとにフォルダが作成されます。`vtmファイル`は、そこフォルダ内にあります。


### 基本コード

``` python
import vtk

# reader
reader = vtk.vtkXMLUnstructuredGridReader () 
reader.SetFileName("VTK/src_283/internal.vtu"); # vtuファイルのパス
reader.Update()

# filter
filter = vtk.vtkGeometryFilter()
filter.SetInputConnection(reader.GetOutputPort())
filter.Update()

# mapper
mapper = vtk.vtkCompositePolyDataMapper2()
mapper.SetInputConnection(filter.GetOutputPort()) #mapperにfilterを設定

# actor
actor = vtk.vtkActor()
actor.SetMapper(mapper)             #actorにmapperを設定
actor.GetProperty().EdgeVisibilityOn()

# renderer
renderer = vtk.vtkRenderer()
renderer.AddActor(actor)            #rendererにactorを設定

##背景色の設定
renderer.GradientBackgroundOn()      #グラデーション背景を設定
renderer.SetBackground2(0.2,0.4,0.6) #上面の色
renderer.SetBackground(1,1,1)        #下面の色

#Window
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(renderer)         #Windowにrendererを設定
iren = vtk.vtkRenderWindowInteractor();
iren.SetRenderWindow(renWin);
renWin.SetSize(850, 850)
renWin.Render()
iren.Start();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132299/15afd27e-7f27-590b-ea78-a20fa7ce8de9.png)


printで情報を見ることができます

``` python
print(reader)
>>
vtkXMLUnstructuredGridReader (0x7ff031cf9b50)
  Debug: Off
  Modified Time: 9623
  Reference Count: 2
  Registered Events: (none)
  Executive: 0x7ff031cf3c90
  ErrorCode: Undefined error: 0
  Information: 0x7ff031ce3700
  AbortExecute: Off
  Progress: 1
  Progress Text: (None)
  FileName: VTK/src_283/internal.vtu
  CellDataArraySelection: 0x7ff031ce3b20
  PointDataArraySelection: 0x7ff031cef0e0
  ColumnArraySelection: 0x7ff031cef0e0
  Stream: (none)
  TimeStep:0
  NumberOfTimeSteps:0
  TimeStepRange:(0,0)

```

## メソッド
実際には`vtkXMLUnstructuredDataReader`に実装されています。

### GetNumberOfPoints
部品数？の取得

``` python
reader.GetNumberOfPoints() 
>> 1
```

### GetNumberOfCells
セル数の取得

``` python
reader.GetNumberOfCells () 
>> 12225
```

### GetNumberOfPoints 
節点数の取得

``` python
GetNumberOfPoints 
>> 25012
```
OkHttpで認証付きのプロキシ通信を行う

Androidだと、認証なしのプロキシの場合は端末の設定画面から設定することができますが、認証付きのプロキシは端末の設定画面から設定することができません。（ユーザー名とパスワードを入力できない。）

今回どうしても認証付きのプロキシで通信を行いたかったので
、色々と調べてみたのですが、OkHttpでサクッと対応できてしまったのでメモがてら投稿。

# `proxyAuthenticator` を使う


`OkHttpClient.Builder` の `proxyAuthenticator` で認証情報を設定します。

下記サンプルコードでは、ユーザー名 `username` 、パスワード `password` としています。

```kotlin

val client = OkHttpClient.Builder()
    .proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("http://example.com", 3128)))
    .proxyAuthenticator(object : Authenticator {
        override fun authenticate(route: Route?, response: Response): Request? {
            val credential = Credentials.basic("username", "password")
            return response.request.newBuilder()
                .header("Proxy-Authorization", credential)
                .build()
        }
    })
    .build()

```


あとはこの `client` をRetrofitとかで使うようにすればOKです。

# 参考
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-authenticator/
AtCoder 生活　37日目

[前回](https://qiita.com/flandrescarlet237/items/35f739bfdecbcf88e287)

#37日目
今回は、[AtcoderProblemsのBoot camp for Beginners](https://kenkoooo.com/atcoder/#/training)のEasy問題を5問解きました

|問題|難易度|自力で解けた|かかった時間|
|:-:|:-:|:-:|---|---|
|AGC002|A|◯|13分50秒|
|ABC124|C|◯|9分14秒|
|ABC090|B|◯|17分33秒|
|AGC024|A|✕|70分36秒|
|ABC079|B|◯|8分16秒|

##[AGC002_A](https://atcoder.jp/contests/agc002/tasks/agc002_a)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

int main(){
    ll a,b;
    cin >> a >> b;
    if(a > 0) {
      	cout << "Positive" << endl;
      	return 0;
    }
  	else if(a <= 0 && 0 <= b){
    	cout << "Zero" << endl;
      	return 0;
    }
    int c = a - b;
    if(c%2 == 0) cout << "Negative" << endl;
    else cout << "Positive" << endl;
}


```

解くのにかかった時間：13分50秒

##[ABC124_C](https://atcoder.jp/contests/abc124/tasks/abc124_c)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

int main(){
    string s;
    cin >> s;
    int cnt = 0;
    if(s.size() == 1) {
        cout << 0 << endl;
        return 0;
    }
    rep(i,s.size()-1){
        if(s[i] == s[i+1]) {
            cnt++;
            if(s[i+1] == '0') s[i+1] = '1';
            else s[i+1] = '0';
        }
    }
    cout << cnt << endl;
}

```

解くのにかかった時間：9分14秒


##[ABC090_B](https://atcoder.jp/contests/abc090/tasks/abc090_b)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

int main(){
    int x , y;
    cin >> x >> y;
    int cnt = 0;
    for(int i = x;i <= y;i++){
        int a = i/10000;
        int b = i%10000/1000;
        int c = i%100/10;
        int d = i%10;
        if(a == d && b == c) cnt++;
    }
    cout << cnt << endl;
}

```

解くのにかかった時間：17分33秒


##[AGC024_A](https://atcoder.jp/contests/agc024/tasks/agc024_a)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

int main(){
    ll a,b,c,k;
    cin >> a >> b >> c >> k;
    if(k%2 == 0)  cout << a-b << endl;
    else cout << b-a << endl;
}

```

解くのにかかった時間：70分36秒
感想：ループを使わずに解ける問題であることに気づけなかった。


##[ABC079_B](https://atcoder.jp/contests/abc079/tasks/abc079_b)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 2;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

int MAX = 2e9;

int main(){
    int n;
    cin >> n;
    vector<int> l(MAX);
    l[0] = 2;
    l[1] = 1;
    rep(i,n+1){
        l[i] = l[0] + l[1];
    }
    cout << l[n] << endl;
}

```

解くのにかかった時間：8分16秒


###最後に
数学的に考えると簡単になる問題を簡単に解けるようになりたい


|難易度|かかった平均時間|解けた問題数|
|:-:|---|---|
|A|13分50秒|2問中1問|
|B|12分39秒|2問中2問|
|C|9分14秒|1問中1問|
DISK2VHDをUEFI環境で利用する方法

#はじめに
DISK2VHDはBIOSのみ対応だが、EFIシステムパーティションにドライブレター割り当てることでUEFIでも利用可能であった

#方法
コマンドプロンプトで次のコマンドラインを実行し、EFIシステムパーティションに任意のドライブレター割り当て

```
MOUNTVOL X: /S
```

※　/S          EFI システム パーティションを与えられたドライブにマウントします。


Disk2vhdを起動し、Use Volume Shadow Copyのチェックを外す(オフにしないとエラーになる)

下記チェックを入れる
回復パーティション
C:ドライブ
X:ドライブ
その他VHDX化したいドライブがあれば

できたイメージを第2世代仮想マシンとしてHyper-V上で稼働させる
VSSが利用できないので、SQLサーバとかは整合性が保てなくなる可能性があるのかな
【JavaScript】配列から重複をなくす方法

## はじめに
コロナウイルスにより圧倒的に暇となってしまったため、なんとなくで競プロをはじめてみました。
大学生の頃に少しかじっていましたが、な〜んにも覚えてません（笑）

仕事でに少しでも役に立ったら嬉しいな〜と思ったので、JavaScriptでやっています。
学生の頃にPython使ってたので、そのうち乗り換えるかもしれない。

JavaScriptで競プロを行う上で、様々なメソッドを使うようになりました。
正直あんまりWeb開発で使わないな〜って漢字のメソッドもたくさん出てきますね！
その中で、配列から重複をなくせる**filter**について記事でまとめます！

## filterの使い方
解説と言っても非常にシンプルです。

```js
nums = [3 ,5, 7, 7, 8 , 9 ,8]

// num: 各要素, i: index
const result = nums.filter((num, i) => nums.indexOf(num) === i)

console.log(result)

// [3, 5, 7, 8, 9]
```

indexOfとは、配列の中で引数に渡した値を探し、一番最初に見つかったindexを返します。
つまり、filterの中でやっていることを詳しく書いていくと、

```js
// nums = [3 ,5, 7, 7, 8 , 9 ,8]

nums.indexOf(3) === 0
// indexが一致するからヨシ！

nums.indexOf(5) === 1
// indexが一致するからヨシ！

nums.indexOf(7) === 2
// indexが一致するからヨシ！

nums.indexOf(7) === 3
// ここではじめて弾かれます。
// nums.indexOf(7)で返ってくる値は'2'
// indexが一致しないからダメ！

nums.indexOf(8) === 4
// indexが一致しないからダメ！

nums.indexOf(9) === 5
// indexが一致するからヨシ！

nums.indexOf(8) === 6
// indexが一致しないからダメ！
```

## おしまい
競プロむずい
けど暇だし頑張る