OmniAICreator/Qiita-1.07M huggingface dataset:
Docker公式イメージのmysqlの文字コードを変更する方法

#はじめに
Dockerを使ってrailsでSNSアプリを作成していたところ、日本語をmysqlデータベースに保存しようとするとエラーがでてしまいました。原因を模索していると、mysqlの文字コードがデフォルトでlatin1になっていたためでした。

latin1は日本語未対応みたいですね。
参考：[Latin-1のアスキーコード表](http://charset.7jp.net/ascii.cgi)
#mysqlの文字コードを日本語対応のutf8mb4に変更する
DockerHubからmysql公式イメージのDescriptionによると、
引用元：[DockerHub mysqlについてのDescription](https://hub.docker.com/_/mysql/)
>Configuration without a cnf file
Many configuration options can be passed as flags to mysqld. This will give you the flexibility to customize the container without needing a cnf file. For example, if you want to change the default encoding and collation for all tables to use UTF-8 (utf8mb4) just run the following:
>
```
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
```

本来mysqlの文字コードを設定する場合、mysqlを設定するcnfファイルを編集する必要があるが、cnfファイルの編集をせず、上記のようにコマンドを実行することでmysqlの文字コードをカスタマイズできるようですね。
#mysqlの文字コード設定方法
docker-compose.ymlファイルを以下のように編集しました。

```:Docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    environment:
      MYSQL_DATABASE: with_exo_development
      MYSQL_ROOT_PASSWORD: dbpass
      MYSQL_USER: dbuser
      MYSQL_PASSWORD: dbpass
    ports:
      - "3306:3306"

  web:
    build:
      context: .
      dockerfile: Dockerfile
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    depends_on:
      - db
    ports:
      - "3000:3000"
    volumes:
      - .:/app
```
dbのcommand部分を追記しました。
#最後に
今回、文字コードをutf8ではなくutf8mb4にした理由は、絵文字もデータベースに保存できるようにするためです。

参考記事：[Docker公式イメージのMySQLで文字コードを指定する](https://qiita.com/neko-neko/items/de8ea13bbad32140de87)
R 4.1の新しいpipe `|>`の使い方メモ

R 4.1からデフォルトでパイプ演算子`|>`が使えるようになりました。これは`tidiverse`で使われてた`magrittr`由来のパイプ演算子`%>%`とは微妙に仕様が異なります。使う側から見て一番大きな違いは`.`を使ってオブジェクトを渡せないことかなと思います。いくつかメモを残します。

# 基本


まずは従来のpipe`%>%`を使った書き方。

これは動く。

```other
# 😀
rnorm(100) %>% hist
```


これも動く。

```other
# 😀
rnorm(100) %>% hist()
```


こいつも動く。

```other
# 😀
rnorm(100) %>% hist(.)
```


新しいpipe`|>`を使った書き方。

`function()`の形が必要なので、これは動かない。

```other
# 😰
rnorm(100) |> hist
```


これは動く。

```other
# 😀
rnorm(100) |> hist()
```


そして、これは動かない。`.`を使ってオブジェクトを渡せないため。

```other
# 😰
rnorm(100) |> hist(.)
```


# ggplot2


`.`が使えないため、もちろんこれは動かない。

```other
# 😰
tibble(x = rnorm(100), y = rnorm(100)) |>
  ggplot(., aes(x, y)) +
  geom_point()
```


どうやるのかというと、データを指定しないでOK。

```other
# 😀
tibble(x = rnorm(100), y = rnorm(100)) |>
  ggplot(aes(x, y)) +
  geom_point()
```


# 他の例：回帰


まずは`%>%`をつかった回帰。

```other
# 😀
tibble(x = rnorm(100), y = rnorm(100)) %>%
  lm(y ~ x, data = .)
```


`.`が使えないので、とりあえず下記のように書くことはできる。

```other
# 😰
tibble(x = rnorm(100), y = rnorm(100)) |>
  lm()
```


しかし、これだとxをyで回帰した結果が出てきてしまう。じゃあ、どうやるのかというと、下記のようなラムダ式（無名関数）を使って、

```other
  \(x) x + 1 is parsed as function(x) x + 1.
```


こんな感じ。

```other
# 🙄
tibble(x = rnorm(100), y = rnorm(100)) |>
  {\(d) lm(y ~ x, data = d)}()
```


なので、`%>%`のほうが使いやすい印象。

そもそも、`|>`を使う利点は？というと、`|>`は爆速らしい ([The New Base Pipe](https://michaelbarrowman.co.uk/post/the-new-base-pipe/))。

# 参考


[The New Base Pipe]([https://michaelbarrowman.co.uk/post/the-new-base-pipe/](https://michaelbarrowman.co.uk/post/the-new-base-pipe/))

[CHANGES IN R 4.1.0 NEW FEATURES](https://developer.r-project.org/blosxom.cgi/R-devel/NEWS/2021/04/21#n2021-04-21)
groupby関数で出来たマルチカラムをいい感じにするTips

# やりたいこと

データ分析実務で、`groupby`関数を使ってあるカラムに対して複数の集約（mean,maxなど）を行っていると、マルチカラムとなってしまい、データが扱いにくくなることがあります。


たとえばこんなコード

 ```python:
df_receipt.groupby("customer_id").agg({"sales_date":["max", "min"]})
```
こんなの嫌だ・・・:disappointed_relieved:
![スクリーンショット 2021-07-03 13.47.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327398/b9cdc4e5-1a2b-ea0c-b4f2-a399934c1b17.png)


こんな感じにしたい…！:satisfied:
![スクリーンショット 2021-07-03 13.43.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327398/24d28a33-ad09-e1ca-3baa-0b95e49ae078.png)


# やり方

やり方は以下の４ステップ。

1. マルチカラムとなっているカラム以外は、indexに退避（`set_index`関数）
1. マルチカラム→シングルカラムにする（`droplevel`関数）
1. 集約化されたカラムに、いい感じのprefix or suffix をつける（`add_prefix` or `add_suffix`関数）
1. indexに退避していたカラムを元に戻す（`reset_index`関数）  

※ `groupby`関数を使っていると、groupby対象カラムはindexとなるので、1.は要らない場合あり

コードサンプル

```python:
# 同じカラムで複数の集約を行うときはlistで表現
tmp = df_receipt.groupby("customer_id").agg({"sales_ymd":["max", "min"]})

# マルチカラムとなっているので削除
# droplevelの対象にしたくないカラムは、indexに退避させておくのがミソ
tmp.columns = tmp.columns.droplevel(0)

# カラム名を修正して、indexを戻してあげる
tmp = tmp.add_suffix("_sales_date").reset_index()
```

# まとめ
`groupby`関数はデータ分析でよく使うので、どんどん仲良くなっていきたいですね:kissing_closed_eyes:
GCPのCDCサービス、DataSreamとはなんなのか？

最近、GCPのサービスとしてdatasreamを知り、結構便利なのでは？と思ったので纏めてみたいと思います。

# CDCとは？
datasreamはCDCのサービスですが、このCDCは change data capture といい、簡単にデータが変更された差分だけを保存する機能です。何かしらの一意のキーを元に変更されたレコードや新規に追加されたレコードを検出することで、変更分だけのデータを取得します。

具体的には 変更した日付(LAST_UPDATE)やテーブルカラムにバージョンをつけて管理する(VERSION_NUMBER)などですね。

このCDCの概念自体は元から存在していたのですが、デザインパターンレベルだったので利用するためには実際に実装する必要がありました。

#Datastream
GCPのdatasreamは先ほど述べた通りCDCのサービスなのですが主に以下のメリットがあります。

* サーバーレスで利用できるので、管理する部分が非常に小さい
* 簡単にセットアップができる
* GCPの別サービスに連携できる。

などがあります。特にGCPと連携できるというのが結構強力で、CloudSQLやSpannerなどのデータをcloud storageに置くことができたり、Dataflowというストリームデータの分析サービスと連携させて、DBのデータを加工させて、BigQueryに入れるという芸当も可能です。

#Datastreamの構成要素
Datastreamには主に三つの構成要素があり

### Private connectivity configurations
VPCなどのプライベートネットワークへ接続するための設定です。
DBなどは機密情報が入っており大抵の場合はプライベートネットワークに設置されているので大抵の場合は使うことになります。

### Connection profiles
接続する先とその情報を送る先の情報を設定する部分です。

### Streams
実際にデータをCDCを使って差分データ抽出し転送するときに使います。

# 最後に
なかなかに便利そうなのですが、2021/07/03現在postgresがサポートされておらず、そこが残念なところです。
とはcoming soonと書いているので気長に待ちたいと思います。

それでは良いdatastreamライフを
SQL DeveloperのPL/SQLデバッグがうまくいかない場合のトラブルシューティング


SQL DeveloperはPL/SQLのデバッグ実行ができるのは便利なのですが、バージョンアップの度にセキュリティがどんどん厳しくなり、最近は簡単にはデバッガが動かせないようになっているようです。
最近、久しぶりにSQL DeveloperのPL/SQLデバッグをする機会があって、動かせるようになるまでに結構時間がかかったので、対応方法をまとめておきました。

# ORA-01031: 権限が不足しています。

```エラーメッセージ
ORA-01031: 権限が不足しています。
ORA-06512: "SYS.DBMS_DEBUG_JDWP", 行68
ORA-06512: 行1
このセッションでは、DEBUG CONNECT SESSIONおよびDEBUG ANY PROCEDUREユーザー権限が必要です。
```

11gから(?)PL/SQLのデバッグを実行するにはユーザ権限が必要になったようです。表示のとおりデバッグ実行ユーザの権限「DEBUG CONNECT SESSION」「DEBUG ANY PROCEDURE」が足りませんので、(権限を付与できるユーザで接続して)これらの権限をGRANT文でデバッグ実行ユーザに付与します。

```
SQL> GRANT DEBUG CONNECT SESSION TO デバッグ実行ユーザ名;
SQL> GRANT DEBUG ANY PROCEDURE TO デバッグ実行ユーザ名;
```

不要になった権限の削除はREVOKE文で行います。

```
SQL> REVOKE DEBUG CONNECT SESSION FROM デバッグ実行ユーザ名;
SQL> REVOKE DEBUG ANY PROCEDURE FROM デバッグ実行ユーザ名;
```

# ORA-24247: アクセス制御リスト(ACL)によりネットワーク・アクセスが拒否されました。

```エラーメッセージ
ORA-24247: アクセス制御リスト(ACL)によりネットワーク・アクセスが拒否されました。
ORA-06512: "SYS.DBMS_DEBUG_JDWP", 行68
ORA-06512: 行1
```
12cから、ホスト・アクセス制御リストによる制御で、デフォルトではデバッグできないようになっているようです。

以下のPL/SQL(DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE)を実行してホスト・アクセス制御リストにアクセス許可情報を追加することで解決できます。

```
BEGIN
 DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE
 (
    host => '*.*.*.*', --接続を許可するIPやホスト(*.*.*.*は全ての接続を許可)
    lower_port => null,
    upper_port => null,
    ace => xs$ace_type(privilege_list => xs$name_list('jdwp'), 
    principal_name => 'デバッグユーザ名', 
    principal_type => xs_acl.ptype_db)
 );
END;
/
```
ホスト・アクセス制御リストの状態はDBA_HOST_ACESから確認することができます。

```
SQL> SELECT * FROM DBA_HOST_ACES;
```

アクセス許可情報を削除するにはBMS_NETWORK_ACL_ADMIN.REMOVE_HOST_ACEを使います。

```
BEGIN
 DBMS_NETWORK_ACL_ADMIN.REMOVE_HOST_ACE
 (
    host => '*.*.*.*', --接続を許可するIPやホスト(*.*.*.*は全ての接続を許可)
    ace => xs$ace_type(
        privilege_list => xs$name_list('jdwp'), 
        principal_name => 'デバッグユーザ名', 
        principal_type => xs_acl.ptype_db)
    )
 );
END;
/
```

参考:  [SQL Developerでデバックできないときの解決策](http://ichannel.tokyo/technoracle/sql-developer%E3%81%A7%E3%83%87%E3%83%90%E3%83%83%E3%82%AF%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%A8%E3%81%8D%E3%81%AE%E8%A7%A3%E6%B1%BA%E7%AD%96/3115/)

# ORA-30683: デバッガへの接続の確立で障害が発生しました。

```エラーメッセージ
ORA-24247: ORA-30683: デバッガへの接続の確立で障害が発生しました。
ORA-12541: TNS: リスナーがありません。
```


https://haradago.hatenadiary.org/entry/20070223/p1

SQL Developerのメニューバー[ツール]→[プリファレンス]を選択して、[デバッガ]の[データベース・デバッグのデバッガ・ホストをプロンプト]にチェックを入れます。

![preference.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408176/067020f1-3a16-ae45-0eac-eef647abd359.png)

デバッグを実行すると[デバッガ・ホスト]のプロンプトが出たらクライアントのIPアドレスを入力すると実行できます。

それでも解消しない場合はクライアントPCのファイアウォールが悪さをしている可能性がありますので、ファイアウォールを解除します。

参考:  [oracle sql developer のデバッグ機能 | ぶりりあんとろーど(仮)](https://haradago.hatenadiary.org/entry/20070223/p1)


# デバッグの実行

ターゲットとなるプロシージャ、ファンクション、パッケージを選択して、デバッグの赤いアイコンをクリックします。[PL/SQLのデバッグ]ダイアログが開くので、(パッケージの場合は[ターゲット]でプロシージャ、ファンクションを選択して)、パラメータを設定して実行します。

なお、ステップ実行を行うためにはデバッグ用にコンパイルする必要があります。SQL Developerであれば対象のプロシージャ、ファンクション、パッケージ選択して右クリックのポップアップメニューで[デバッグ用にコンパイル]を選択すればOKです(パッケージ内ののプロシージャ、ファンクションをステップ実行する場合はパッケージ・ボディもデバッグ用にコンパイルする必要があります)。

詳しいデバッグの方法はこちらがよくまとまっているのでご参照ください。
 [Oracle SQL Developerのデバッグの方法 | ITSakura](https://itsakura.com/sqldeveloper-debug)
truncateを使用した表示文字数制限をしようとしたら上手く行かなかった件

# やろうとしたこと

環境：Ruby on Rails 6.1.3.1

データベースに保存されている文字列をviewに表示する際に、表示される文字数に制約をかけて表示したい。

例：

```
(controller)

def show
      @title = あいうえおかきくけこさしすせそ
end

```
```
(view)
<%= @title %>  => (表示したい内容)あいうえお...

<!-- 先頭5文字だけ表示したい。省略されていたら「...」をつける -->
```


文字数の表示制限をするために、trancateというメソッドがあるらしいことを知った
https://qiita.com/ishidamakot/items/2e74d980b3a338e4c784

```
<%= @title.trancate(5) %>
```



# 実際にはどうなった？
```
<%= "abcdefg".truncate(6)  %>  => abc...(3文字まで)
<%= "あいうえおか".truncate(6)  %> => あいうえおか(6文字以上？)
<%= "あいうえおかき".truncate(6)  %>　=> あいう...(3文字まで)
```
? truncateの値は変わっていないのに、与えられる文字列の長さによって、文字列の省略が起きたり起きなかったりする
truncateの仕様を理解できていないので、挙動を予想することができなかった

# どうやって解決したか

truncateはメソッドの内部で文字のbyte数を計算して文字数を数えて処理を行っているらしい。
truncateメソッドは利用せず、byte数を使って文字数の表示制限を試みる。


```
<%="abcdefg".byteslice(0, 3).scrub('')  %> => abc

<%= "あいうえおかき".byteslice(0, 3).scrub('')  %> =>  あ

```


全角は一文字あたり3byteなので文字数x３の数値を指定する。

これで、文字数で表示制限ができる。	
(予想通りの挙動を実現することができた)

参照
https://techacademy.jp/magazine/19631


# 省略したら「・・・」をつける
バイト数18以上なら省略をするコード

```
xxx.html.erb
<!-- controllerから渡される変数 title -->
 <% title = "なにかしらの文字列" %>
 
 <%if title.bytesize > 18 %>
 	<%= title.byteslice(0, 18).scrub('')+"..." %>
 <% else %>
 	<%= title %>
 <% end %>
```
 
# 次考えること

半角全角が混同する場合の処理については今度考える。
【Flutter/Dart】Map<String, num>型をMap<String, double>/Map<String, int>にキャストする方法

###前提
業務でFlutter×Firebaseでアプリ開発を行なっています。

DBはFirestoreを利用しているのですが、仕様上数値型の扱いが不安定なことがあり
Firestoreに格納するデータはnum型に統一し、アプリでint/doubl型にキャストする、という手法を採用しています。

単一のデータのキャストはシンプルな処理ではあるのですが、
Map型をキャストしたい際にやや手こずったため、備忘録として残します。

###結論

以下で解決します。

```dart

// 宣言
Map<String,num> examples;

// Mapに含まれているkeyのリストのkeysとvalueのリストのvaluesをそれぞれ取り出し、
// valuesに対してtoDouble()でキャストします。

Map.fromIterables(examples.keys,
              examples.values.toList().map((e) => e.toDouble()).toList())

```

```dart
// Map.fromIterablesの定義はこちら

Creates a map associating the given [keys] to the given [values].

The map construction iterates over [keys] and [values] simultaneously, 
and adds an entry to the map for each pair of key and value.

If [keys] contains the same object multiple times, the value of the last occurrence overwrites any previous value.

The two [Iterable]s must have the same length.

The created map is a [LinkedHashMap]. A LinkedHashMap requires the keys to implement compatible operator== and hashCode. It iterates in key insertion order.

List<String> letters = ['b', 'c'];
List<String> words = ['bad', 'cat'];
var map = Map.fromIterables(letters, words);

```

###参考

https://stackoverflow.com/questions/22435366/dart-a-nicer-way-of-mapping-maps
M1Macでrbenv install 2.7.2実行時に遭遇したエラー

### やりたいこと
rbenv install 2.7.2を実行して
Ruby2.7.2をインストールしたい

### 環境
Ruby 2.6.5
M1チップ搭載MacOS

### 前提条件
Homebrewインストール済
rbenvインストール済

### 発生したエラー

```
Downloading openssl-1.1.1i.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242
Installing openssl-1.1.1i...
Installed openssl-1.1.1i to /Users/yunyun_jp/.rbenv/versions/2.7.2

Downloading ruby-2.7.2.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.bz2
Installing ruby-2.7.2...
ruby-build: using readline from homebrew

BUILD FAILED (macOS 11.4 using ruby-build 20210119)

Inspect or clean up the working tree at /var/folders/s2/bcsz8yb94990dsrrg332gxw40000gn/T/ruby-build.20210702152126.84842.ESLM2k
Results logged to /var/folders/s2/bcsz8yb94990dsrrg332gxw40000gn/T/ruby-build.20210702152126.84842.log

Last 10 log lines:
compiling date_parse.c
installing default console libraries
compiling date_strftime.c
compiling date_strptime.c
linking shared-object objspace.bundle
linking shared-object io/nonblock.bundle
linking shared-object io/console.bundle
linking shared-object nkf.bundle
linking shared-object date_core.bundle
make: *** [build-ext] Error 2
```

### 解決方法

以下のコマンドで解決しました。

```
arch -arm64 rbenv install 2.7.2
```

成功時のログ

```
Downloading openssl-1.1.1i.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242
Installing openssl-1.1.1i...
Installed openssl-1.1.1i to /Users/yunyun_jp/.rbenv/versions/2.7.2

Downloading ruby-2.7.2.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.bz2
Installing ruby-2.7.2...
ruby-build: using readline from homebrew
Installed ruby-2.7.2 to /Users/yunyun_jp/.rbenv/versions/2.7.2
```
JavaMail 1.6.2以上で特殊なメールアドレス（RFC822違反アドレス）に送ろうとするとAddressExceptionが発生する件

検索してもまったく情報出てこなかったので、需要があると信じて。

## なにが起きたか

JavaMail 1.6.2以上で、特殊なメールアドレス（2連続のドット「..」が使用されていたり、@マークの直前でドット「.@」を設定しているメールアドレス）に対するバリデーションが追加されており、この条件のメールアドレスにメール送信しようとすると必ずAddressExceptionが発生する。

(1.6.4からはJakarta Mailと名前が変わってますが、この記事では古い名称のJavaMailで呼ぶことにします。)

わたしはSpring Bootの2.1系でspring-boot-starter-mailを使った際に、JavaMail 1.6.4が使われていて、この例外に遭遇しました。

### 参考
問題になるメールアドレスの説明はこちら。

- ドコモさんの[特殊な形式のアドレス（RFC違反アドレス）のご利用について](https://www.nttdocomo.co.jp/service/docomo_mail/rfc_add/)
- メールアドレスの例は、 `hoge.@docomo.ne.jp`など。


## 回避策

ワークアラウンドとして、考えられる案は次の通り。

- JavaMail 1.6.1以下を使う
- ほかのライブラリをさがす（ただ、わたしは候補見つけられていません）
- JavaのアプリでSMTPでメール送信を行わず、メール送信処理をAPI経由で実現する
  - クラウドサービスで使えるところはないか探す、あるいは自前で作る。自前の場合はAPI経由ではなく、Shared Databaseで連携してもよい。

## 他に考えられる対応

- そもそも新規サービスなら、登録時にRFC違反のメールアドレスをバリデーションエラーにする。
  - その際はどのルールに引っかかっているか説明し、メールアドレスの変更までお願いできるととてもよい。
- 昔からあるサービスで、すでに特殊なメールアドレスでの登録がある場合は、メールアドレスのお願いをしてみる（ただし、効果はないかもしれない）

## JavaMailのコードの説明

2018年に加えられた変更で、JavaMailのInternetAddressクラスのバリデーションが厳しくなった。
このクラスはJavaMailでメールアドレスを表現するバリューオブジェクトらしい。

JavaMail 1.6.2 のコードを見てみると、本事象に関係するのはバリデーションをかけてる箇所のうち、特に[このあたり](https://github.com/javaee/javamail/blob/JAVAMAIL-1_6_2/mail/src/main/java/javax/mail/internet/InternetAddress.java#L1351)や[ここ](https://github.com/javaee/javamail/blob/cb7bd7e096d480731b4e28e1ba3ab42f2bc1826a/mail/src/main/java/javax/mail/internet/InternetAddress.java#L1360) 。


本件が関係していると、例外のメッセージとしては、"Local address contains dot-dot"や
"Local address ends with dot"あたりが出てくると思われる。

このバリデーションは、checkAddressメソッドのvalidate引数(なお、バージョン1.6.3以降では、strict引数）に関係なくバリデーションがかかる。そのため、無効化したり、回避する方法がないように見える。つまり、メソッドレベルで無理なので、設定でも回避できなさそうに思われる。

そのため、とりあえず回避するなら、このバリデーションが実装されてないバージョンである1.6.1に下げるか、JavaMailを使わないという選択肢になると考えられる。

余談ですが、JavaMailは1.6.3より Eclipse EE4J Projectに移管されており、1.6.4より名称も[Jakarta Mail](https://eclipse-ee4j.github.io/mail/) と変わっています
セル関数のCOUNTIFSの使い方の応用

# 例題
成績表のうち、100点を1科目でも取った生徒のすべてのレコードを抽出可能にします。

以下の記事のSQLで実現していることと同じようなことをエクセル等の表計算ソフトのセル関数で実現してみます。

https://qiita.com/takahasinaoki/items/c74e177577aa34b5520a

# 実行結果
抽出対象のレコードの行すべてに０以外の値が、設定されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/9f21e762-30ed-8101-3f88-1e322659af98.png)

セル関数を表示させると以下となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/5435f5dd-11c9-8f67-05cd-f8a7c5b7525d.png)

# 解説
COUNTIFSの
１つ目の抽出条件に、点数が100点となる条件を指定します。
２つ目の抽出条件には、同一氏名のレコードすべてを抽出させるために、<font color=RED>氏名の列を検索条件範囲として、検索条件に**該当行の氏名**を与えます。</font>
SwiftUIでスプラッシュ画面を実装する方法(iOS)

## はじめに

SwiftUIでスプラッシュ画面を実装する方法を紹介します。

## 環境

- OS：macOS Big Sur 11.3
- Xcode：12.5 (12E262)
- Swift：5.4

## スプラッシュ画面の実装方法

~~おそらくSwiftUIではデフォルトでスプラッシュ画面が用意されていないので、~~ 通常のビューとして実装します。

追記
[おまけ](#おまけ-infoplistで設定する) に書きましたが、デフォルトでスプラッシュ画面が用意されていました。

私はUIKitに合わせて `LaunchScreen.swift` と命名しています。

本記事では背景色が `Primary` カラーで、ビューの真ん中に `Logo` イメージを表示するだけのスプラッシュ画面とします。

```swift:LaunchScreen.swift
import SwiftUI

struct LaunchScreen: View {
    @State private var isLoading = true
    
    var body: some View {
        if isLoading {
            ZStack {
                Color("Primary")
                    .ignoresSafeArea() // ステータスバーまで塗り潰すために必要
                Image("Logo")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .padding()
            }
            .onAppear {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    withAnimation {
                        isLoading = false
                    }
                }
            }
        } else {
            ContentView()
        }
    }
}

struct LaunchScreen_Previews: PreviewProvider {
    static var previews: some View {
        LaunchScreen()
    }
}
```

本記事ではスプラッシュ画面を0.5秒間表示して、 `ContentView()` に切り替えています。
実務ではスプラッシュ画面の表示時間を固定にするのでなく、初期データ取得などが完了するまで表示するのが望ましいです。

スプラッシュ画面を実装したら、初期表示するビューを変更します。

```diff_swift:FooApp.swift
import SwiftUI

@main
struct FooApp: App {
    var body: some Scene {
        WindowGroup {
-           ContentView()
+           LaunchScreen()
        }
    }
}
```

これでスプラッシュ画面の実装が完了です。

## おまけ: Info.plistで設定する

[Twitter](https://twitter.com/yoshipaka/status/1411175522751578112?s=20) で教えていただいたのですが、iOS 14からは `Info.plist` でかんたんに設定できました。

今回だと以下の設定のみでスプラッシュ画面が実現できます。
![スクリーンショット 2021-07-03 17.24.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/681ecfea-6146-3d21-4e1e-57b242203e66.png)

```xml:Info.plist
        <key>UILaunchScreen</key>
        <dict>
                <key>UIImageName</key>
                <string>Logo</string>
                <key>UIColorName</key>
                <string>Primary</string>
        </dict>
```

ただ自分が触った限りだと画像サイズを指定できず、画面からはみ出してしまいました。
予め小さい画像を用意する必要がありそうです。

表示時のアニメーションは、今までのStoryboardを使う方法と同じように見えました。

Appleが公式で提供している方法ということもあり、単純なスプラッシュ画面を表示する際は `Info.plist` を使うほうがいいと思います。
複雑なビューや初期データ取得を待ちたい場合などは、自前で実装するのがよさそうです。

## おわりに

かんたんにスプラッシュ画面を実装できました！

[HIG](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/launch-screen/) を読み、適切なスプラッシュ画面を用意しましょう :relaxed: 

## 参考リンク

- [[SwiftUI] Splash Screen の実装 - Reona's Blog](https://reona.dev/posts/20200908)
- [Launch Screen in SwiftUI | Apple Developer Forums](https://developer.apple.com/forums/thread/651549)
- [UILaunchScreen | Apple Developer Documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/uilaunchscreen)
- [【Xcode12】Info.plist で Launch Screen を構成しよう – ギャップロ](https://gaprot.jp/2020/11/24/launch-screen-plist/)
- https://twitter.com/the_uhooi/status/1410432384017698817?s=20
他クラス分類の評価時にはclassification reportとconfusion matrixがめっちゃ便利なので使っていけ

## はじめに

二値分類では評価がやりやすいですが、他クラス分類時では結構いろんな評価指標あって迷いますよね。
(再現率だ、適合率だAUCだなんだかんだ...)

しかし、pythonには便利なライブラリがあります。

**各クラスの適合率や再現率がひと目でわかったり、どのクラスに分類間違いしているかなどを視覚的に理解できるライブラリを紹介します。**

他クラス分類時に汎用的で少ないコードで使うこともでき非常に便利な２つのライブラリ。

**- classification report
- confusion matrix**

## テストデータの作成
評価に使用するテストデータを作成します。

``` python:

import random
import pandas as pd
from sklearn.metrics import classification_report, confusion_matrix
import matplotlib.pyplot as plt
import seaborn as sns

#データ作成
df = pd.DataFrame()

#正解列
df["true"] = [random.randint(0,5) for i in range(200)]

#予測列
#エラーを作るため適当に20個のインデックスで書き換え
err_idx = df.sample(20).index
df["pred"] = df["true"]
df.loc[err_idx, "pred"] = [random.randint(0,5) for i in err_idx]
print(df.shape)
df.head()

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533584/b2d45cb7-d648-3f93-142f-427f4e1a9470.png)


## classification report
classification reportを使うと、各クラスの適合率や再現率、F値を簡単に把握できます。
DataFrame形式に落とし込むと表示が楽になります。

``` python:
df_report = pd.DataFrame(classification_report(df["true"], df["pred"], 
                                               output_dict=True)).T
df_report
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533584/1b8a6641-ec99-1617-94fd-d3f1315ae8e2.png)

うむ、楽ちん。

## confusion matrix

confusion matrixを使うと、各クラスがどのように分類間違いしたかを視覚的に理解できます。

``` python:
sns.heatmap(confusion_matrix(df["true"], df["pred"]), annot=True)
plt.xlabel("pred")
plt.ylabel('true')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533584/9e8606c2-05b3-53be-7890-6edec75f4d27.png)

うん、見やすい。

## 終わりに
classification reportとconfusion matrixの紹介でした。
少ないコードで視覚的に理解できるので、とりあえず使っておくと便利です。
【Vue.js】パスカルケースとケバブケースについて

Vueの基礎知識としてケースの使い分けを理解するために備忘録として書いています。

# そもそもケースとは？
簡単に言うと、**「ファイル名や関数名とかの書き方のルールみたいなもの」**と認識しておけばいいと思います。
それでは、パスカルケースとケバブケースについて見ていきましょう。

# パスカルケース
**単語の区切りを大文字で書く方法**
先頭が大文字だと「アッパーキャメルケース」
先頭が小文字だと「ローワーキャメルケース」とも言われます。
※ Vueではコンポーネントに「ローワーキャメルケース」は使いません。

```
SampleCase
testCase
```

# ケバブケース
**単語を```-```で区切って書く方法**
由来は```-```がケバブの串に見えるかららしいです。

```
sample-case
user-list
```

# 主な使い分け
下記のように利用すれば問題ないと思います。

- **HTMLでは「ケバブケース」**
- **コンポーネントでは「パスカルケース」**

実際に例をいくつか見てみましょう。
## コンポーネント
**基本的には「パスカルケース」を使いましょう。**

```vue:.vue
<template>
  <TestComponent></TestComponent>
</template>
```

**＊注意＊**
**componentsに記述するときに、
「パスカルケース」で書けば「ケバブケース」でも問題ないが、
「ケバブケース」で書くと「ケバブケース」でしか動かない。**

```vue:.vue
<template>
  <TestComponent></TestComponent>     <!-- OK -->
  <test-component></test-component>   <!-- OK -->

  <TestComponent2></TestComponent2>       <!-- NG(ケバブケースで指定しているから) -->
  <test-component2></test-component2>     <!-- OK -->
</template>

<script>
export default {
  components: {
    TestComponent, 
    'test-component2': TestComponent2,
  }
}
</script>
```
## props
Vueで親コンポーネントから子コンポーネントに値を渡すpropsですが、
**属性は「ケバブケース」その他は「キャメルケース」**で書きましょう。

```vue:.vue
<template>
  <TestComponent :test-data="hoge"></TestComponent>
</template>

<script>
export default {
  props: ['testData']
}
</script>
```

## emit
emitではキャメルケースとケバブケースを勝手に変換してくれたりはしません。
**イベント名は常に「ケバブケース」**を使いましょう。
本番環境（heroku）に環境変数を設定し忘れた結果

Heroku上に環境変数を設定するのを忘れていたため
エラーが起きてしまいました。

herokuにログインして以下のコマンドを実行です。

（ターミナル）
heroku config:set PAYJP_SECRET_KEY=‘sk_test_*************’
heroku config:set PAYJP_PUBLIC_KEY=‘pk_test_*************’

次にheroku configで環境変数が設定できたか確認します。

（ターミナル）
PAYJP_PUBLIC_KEY:     pk_test_*************
PAYJP_SECRET_KEY:     sk_test_*************

最後にgit push heroku masterで終了の予定でした。
しかし、まだエラーは続いています。


どうやらエラー原因は、環境変数を設定する前に
git push heroku masterしてしまったからなようです。

環境変数を設定し直して再度
git push heroku masterしてみました。

結果は、Everything up-to-date
とターミナル上に表示されるだけ。
変更が反映されないのです。

そんな時は以下のコマンドを実行です。
git commit --allow-empty -m “空のcommit”

すると空のcommitを作ることができます。

最後にherokuにプッシュします。
git push heroku master

これでダメならお手上げの状態でしたが
何とかアプリが正常に動いてくれました。
pandasでExcelのシリアル値をdatetime型に変換する

timedeltaを使わなくてもできると知ったのでメモ。

```python
import pandas as pd

df = pd.DataFrame({'date': [44378, 44379, 44380]})
df['date'] = pd.to_datetime(df['date'], unit='D', origin='1899/12/30')

print(df['date'])
```
```:出力
0   2021-07-01
1   2021-07-02
2   2021-07-03
Name: date, dtype: datetime64[ns]
```

1900/3/1より前の日付がある場合は

```python
df = pd.DataFrame({'date': [1, 59, 61]})
df[df['date'] < 61] += 1
df['date'] = pd.to_datetime(df['date'], unit='D', origin='1899/12/30')

print(df['date'])
```
```:出力
0   1900-01-01
1   1900-02-28
2   1900-03-01
Name: date, dtype: datetime64[ns]
```
【Git】mainブランチに直接pushしない時のgitの流れ

※自分用の備忘録です。


#作業時
mainブランチから作業ブランチを切る。

```
git checkout -b feature1
```

作業毎に

```
git add -A
git commit -m "○○追加"
git push origin feature1
```

#mainブランチへマージする。
Github上でプルリクエスト作成。
プルリクエストにOKが出たら、マージの前に作業コミットをまとめる。

まずコミットの内容を確認。

```
git log --oneline
```

```
git rebase -i <HEAD>
```
をして、pickをsに変更する。
[Gitの複数コミットをrebaseとsquashでまとめる方法](https://iwb.jp/git-commit-rebase-squash/)


リモートへpushする。

```
git push origin feature1 -f
```
[なぜrebase後に強制プッシュをする必要があるのか](https://qiita.com/hitochan777/items/ea08df354b42be57e5fc)

Github上でマージボタンを押す。
mainブランチへのマージ完了🙆‍♀️

#次の作業に進む。
ローカルでfeature1ブランチにいるので、mainブランチに戻る。
mainブランチをリモートに合わせる。


```
git fetch origin
```

上記で、リモートの最新情報をローカルリポジトリに取得。(↑これだけではワークツリーには反映されない。)

```
git merge origin/main
```
上記で変更をワークツリーに反映。



#feature1にいる状態でfeature2ブランチを切って作業を進めてしまい、mainブランチからfeature2ブランチを切り直したい場合
(ちなみにfeature2でパッケージのインストールなどしてしまった...)

feature1ブランチに戻る。

```
git checkout feature1
```
mainブランチに戻る。

```
git checkout main
```
↑これでエラー出る。

```
error: Your local changes to the following files would be overwritten by checkout:
        <ファイル名>
        <ファイル名>
Please commit your changes or stash them before you switch branches.
Aborting
```

とりあえずfeature2ブランチを消してみた。(これやった後もエラー出るのでしない方がいいかも。)

```
git branch -D feature2
```
[Gitでローカルブランチを削除する](https://qiita.com/hogeta_/items/33d2334c9b1919bd5120)

```
git checkout main
```
上記と同じエラーが出る。

そこで...

```
git stash
```
をしてみると

```
Saved working directory and index state WIP on feature1〜(省略)
```
と返ってきて、
`git checkout main`ができた！

[【Git】checkout時に、「error: Your local changes to the following files would be overwritten by checkout: 」と表示された時の対処方法
](https://note.com/koushikagawa/n/n6f7c16a0d9e6)[【git stash】コミットはせずに変更を退避したいとき
](https://qiita.com/chihiro/items/f373873d5c2dfbd03250)
【Ruby】等しくない数値が == で等しいと判定される話

Ruby で，`0.1` というリテラルで生成される浮動小数点数は，厳密には数学的な 0.1 に一致しない。
2 進法に基づく浮動小数点数で 0.1 を表すことはできないのだ。

では，`0.1` で生成される浮動小数点数は，実際にはどんな値を表しているのだろうか。
それは [Float#to_r](https://docs.ruby-lang.org/ja/3.0.0/method/Float/i/to_r.html) で Rational オブジェクトに変換してみれば分かる。このメソッドは，浮動小数点数が表す数に厳密に等しい有理数を返す。

```rb
p 0.1.to_r # => (3602879701896397/36028797018963968)
```

なんか，分子・分母ともにずいぶんデカい整数となったが，よくよく見れば分母は分子の 10 倍より 2 だけ小さい数であることが分かる。つまり，0.1 に極めて近い数なのだ（いや，そうでないと困りますョ！）。
なお，上の結果は環境によって違いがありうる。

ここまでが前置き。

Ruby の比較演算子 `==` は，両辺が数値（Numeric オブジェクト）のとき，**基本的には**「数値として等しいか否か」を判定するメソッドだ。
つまり，型（クラス）が一致していなくても

```rb
p 2.0 == 2 # => true
p 2 == 2r  # => true
```

のように同じ数値を表していれば `true` を返す。
（`2r` は「分母が 1，分子が 2 である Rational オブジェクト」を表す有理数リテラルである）

逆に同じ数値クラスでも，表す値が違っていれば `false` を返す。

ところが，である。異なる数値を表す数値オブジェクトが `==` で `true` となる場合があるのだ。

冒頭で見たように，`0.1` は数学的な 0.1 からわずかにズレている。
一方，`0.1r` という有理数リテラルによって生成されるのは，数学的な 0.1 に厳密に等しい Rational オブジェクト（分母が 10，分子が 1）である。
これらは数値としてわずかに違っているにも関わらず

```rb
p 0.1 == 0.1r # => true
```

となる。えっ？

Ruby の比較演算子 `==` は，実際にはメソッドとして実装されており，左辺のクラスに定義されたメソッドで振る舞いが決まるわけだが，公式リファレンスマニュアルを見ても上記のようなことは一切書かれていない。
[Float#== (Ruby 3.0.0 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/3.0.0/method/Float/i/=3d=3d.html)
[Rational#== (Ruby 3.0.0 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/3.0.0/method/Rational/i/=3d=3d.html)

実装を見たわけでもないので推測になるが，上記の現象が起こる理由は，

```rb
0.1 == 0.1r
```

を評価したときにまず `0.1r` が左辺に合わせて Float に型変換され，その際に丸め誤差が生じて `0.1` と等しい Float オブジェクトができるということだろう。

しかし常に「右辺が左辺に合わせて型変換される」わけではないことに注意。
実際，

```rb
p 0.1r == 0.1 # => true
```

のように，左右を入れ替えても同じ結果になる。
Rational と Float とでは，Float のほうに合わせられる，ということだ。

丸め誤差が生じない方向に型変換（Rational と Float なら Rational に）する仕様にしておけばよかったのに，と思わなくもない。

一方で，「Float というものはそもそも誤差を帯びた数値を表すものなので，相手が Integer や Rational など誤差を帯びないものでも，比較においては Float 側に合わせるべき」という思想もあるのだろう[^er]。
Ruby の `==` がそういう思想に明確に基づいて設計されているのかどうかは知らない。

[^er]: 私自身は，Float が誤差を帯びた数値を表すものとは考えていない。誤差は型変換や数値計算により生じるのであって，Float オブジェクト自体は（NaN や Infinty などを除き）数直線上の一点に対応している，と考えている。
TypeScriptでUnion型の判別

実際に案件でUnion型を判別していた処理をリファクタリングした時の内容を共有します。

もともとのコードは以下のような内容です。

```ts
type Form = CheckForm | FreeTextForm;

interface BaseForm {
  id: number;
  type: 'checkbox' | 'freeText';
  description: string;
}

interface CheckForm extends BaseForm {
  options: Options[];
}

interface FreeTextForm extends BaseForm {
  maxLength: number;
}

const form: Form = {
  id: 1,
  type: 'checkbox',
  description: 'select only one',
  options: [{key: 1,value: '1'},{key: 2,value: '2'}]
}


function findOptionIndex(form, key) {
  // CheckFormのときだけ処理をしたい
  if (form.type === 'checkbox') {
    const f = form as CheckForm; // form.optionsを参照できるように型を指定している
    return form.options.findIndex((opt) => opt.key === key);
  } else {
    return -1;
  }
}

```

Formは複数の型を結合したUnion型のため、`form.options`を参照しようとすると`options`をメンバとして持っていない可能性があると怒られてしまいます。
 そのため`as` を使って`form`の型を`CheckForm`として扱うように指定していました。

しかしこの記述をするにはいちいち変数にオブジェクトを入れ直さないといけないのであまりよろしくないです。
なので、まず特定の条件をクリアした場合にどういう型として扱うか指定することができるユーザー定義の型ガードというので書き直してみました。

```ts
const isCheckForm = (form: Form): form is CheckForm => {
  return form.type === 'checkbox';
}

function findOptionIndex(form: Form, key: number) {
  if (isCheckForm(form)) {
    return form.options.findIndex((opt) => opt.key === key);
  } else {
    return -1;
  }
}

```
上記のように関数の返り値を`arg is TypeName`とすることで、関数の返り値が`true`だった場合に引数にいれた`arg`を`TypeName`で指定した型のオブジェクトとして扱うことができます。
これで可読性もだいぶ良くなり型の予測もしやすくなりました。

ただ一つ問題があり、この方法だと`is`の後ろにどんな型でも指定できるので、開発者が間違った型を指定するとTypeScriptがエラーを検知できなくなるリスクがあります。

Union型のメンバが持つリテラル型のプロパティを元にUnion型のメンバを判別できるらしく、最終的に先ほどのリファクタリングは諦めて、`CheckForm`と`FreeTextForm`の`type`プロパティに文字列を指定することで解決しました。
内容は以下のようになりました。

```ts
type Form = CheckForm | FreeTextForm;

interface BaseForm {
  id: number;
  type: 'checkbox' | 'freeText';
  description: string;
}

interface CheckForm extends BaseForm {
  type: 'checkbox'; // 判別のためにリテラル型のプロパティを含める
  options: Options[];
}

interface FreeTextForm extends BaseForm {
  type: 'freeText'; // 判別のためにリテラル型のプロパティを含める
  maxLength: number;
}

const form: Form = {
  id: 1,
  type: 'checkbox',
  description: 'select only one',
  options: [{key: 1,value: '1'},{key: 2,value: '2'}]
}


function findOptionIndex(form: Form, key: number) {
  // CheckFormのときだけ処理をしたい
  if (form.type === 'checkbox') { // CheckFormのtypeプロパティと一致しているため、CheckFormと認識される
    return form.options.findIndex((opt) => opt.key === key);
  } else {
    return -1;
  }
}

```

##  参考
https://typescript-jp.gitbook.io/deep-dive/type-system/typeguard
https://typescript-jp.gitbook.io/deep-dive/type-system/discriminated-unions
grepコマンド基礎

シェルスクリプトやコマンドを使った開発や作業の効率化に興味があり、
[Software Design 2021年6月号](https://gihyo.jp/magazine/SD/archive/2021/202106)に掲載されていた「シェルとコマンドで使う正規表現」という章を読んだのでまとめます。

## 基本正規表現（BRE）
grepでデフォルトで使用されている正規表現は、基本正規表現（Basic Regular Expressions、BRE）と呼ばれれるもの。
特殊な意味をもつ文字は`.[]^$*`の6文字だけ。他の記号も使えるがエスケープが必要。

シェルで正規表現を書くときは基本的にシングルクォートで囲む。
そうしないとシェルが「*」や「$」などの文字列を別の意味で解釈してしまう。

```zsh:()で囲まれた文字列を検索
$ grep '（.*）' text.txt
（きん）の網はゆらゆらゆれ、泡はつぶつぶ流れました。
『ふうん。しかし、そいつは鳥だよ。かわせみと云うんだ。大丈夫（だいじょうぶ）だ
『いいいい、大丈夫だ。心配するな。そら、樺（かば）の花が流れて来た。ごらん、き
　泡と一緒（いっしょ）に、白い樺の花びらが天井をたくさんすべって来ました。
```

```zsh:-oオプションでマッチした部分だけを抽出
$ grep -o '（.*）' text.txt
（げんとう）
（ひき）の蟹（かに）
（は）
（はがね）
（てんじょう）を、つぶつぶ暗い泡（あわ）
（つぶ）
```


```zsh:2つ以上のかっこを別々に抽出
$ grep -o '（[^（]*）' text.txt
（げんとう）
（ひき）
（かに）
（は）
（はがね）
（てんじょう）
（あわ）
（つぶ）
```

`[^（]`でかっこの中のかっこを否定している

## 拡張正規表現（ERE）
grepには`-E`というオプションが用意されている。これは拡張正規表現（Extended Regular Expressions、ERE）というクラスの正規表現を使うためのオプション。

### GREとEREを比較
```zsh:BREで5桁-5桁-5桁のレコードを検索
$ cat numbers.txt | grep '^.....-.....-.....$';
```
```zsh:EREで5桁-5桁-5桁のレコードを検索
$ cat numbers.txt | grep -E '^.{5}-.{5}-.{5}'
```
BREでは繰り返す数だけ`.`を入力する必要があるが、EREでは`{5}`というように任意の文字が５個続くという表現を短い文字数で表現できる。
BREでもエスケープを使えば表現可能。
```zsh:BREでもエスケープを使えば表現可能。
$ cat numbers.txt | grep '^.\{5\}-.\{5\}-.\{5\}'
```

  {}による個数の指定には、{m,n}という記法もあり、この記法は「m個以上n個以内」という意味になる。
  
```zsh:2〜3桁-2桁以内-2桁桁以上を{m,n}を使って表す
$ cat numbers.txt | grep -E '^.{2,3}-.{,2}-.{2,}$'
```

ほか、EREでエスケープなしで使える記号には、正規表現をグループ化する「()」や、1文字以上の繰り返しを表す「+」、直前に書いた文字が0文字か1文字存在することを表す「?」、()の中でORを表す「|」がある。

## Perl互換正規表現（PCRE）
BRE、EREの違いは記号をエスケープするかどうかぐらいだったが、grepではPerlで使われる強力な正規表現も利用できる。
この正規表現はPerl互換性正規表現（Perl Compatible Regular Expressions、PCRE）と呼ばれ、同じ名前でライブラリ化されている。
grepでもこのライブラリが使え、-Pオプションで呼び出すことができる。

※ただし、macOSではPCREがサポートされていないので、別途GNU版のgrepをインストールする必要がある。インストールしたコマンドはggrepというコマンド名で登録される。

```zsh:MacでPCREを使うためにGNU版のgrepをインストールする
$ brew install grep
$ ggrep -P '.*' text.txt
```

### RCREを使って正規表現を書き換える
```zsh:.{2,3}ではなく\d{2,3}として、数字であることを明記
$ cat numbers.txt | grep -Po '^\d{2,3}-\d{2,3}-\d{2,3}$'
```
```zsh:(?番号)で正規表現を使い回し
$ cat numbers.txt | grep -P '^(\d{2,3})-(?1)-(?1)$'
```
?番号を使うと、`()`で囲んだ正規表現を再利用できる。
?1の場合は一つ目の`()`の中の正規表現なので、\d{2,3}を再利用している。


おわり
JavaScriptで和暦を西暦に変換する方法（令和にも対応）

幾つかのサイトをクローリングして日付をキーにスクレイピングしていまして、和暦と西暦が混在しまくっていてめんどくさかったので、和暦を西暦に変換する関数を作りました。
初心者ながら作ってみましたが、コードも短いので使い勝手がいいかと！

#和暦→西暦に変換する JavaScript 関数
テキスト内に西暦と和暦が混在していても一気に変換できるようにしています。

```javascript
function wareki2seireki(date) {
  // 
  // 和暦が含まれていたら西暦に変換する関数
  // date:string型
  // 
  let eraName = {"明治": 1867,                                             // 各元号の始まりの年数（0年）を定義
                 "大正": 1911,
                 "昭和": 1925,
                 "平成": 1988,
                 "令和": 2018};
  reg = RegExp("(" + Object.keys(eraName).join("|") + ")([元0-9０-９]+)年") // [元号]+[○年]にマッチする正規表現を作成

  while(date.match(reg)){                                                  // [元号]+[○年]にマッチするまで繰り返す
    let era = date.match(reg);                                             // 元号を抽出
    let year = parseInt(era[2].replace(/[元０-９]/g, function(match){       // 全角数字を半角数字に変換、元年にも対応
      if(match === '元') {
        return 1;
      }
      return String.fromCharCode(match.charCodeAt(0) - 65248);
    }));
    year += eraName[era[1]];                                               // 和暦年数を西暦年数に変換
    date = date.replace(reg, year + "年");                                 // 和暦を西暦に置き換え
  }
  return date;
}
```

#参考サイト
下記サイトを参考にしました。
https://blog.capilano-fw.com/?p=3307

原型はあまり残っていませんが・・・汗
こちらはPHPやPythonのコードもあります。

#追記

コメントを頂き、もっとずっと洗練された書き方を共有いただきました。
``g``オプションを使ってwhileループをなくすことに関しては、そりゃそっかと思いましたが、特に全角数字を半角数字に変換する部分でindexを利用する部分は目から鱗で、こんな方法があるのかと驚きました。
あとアロー関数に対して理解が深まり、とても勉強になりました。
ぜひ上記ではなく下記のスクリプトを利用くださいｗ

```javascript
function wareki2seireki(date) {
  const eraName = {
    明治: 1867,
    大正: 1911,
    昭和: 1925,
    平成: 1988,
    令和: 2018,
  };

  const reg =
    RegExp('(' + Object.keys(eraName).join('|') + ')(元|[0-9０-９]+)年', 'g');

  return date.replace(reg, (_, w, n) =>
    + n.replace(/[０-９]/g, m => '０１２３４５６７８９'.indexOf(m)).
        replace('元', '1') + eraName[w] + '年');
}
```
Rails 爆速でパンくずリストを作成する

## パンくずリストとは

https://www.seohacks.net/blog/899/

超簡単に言うと、ページ遷移を視覚的に表示してくれるものです。
導入する目的としては主にSEO対策とUIの向上でしょうか。

その辺は「パンくずリスト」とかで調べると死ぬほど出てくるので、それを見ると良いと思います。

## gretelというGem

Railsの場合、gretelというGemを導入することで、爆速でパンくずリストを作成できます。

gretelはリンクを設置したリストを画面に表示する機能を実装できます。

ということでいつも通り

```Gemfile

gem "gretel"

```

Gemfileに記述して、いつも通りbundle install(DB作成後はサーバーの立ち上げ直しも)

```
rails g gretel:install
```

これでgretelをインストールします。

インストールしたらconfig/breadcrumbs.rbが自動で作成されます。

## breadcrumbs.rbの記述

```config/breadcrumbs.rb
#rootは自動で作成されている
crumb :root do
  link "Home", root_path
end

#ここから追記する

crumb "現在のページ名（表示させるビューにもページ名記述）" do
  link "パンくずリストでの表示名", "アクセスしたいページのパス"
  parent :親要素のページ名（前のページ）
end

#例
crumb :tweets do
  link "ツイート一覧", tweets_index_path
  parent :root
end

crumb :contacts do
  link "コンタクト", contacts_index_path
  parent :tweets
end
```

これでパンくずリストの表示設定ができたので、あとはこれを各種ビューに表示させればOK

## ビューへの追記

```views/layouts/application.html.erb
<!DOCTYPE html>
<html>
 <head>
   <title>Breadcrumb</title>
   <%= csrf_meta_tags %>
   <%= csp_meta_tag %>
   <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
   <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
 </head>
 <body>
   #追記
   <%= breadcrumbs separator: " &rsaquo; " %> 
　　　　　#追記ここまで
   <%= yield %>
 </body>
</html>
```

separator: " &rsaquo;”は、パンくず間の区切りである「>」を示します。

パンくずを表示したいViewに

```
<% breadcrumb :root %>
#rootのところはconfig/breadcrumbs.rbで作成した名前
```

これでパンくずリストは作成完了です。

[![Image from Gyazo](https://i.gyazo.com/fe0b7caa0fbab653f99639bd824a2c72.png)](https://gyazo.com/fe0b7caa0fbab653f99639bd824a2c72)

めっちゃ簡単かつWEBアプリケーションとして必要な機能でもあるので、導入する時のコスパは高いですね。
個人的にはこういう簡単で、効果が高い機能は大好きです。
TOPPERS/ASPをRustから使うサンプル(α版)を公開しました

# お知らせ

過去数回解説したものの実装をgithubで公開しました。

- [TOPPERS/ASP for Wio Terminal with Rust](https://github.com/s-meika/toppers_asp)
- [サンプルプログラム](https://github.com/s-meika/asp_sample1)

まだ動作確認は途中ですが、記事も本物がないと読みづらいと思っての公開です。
これから動作解説＆動作確認方法もアップしていきます。

# 注意点

Readme.MDにも書きましたが、elf形式のままhf2で書き込むと何故か動きません。
binにすると問題がないので、それでしのいでいます。

# githubにあげてみた感想

ひさしぶりにGit使ったので疲れました。
masterとmain間違えて付け替えしたりするのが面倒でした。
Convert hex in text representation to decimal number


## int
```sql
SELECT ('x' || lpad(hex, 8, '0'))::bit(32)::int AS int_val
FROM  (
   VALUES
      ('1'::text),
      ('f'),
      ('100'),
      ('7fffffff'),
      ('80000000'),     -- overflow into negative number
      ('deadbeef'),
      ('ffffffff'),
      ('00000000000000000000000000000000000000000000000008f622eefc55fed6'), -- overflow
      ('ffffffff123')  -- too long
   ) AS t(hex);
```

## bigint

```sql
SELECT ('x' || lpad(hex, 16, '0'))::bit(64)::bigint AS int_val
FROM  (
   VALUES
      ('1'::text),
      ('8f622eefc55fed6'),
      ('00000000000000000000000000000000000000000000000008f622eefc55fed6') -- overflow
   ) AS t(hex);   
```

---

参考：

https://stackoverflow.com/questions/8316164/convert-hex-in-text-representation-to-decimal-number

https://newbedev.com/convert-hex-in-text-representation-to-decimal-number
NUC 電源トラブル

起動しなくなって画面も映らなくなったら

リカバリーbiosをintel公式からダウンロードし、
usbメモリにfat32フォーマット済みにして
.capファイルを直下にコピー

https://downloadcenter.intel.com/ja/download/30404/BIOS-FNCML357-

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/386f3cd8-e576-b7fe-fb5a-e654db01d92e.png)


電源ボタンを押しつづけながらF4キー連打

delキーも押した方がいいかも？

更新の画面が済めば完了。

自分はこれで復旧しました
【Docker】docker-composeのオプション--buildの使い方　no.25

![パンプキン カフェ (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011305/c5d83aec-7698-0efe-92c9-5a2803dca373.png)


こんにちは。まゆみです。

Dockerについての記事をシリーズで書いています。

[前回の記事](https://qiita.com/Mayumi_Pythonista/items/ad12ea33d7360b2c6d1e)で、docker-composeについて詳しく書かせていただきました。

『docker-compose up』は、

『docker build』 + 『docker run』の２つのコマンドを一気にしてくれる便利なコマンドなのですが、逆に

docker buildだけしたい

もしくは

docker rebuild したいという時もあると思います
 
今回の記事では、docker-composeを使う時に

>もう一度buildし直すのか？

>build のみしたいのか？

>build はせず、run のみしたいのか？

の使い分けを図を用いて解説していきます。

ではさっそく始めていきますね。

#図式してみた

![build.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011305/2c4a1239-e61c-9616-e603-464792525ca9.png)


docker-compose up を初めてする時は、もちろんbuild しなければImageが無いので、docker-compose up はbuild からrun までしてくれます。

間違いやすいのは、2回目以降にup をする時です。

<font color="Red">2回目にdocker-compose up をする時は、run の部分だけする事になります</font>

すなわち、もしソースコードに変更を加えた時も、Imageはrebuildされることなく、変更が反映されないということです。

##--buildオプション

ソースコードが更新された時、

```
docker-compose up --build 
```
とオプションを付けて実行します。

これで、build ➡　run　で実行されます。

##build のみしたい時
Imageだけをbuildして、run まではしたくない時は

```
docker-compose build
```

docker-compose buildの場合

>docker build <ディレクトリ>


のように、ディレクト名は付けなくていいのも注意しておきましょう

#まとめ
今回の記事は、短いですがここで締めくくらせていただきますね。

お役に立てれば幸いです。
Gitのよく使うコマンドを図にまとめてみた

![Gitまとめ.002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166484/c39b4c54-38ed-2ad5-fc3c-155940b44d32.png)


## 解説

- Gitの差分や戻すのコマンドに迷ったら、この図で確認してみると良いかも知れません。
AWSでEC2を構築し、TeratermでSSH接続する

・自己紹介
24歳でインフラエンジニアとして活動しておりまして、現在はサーバ運用業務を行っており、主にLinuxとOracleDBを使用して作業を行っております。
資格はLPIC3(304)を取得しており、AWS SAA取得に向けて勉強中です。

・目的
将来的にクラウドエンジニアとして活動していきたいと考えており、スキルや知識の向上の為、AWSについて基礎から学ぶこととしました。

1.AWSアカウントを作成する。
AWS公式ページから「まずは無料で始める」をクリックし、必要な情報を入力する。
登録完了画面が表示されたら、「コンソールにサインインする」をクリックし、AWSマネジメントコンソールの画面が表示されたら、ここからサービスを立ち上げるGUI画面に遷移することができる。
2.EC2サービスを立ち上げる
AWSマネジメントコンソールからEC2と検索し、EC2の画面に移る。
インスタンスの起動をクリックし、7つのステップを進めていく。
3.Amazonマシンイメージ(AMI)の選択
OSのイメージを選択する。Amazon Linuxサーバ(無料枠)を選択する。
4.インスタンスタイプの選択
無料枠を選択する。
5.インスタンスの詳細を選択
インスタンスの数やネットワークの選択などが設定できる。今回は特に変更せずにデフォルトのままで対応。
6.ストレージの追加
ボリュープタイプに「汎用SSD(qp2)」を選択する。
7.タグの追加
インスタンスの説明をタグとして設定することができる。
今回は、タグの追加からキーを「Name」で、値を任意の名前に設定した。
8.セキュリティグループの設定
EC2インスタンスにどういったアクセスを許可するのかを設定できる。
今回はデフォルトで対応。
9.インスタンスの作成確認
今までの設定内容を確認できる。
こちらもデフォルトで対応。確認を押すと、キーペアの作成画面が表示される。
作ったインスタンスにアクセスするためのキーペア(セキュリティキー)を作成する。
任意の名前を設定して、ダウンロードをクリックする。
インスタンスの表示から、インスタンスの状態が「running」に変われば、インスタンスの起動が成功したことが確認できる。
10.EC2インスタンス操作
個人PCがwindowsの為、Tetatermを使用して接続を試みた。
まず、起動したインスタンスに入り、左側にチェックを入れる。
詳細のパブリックIPv4アドレスをコピーする。
Tetatermを起動し、先ほどコピーしたIPアドレスを張り付けて、接続する。
SSH Authenticationの画面が表示され、Usernameに任意の名前を入力、Authentication methodsの2つ目にチェックを入れて、先ほどダウンロードした鍵ファイルを選択する。
okをクリックし、EC2インスタンスに接続できたことを確認する。
11.EC2インスタンスの削除
EC2インスタンスのダッシュボード画面からインスタンスに入り、先ほど作ったインスタンスの左側にチェックを入れる。
インスタンスの状態をクリックし、「インスタンスを終了する」クリックする。
※インスタンスはすぐに削除が出来てしまう為、大事なインスタンスはインスタンスの設定から終了保護を有効化することが推奨されている。

考察
今回、初めてAWSアカウントからEC2インスタンスの作成を行いましたが、ものの数分でしかも少しの作業でサーバを立ち上げることが出来たことに感動しました。
サーバ運用業務に従事しており、オンプレでの作業が多いため、今回のクラウドでサーバを作成する作業はとても勉強になりました。
ConoHaVPS API の「ローカルディスクのイメージ保存」の実践例

## ConoHa API を使ったローカルディスクのイメージ保存方法

下記 ConoHa API の使用例である.
簡潔な記載のため試行錯誤したので、補足しておく.

https://www.conoha.jp/docs/compute-create_image.php 



## 検証条件

### 時期

2021/01頃である

### ローカルホスト環境

Bash Shellscript および jq コマンドを使用できる環境が便利である.
なお、jq コマンドのインストール方法は別途調べてほしい.


## 手順

### 1. 事前に VPS サーバを「停止」させておく

事前にイメージを保存したい **VPS サーバを停止させておく** 必要がある.
サーバを停止させておかないと、イメージ保存 API を叩いた際に HTTP ステータスコード 404 が返される (はずである).

### 2. API Token を得る

ここでは下記 URL の操作を行う. (API を実行するたびに使い捨ての API Token が必要になる)
https://support.conoha.jp/v/apitokens/#01

#### 2-1. 「APIユーザ名」と「テナントID」を得る

事前に、上記 URL に記された要領で自身の「APIユーザ名」と「テナントID」を確認しておくこと.
ここでは下表のような値であったものと仮定する.

|項目|値|
|:--|:--|
|APIユーザ名|gncu76543210|
|テナントID|1234567890abcdef0987654321fedcba|
|ConoHa ログインパスワード| p@ssw0rd |

#### 2-2. 「API Token」を取り出す

上記「2-1」の値を使って、次の要領で「API Token」を取得する.

```bash
$ curl -i -X POST \
   -H "Accept: application/json" \
   -d '{ "auth": { "passwordCredentials": { "username": "gncu76543210", "password": "p@ssw0rd" }, 
   "tenantId": "1234567890abcdef0987654321fedcba" } }' \
   https://identity.tyo2.conoha.io/v2.0/tokens | sed -n '7,$p' | jq .access.token.id
```

### 3. イメージ保存 を行う

イメージを保存する際の名称を「`BACKUP-1`」とする.

URL は次のようになっている
・「v2」直後に「テナントID」を書く
・「servers」直後には後述の参考図の赤枠の値 (「`aaaabbbb〜`」) を書く

```bash
$ curl -i -X POST \
    -H "Accept: application/json" \
    -H "Content-Type: application/json" \
    -H "X-Auth-Token: 〈上記 2-2 で得られた API Token の値〉" \
    -d '{"createImage": {"name": "BACKUP-1"}}' \
    https://compute.tyo2.conoha.io/v2/1234567890abcdef0987654321fedcba/servers/aaaabbbb-cccc-dddd-eeee-ffffffffffff/action
```
　
**参考図**
![conohaapi.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/8682bd35-9956-1c24-58c8-fad008cdecc6.jpeg)


以上
[Ruby] 正規表現subメソッドの応用

##はじめに
今回はsubメソッドを使って実践的な使い方を紹介したいと思います。
よろしくお願いします。

##subメソッドの応用
前回subメソッドを使って、文字列の置換えができることを紹介しました。
応用として、もし、ある文字列を取り除きたいという時にもsubメソッドが使えます。

例えば電話番号のハイフンを取り除きたい時に、置き換えられる文字列をハイフンにし（第一引数）、空の文字列に置き換えるように第二引数を指定すれば、取り除くということが実現できます。

しかしながらsubメソッドは、指定した文字列を、最初の一文字だけしか置換えてくれません。

```irb:ターミナル
irb(main):001:0> tel = '090-1234-5678'
=> "090-1234-5678"

irb(main):002:0> tel.sub(/-/,'')
=> "0901234-5678"
# 最初のハイフンしか置換されない
```


そこで、指定した文字列を一つだけではなく、全てを置換えてくれる**gsub**メソッドを使用します。使い方は同じです。

##gsubメソッドでハイフンを取り除く

gsubメソッドのgはグローバルマッチから来ています。subメソッドと使い方は同じなので簡単です、

```irb:ターミナル
irb(main):001:0> tel.gsub(/-/,'')
=> "09012345678"
```

きれいにハイフンを全て取り除くことができました。

##最後に
置き換えるという機能を持ったsubメソッドですが、第二引数に空の文字列を指定することで取り除くということを実現できました。
なんだかトンチが効いてるというか、ものは考えようですね。
AtCoderログ：0004 - ABC 088 A

# 問題

https://atcoder.jp/contests/abc088/tasks/abc088_a

## 問題文
> E869120 は $1$ 円硬貨を $A$ 枚と $500$ 円硬貨を無限枚持っています.
> これらの硬貨だけを使うことによって, ちょうど $N$ 円を支払うことができるかを判定しなさい.

## 制約
> ・$N$ は $1$ 以上 $10000$ 以下の整数
> ・$A$ は $0$ 以上 $1000$ 以下の整数

## 収録されている問題セット
- [AtCoder に登録したら次にやること](https://qiita.com/drken/items/fd4e5e3630d0f5859067) ([第01問類題](https://qiita.com/drken/items/fd4e5e3630d0f5859067#%E7%AC%AC-1-%E5%95%8F--abc-086-a---product-100-%E7%82%B9))


# 回答 (AC)
$N$ を $500$ で割った商を $q$, 余りを $r$ とおく (つまり $N \div 500=q \cdots r$ とおく) と、E869120 は $500$ 円玉を $q$ 枚を支払うことは可能です。なので、余りの $r$ 円が支払えるかが判定できれば十分です。ここで $1$ 円玉は $A$ 枚あるので、$r$ が $A$ 以下なら支払うことは可能です。以上をまとめて、以下のようなコードを作成しました。ACでした。

```C++:abc088a.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n, a;
  cin >> n >> a;
 
  if ( n%500<=a ) {
    cout << "Yes" << endl;
  } else {
    cout << "No" << endl;
  }
}
```

# 調べたこと
## [AtCoderの解説](https://atcoder.jp/contests/abc088/tasks/abc088_a/editorial) → [ユーザ解説](https://atcoder.jp/contests/abc088/tasks/abc088_a/editorial)
同じ方針でした。

## [AtCoderの解説](https://atcoder.jp/contests/abc088/tasks/abc088_a/editorial) → [コンテスト全体の解説](https://img.atcoder.jp/abc088/editorial.pdf)
同じ方針でした。

# リンク
## 前後の記事
- 前の記事 → [AtCoderログ：0003 - ABC 064 A](https://qiita.com/RoadLynton/items/bc4ef08c45e0bf485d77)
- 次の記事 → [AtCoderログ：0005 - ABC 157 A](https://qiita.com/RoadLynton/items/a5e26129389ea71f06c6)
【Git】 うっかりmainブランチでコードを書き進めてしまった。新しいブランチで最初から書き直さないとダメ？？

## コード

```shell
$ git stash -u          # 一旦、作業したコードをstash(隠し場所)に移す
$ git checkout ブランチ名 # 本来作業したかったブランチに移動
$ git stash pop         # stashに置いておいたコードを戻す
```

## 解説

- `git stash -u`で、一旦現在のコードを退避する。
- `git stash pop`で退避していたコードを新規ブランチに戻す。
お試しredmineをdockerで

新しいプラグインを試すとかいろいろしたいのに rbenvいれて mysqlいれて云々とかとてもめんどくさいので、dockerでお気楽に試す環境を作る。

## docker-compose.yml

```
version: '3'

services:
    redmine:
        image: redmine
        restart: always
        ports:
            - 8080:3000
        environment:
            REDMINE_DB_POSTGRES: db
            REDMINE_DB_PASSWORD: example
            REDMINE_SECRET_KEY_BASE: supersecretkey
            REDMINE_PLUGINS_MIGRATE: 1
    db:
        image: postgres:latest
        restart: always
        environment:
            POSTGRES_PASSWORD: example
```

## 起動

```
# docker compose up -d
```

## プラグイン

`/usr/src/redmine/plugins' にインストールする

例えば予定実績レポートだと

コマンド

```
# cd /usr/src/redmine/plugins
#  git clone https://github.com/toritori0318/redmine_estimate_timelog.git
#  bundle exec rake redmine:plugins:migrate RAILS_ENV=production
```

コンテナ再起動

```
$ docker compose stop
$ docker compose start
```

この時に `docker cmpose down` するとデータなくなるので注意

**編集リクエストがありましたが、旧バージョンでは `docker-compose` だったけど、現行(少なくとも4.0以降)ではサブコマンド化されてて `docker compose` となっています**

## 確認

`http://localhost:8080` にブラウザでアクセスすると確認できる。
初期ユーザーは admin/admin
FastAPIでアップロードしたファイルをそのままS3にアップロードする方法

#はじめに
APIサーバーをFlackからFastAPIに移行する機会があったのでFastAPIでファイルアップロード機能を実装する方法についてメモしました。

また、FastAPIアップロードされたファイルを一旦保存せずに、そのままS3にアップロードしアップロードしたオブジェクトのパスを取得する方法についても紹介しています。

#準備
FastAPIでファイルアップロード機能を利用するには`python-multipart`をインストールする必要があります。

```
sudo pip install python-multipart
```

#コード

```Python:main.py
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/file_upload")
async def file_upload(file: UploadFile = File(None)):
    s3_bucket = "バケット名"
    s3_dir = "ディレクト"
    region_name = "ap-northeast-1"

    s3 = boto3.client("s3", region_name = region_name)

    response = s3.put_object(
        Body = file.file,
        Bucket = s3_bucket,
        Key = f"{s3_dir}/{file.filename}"
    )
    
    # S3にアップロードしたオブジェクトのパス
    file_url = "https://%s.s3-%s.amazonaws.com/%s" % (s3_bucket, region_name, s3_dir+"/"+file.filename)
    
    response = {
            "file_url": file_url
        }
        
    return response
```

#確認
/docs にアクセス
`ファイルを選択`して`Execute`をクリック
![スクリーンショット 2021-07-03 10.48.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237056/bf78c58f-0656-a882-ba5e-f3595c3fcbd3.png)


アプロードに成功するとS3のオブジェクトURLが取得でる。
![スクリーンショット 2021-07-03 10.51.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237056/bcdd4679-8237-e3dd-a330-c251fa4fcd94.png)

以上、`FastAPIでファイルアップロードしてそのままS3にアップロードする`のメモでした。

今後もこのようなプチスキルも記事にしていきたいと思います。
最後までご覧いただきありがとうございました。
JS基本①　〜型について〜

# JSの型について思い出せるようにメモ
JSはオブジェクト以外はプリミティブ型のようです。
プリミティブ型は一度作成されるとその値を変更することはできない。

### プリミティブ型一覧 7種
* String
* Null
* Number
* Symbol(重複しない)
* Boolean
* BigInt
* undefined