OmniAICreator/Qiita-1.07M huggingface dataset:
AIレシピ生成アプリをRails×Next.js×ECSで作ってみた

# はじめに
webアプリケーションを久しぶりに作成したので紹介記事です。


# 開発アプリ概要
### アプリ名: FitChef
カロリーや調理時間、使いたい食材、苦手な食材の情報をもとにAIがレシピをその場で生成します。
※塩分量、タンパク質量、食事のジャンル(和食、洋食、中華)、グルテンフリーなどの項目も追加予定


### 使用技術
- Backend
    - Rails 7.0.8.1(api mode)
    - Ruby 3.2.2
    - Nginx
    - Puma
    - Mysql
- Frontend
    - Next.js 14.1.0(app router)
    - React 18
    - typescript 5
- Infra
    - AWS(ECS Fargate/ECR/RDS(mysql)/ALB/Route 53/ACM/VPC)
    - Vercel
    - Docker
    - docker-compose

### インフラ構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3795759/9abd8d5e-5ba7-4494-011d-cf04ecfd030b.png)

### 機能一覧
- ユーザー機能
    - 新規登録
    - ログイン/ログアウト
    - パスワード再設定
    - 認証メール再送
    - ユーザー情報変更
- レシピ生成機能
    - GPT3.5を利用したレシピ生成
    - 調理時間、カロリーレンジ、使いたい食材、使いたくない食材をもとにレシピを生成
- レシピ情報
    - 一覧画面
    - 詳細画面
    - お気に入り登録

### Demo
##### [ユーザー機能]
muiを使用した動的フロントバリデーション
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3795759/16dc99da-9725-547c-dbfe-9a238e656bac.gif">

##### [レシピ生成]

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3795759/2bd00894-d9d9-2add-fa95-6fd14f34855d.gif">






##### [レシピ一覧]
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3795759/ab1c20b6-5f27-bfd0-d14e-6938be4bc78f.png">


##### [レシピ詳細]
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3795759/97b25cae-e106-58cf-5ebd-16fac569b5ea.png">
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3795759/8d8e4cd2-3508-993d-985d-1f15c30b8038.png">



### 工夫点
- gpt3.5からの正常レスポンス率を上げるように、requestを調整
    - リクエストを投げて、正常なレスポンスが返って来ているかを確認するスクリプトを作成し、確率が最大になるまで調整を行なった
    - gpt4.0を利用することで精度が大幅に向上するが、gpt3.5との価格差が20倍あるため、3.5で正常率を上げる方がコスト的に優れている
    - 精度を上げる上で参考にした資料
        - https://cookbook.openai.com/articles/techniques_to_improve_reliability
- sql発行回数
    - レシピ一覧のアクセスの際に全ての情報を取得し、再読み込みされるまでフロントで情報を管理している。お気に入り追加やレシピ詳細に遷移した際も、フロントで情報を保持したものを利用することで、sqlを発行しないようにしている。

### 今後追加予定の機能
- レシピ作成条件の拡張
    - 食事のジャンル(和食、洋食、中華)
    - 栄養素(高タンパク質、低炭水化物)
    - その他(ベジタリアン、グルテンフリー)
- 買い物リスト
    - 買い物リスト
    - amazon freshとの連携
- ユーザー登録方法
    - amazon
    - google
    - twitter
- gpt4.0解放
debian (stretch) で apt remove が Faild to stop service: Unit service not loaded で削除失敗する  

`apt remove` でパッケージを消そうとしたら、 

```
Failed to stop <*>.service: Unit <*>.service not loaded.
subprocess installed pre-removal script returned error exit status 5
```

とかエラーが出てパッケージが消せない。

systemd の作法にのっとって service の制御ができていない？

`/lib/systemd/system/` フォルダに `<*>.service` (消したいパッケージのサービス名) がファイルとして存在してないと思います。他のファイルに習って書いてやれば良いのですが、書き込むの面倒（消すので）なので、`/dev/null` にシンボリックリンク張ります。

```
cd /lib/systemd/system/
ln -s /dev/null <*>.service
```

そうすれば service の停止や起動を無視して apt でパッケージが消せるようになりました。
コマンドラインからWordPressをインストールし直す

学習のためにAWSのEC2上でWordPressインストールしていろいろ見る、というのをやろうとした際、うっかりEC2のElastic IPを設定し忘れ、起動するたびIPが変わる環境になっていたため、インストール済みのWordPressにログインできなくなった、とかいうアホみたいな状況に陥ってしまった　 (ᐛ) ばなな
なんとかするにはWordPress消して入れ直す＝ちゃんとElastic IP設定した状況で設定し直す必要があるので取り組んだが、「消す」部分についてあまりいい手がかりが見つからなかった
なんとかできたことはできたので備忘録的に書いておく

# TL;DR
- （ssh接続から）WordPress用に設定したデータベースを `DROP DATABASE ~` で消す
- データベース作り直す（権限とかも付け直す）
- `/var/www/html/` 下のWordPress用ファイルを `rm -rf` で全て削除する
- `/var/www/html/` に再度WordPress用ファイルをコピーする
- 対象IP（ドメイン登録してあればドメイン）にアクセスしWordPress設定を最初からやり直してインストールする

これでOK

# 発生
さーて先日AWSに設定したWordPressをいろいろいじるかー、とインスタンス・RDS起動してWordPressに入ろうとしたら、反応がない
見ると「（指定のIP）から反応がない」的な旨が表示される
IP間違ってたかなと思いEC2のダッシュボードにIP確認しに行くと、先日のものから変わっている

あ、これって
Elastic IP設定してなかった…

まあそりゃ反応ないよな、と思い、現在のIPに変えて再度アクセス
ログインフォームが出てきたから、よしよしこれでOKだ、と思いログインすると

やっぱり反応がない
URL確認すると、先日設定したIPのままになっている

あー、インストールした際に前のIPで紐づいちまうのかこれ…

こうなると今のやつ消して、Elastic IP設定した後でちゃんとインストールし直すしかないな、と思い今のWordPressを消す手段を探った
が…

# EC2上のWordPressを消してください

「WrodPress　削除」「WordPress　再インストール」とかで探してみると、出てくる情報はだいたいこう

```
WordPressのファイルを消してください
```
これはまあいい
（※これのみ試すと、WordPressの再インストールには進めるが、最終的に「既にデータベースが作られてます」と出てきて紐づけがスキップされ、変わらない状態に戻ってこれる）

```
WordPressのデータベースを削除してください
```

ふむふむ、ではその方法は？
```
コントロールパネルに入ってデータベースの削除を選んでください
```

なるほどですね　そのコントロールパネルはいれないです(ᐛ)

# ただしブラウザでのログインはなしで

幸いにもssh接続自体はできる（現在有効なIP自体はわかるので）
だからターミナルとかから今使ってるmysqlとかにも入れるし操作はできる
…じゃあここからデータベース消すしかないか

# 作り直しのための環境構築から

「ちゃんとElastic IP設定した状況で設定し直す」を実行するため、まずはElastic IPを設定しておく
これやってないと、また次にインスタンス起動した際にIP変わっちゃって同じ事態に陥るから

![ElasticIP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700474/de51bffe-7e0d-ca51-9707-3031195e598e.png)

割当てを行い、これをちゃんとEC2インスタンスに紐づけておく

続いてsshでEC2へ
上記で割り当てたIPを使い接続する
```
ssh 上記で割り当てたIP
```
こんな感じのやつ

# コマンドラインでデータベースを削除

上記のようにしてEC2に接続したら、 `mysql` コマンドで既に用意してあるデータベースに入れるはず
ユーザ自体は、データベース消せればなんでもいいので、とにかく入る
```
mysql -h データベース名 -u ユーザ名 -p
```
この後にパスワード入力すれば、頭に `MySQL [(none)]>` などが表示されてMySQLに入ってるはず

ひとまず現在のデータベースを確認すべく、いったん `SHOW DATABASES;` してデータベース名を確認（既知なら省略可）、消したいデータベース（入れなくなってるWordPressに紐づけたデータベース）の名前がわかったら、 `DROP DATABASE` コマンドで当該データベースを消す
```
DROP DATABASE 消したいデータベース名;
```

これでOK

削った後は、このまま `CREATE DATABASE` で先ほど消したのと同じデータベースを作り直し、 `GRANT` で権限付けておくなどの、作成時と全く同じ過程を踏んでおく

# コマンドラインでWordPressのファイルを消す

上記でMySQLに必要な操作は終わったので、 `exit` で退出する

続いては現在置いてあるWordPressのファイルを消すことにする

自分が設定していた場合ではapache環境なので、WordPressで使われる実ファイルは `/var/www/html/` 下に存在している
なのでここのファイルを消してやればいい
削除となれば `rm -rf` コマンドの出番だ

```
sudo rm -rf /var/www/html/*
```
※自分の場合は `/var/www/html/` 下にWordPress用ファイル・ディレクトリしかなかったのでこれでOKだったが、それ以外のファイル・ディレクトリを置いている場合だと、それも消えてしまうので、 `*` 使わずちゃんとWordPress用ファイル・ディレクトリ名のみ指定して1つ1つ消していくこと

削除できたら（ `ls` などで確認）、再度同じファイルを `/var/www/html/` 下にコピー `cp` なり移動 `mv` なりして置き直し、 `chown` での権限付与、 ` systemctl restart httpd.service` での再起動など必要な手順をちゃんと踏んでおく


…さて、これで「いかがでしたか？」されたWordPressの再インストール手順を、WrodPressのコントロールパネルに入らずにできたはずだ

改めてElastic IPとして設定したIPにアクセスすると…

ちゃんと「WordPressにようこそ」画面が表示された
今度は過程踏んで行くと、データベースとの紐づけも再度やり直すことができた

これでOKのはず

設定する際はちゃんと必要な手順踏んでないと面倒なことになるよ、というだけの話でした
ブラウザでDRM保護されたものをスクショする

注意：これは違法行為を推奨するものではありません

ブラウザではDRM保護されたコンテンツがスクショができなくなっています(スクショをしようとしたら黒くなります)

![wakaba.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785521/436ed7be-aa84-204c-5aaf-862bbeaba4e2.png)

ですが、これには抜け穴があり、パフォーマンスタブから実行できる、パフォーマンスを計測する機能を実行するとスクショができてしまう問題があります
DRM保護の趣旨を踏まえると、これができてしまうのは問題なのでは？と思うのですが、難しいところですね...

![performance.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3785521/24420dc4-77bf-87dc-225e-96b6bcc3b7d8.png)
5分で読むReact豆知識 | 入門 | 第10回: OnClickの即時関数に注意

# はじめに
このシリーズは、Reactの公式ページなどから特に重要と感じたトピックを選び出し、何度でも読み返せるよう簡潔にまとめたものです。:orange_book:

**進捗管理には「いいね」か「ブックマーク」がお勧め**
さらに僕のモチベーションアップになります！:thumbsup:よろしくお願いいたします！:thumbsup:

**[シリーズ一覧はこちら](https://qiita.com/mr_noppo/items/9f2841fd0bc574a4685c)**

# OnClickの即時関数に注意

このボタンタグ、onClickについている関数はいつ動くと思いますか？

```react
<button onClick={handleClick()}>ボタン</button>
```

実はレンダリングされてすぐなんです。

```react
{/*OK:渡すことでイベントハンドラ関数がクリックに反応する*/}
<button onClick={handleClick}>ボタン</button>
{/*NG：()があるとレンダー後すぐに発火する*/}
<button onClick={handleClick()}>ボタン</button>
{/*アロー関数で定義するのも可能*/}
<button onClick={() => {alert('test2')}}>ボタン</button>

```

これは{}で囲われたものはJavaScriptとして扱われるため、()をつけると関数実行と認識し即時実行されるためです。
イベントハンドラへ関数定義を渡すときは()をつけないよう気をつけましょう。

# 参考サイト
より詳しく学びたい方はこちら

https://ja.react.dev/learn/responding-to-events
5分で読むReact豆知識 | 入門 | 第9回: レンダリングの落とし穴

# はじめに
このシリーズは、Reactの公式ページなどから特に重要と感じたトピックを選び出し、何度でも読み返せるよう簡潔にまとめたものです。:orange_book:

**進捗管理には「いいね」か「ブックマーク」がお勧め**
さらに僕のモチベーションアップになります！:thumbsup:よろしくお願いいたします！:thumbsup:

**[シリーズ一覧はこちら](https://qiita.com/mr_noppo/items/9f2841fd0bc574a4685c)**

# stateで定義したオブジェクトを直接更新してはいけない理由

## このパターンはレンダリングされる？-①
下記のコードで表示されたボタンを押した時に、画面は最新の値でレンダリングされるでしょうか？

```react
export default function ObjectState() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });
  return (
    <>
        <button
          onClick={() => {
            position.x = position.x+1;
            position.y = position.y+1;
          }}>up
        </button>
        <div>
          {position.x}
        </div>
    </>
  );
}
```

結果はレンダリングされません。
レンダリングされない理由は、setPosition関数を利用していないためです。オブジェクト自体の参照値を見ているため、オブジェクトの中身を直接変更してもReactはその変更を検知しません。このため、上記の更新方法はやめましょう。

更新例〜スプレット構文を使う〜
```react
setPosition({
  ...position,
  y: e.clientY
});
```

更新例〜ローカルミューテーションを利用した更新〜
```react
const nextPosition = {};
nextPosition.x = e.clientX;
nextPosition.y = e.clientY;
setPosition(nextPosition);
```


##このパターンはレンダリングされる？-②
下記のパターンは再レンダリングされるでしょうか？
```react
import { useRef ,useState } from 'react';

export default function Counter() {
  let countRef = useRef(0);
  const [count, setCount] = useState(0);

  function handleClick() {
    // This doesn't re-render the component!
    countRef.current = countRef.current + 1;
    setCount(0);
  }

  return (
    <button onClick={handleClick}>
      You clicked {countRef.current} times
    </button>
  );
}
```

答えは再レンダリングされません。
handleClickでsetCountを使っているから再レンダリングされるのでは？となりますがReactは前回の値と比較し変化しなければレンダリングのトリガーにはせずレンダリングをスキップします。


## ローカル変数はレンダー間で保持されない

hooksでuseStateやuseRefを使う理由の一つは、レンダー間で値を保持することです。

ローカル変数は、レンダリング間で保持されないため初期値に戻され、
useStateのようなレンダリングのトリガーがないため変更に気づけません。

下記のような処理を書いてもレンダリングはされないため注意しましょう。
```react
let count = 0; // 再レンダリングされると初期化されます
function handleClick() {
    count = count + 1;
}
<button onClick={handleClick}>
    next
<button/>
{/* 値の変更がトリガにならないため描画される値は変わりません */}
<div>{count + 1}<div/>
```

（参考ページ参照）ボタンをクリックしてカウントを上げていっても表示は変わらない。また、修正して再レンダリングが始まるとカウントは０に初期化されます。

# 参考サイト
より詳しく学びたい方はこちら

https://ja.react.dev/learn/updating-objects-in-state

https://ja.react.dev/learn/state-a-components-memory
WireguardがLogを吐かない問題の対処

# WireguardはLogを吐かない
Wireguardはログを吐かない。これに対する対応は[公式ページ](https://www.wireguard.com/quickstart/#debug-info)や[Arch Wiki](https://wiki.archlinux.jp/index.php/WireGuard#%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%83%AD%E3%82%B0%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B)で対応方法が載っている通り。
```sh
# modprobe wireguard
# echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
```
で対応できる。

## 起動時に実行したい
rc.localにでも書いとけ。

## systemdでやりたい
UNITファイルを書く
```ini:wireguard-logging-start.service
[Unit]
Description=Setup WireGuard Logging
After=network.target

[Service]
ExecStart=/usr/local/bin/wireguard-logging-start.sh
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=multi-user.target
```

もちろん`/usr/local/bin/wireguard-logging-start.sh`には上のコマンド二行が入る。

## ちょっとだけ効率化
通常`wg-quick@wg01`のようなスクリプトでmodprobeは実行されているはずなので、順序制御を入れれば少し簡略化できる。

```sh:wireguard-logging-start.sh
echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
```
```ini:wireguard-logging-start.service
[Unit]
Description=Start WireGuard Logging
After=wg-quick@wg01.service
Requires=wg-quick@wg01.service

[Service]
ExecStart=/usr/local/bin/wireguard-logging-start.sh
Type=oneshot
RemainAfterExit=true

[Install]
WantedBy=multi-user.target
```

### ドロップインで対応
上の方法では名前がwg01に限定されてしまうので、以下の方法の方が柔軟。
上の方が構造は単純なのでどちらでも。

```ini:/etc/systemd/system/wg-quick@.service.d/logging-start.conf
[Unit]
Description=Start WireGuard Logging
PartOf=wg-quick@.service

[Service]
ExecStartPost=/usr/local/bin/wireguard-logging-start.sh
```
mitmproxyのaddonを書いてみる

mitmproxyのaddonを書いてみる

リクエスト先をlocalhostに飛ばしたいとき
```python
def request(flow):
    if flow.request.pretty_host == "example.com":
        flow.request.host = "localhost"
        flow.request.port = 8889
        flow.request.scheme = "http"
        flow.request.headers["Host"] = "localhost"
        flow.request.headers["Content-Type"] = "text/plain"
```

レスポンスを加工したいとき
```python
def response(flow: http.HTTPFlow) -> None:
    # 略
```

content viewを追加したいとき
```python
from mitmproxy import contentviews, flow, http
from mitmproxy.contentviews.json import format_json, parse_json, PARSE_ERROR

class XXXX(contentviews.View):
    name = "content viewの名前"

    # 戻り値が表示する内容
    # parse_jsonにjsonを渡すと色付けして表示してくれるので、加工したデータをjsonにして渡すとよい
    def __call__(
        self,
        data: bytes,
        *,
        content_type: Optional[str] = None,
        flow: Optional[flow.Flow] = None,
        http_message: Optional[http.Message] = None,
        **unknown_metadata,
    ) -> contentviews.TViewResult:
        json_body = parse_json(data)
        if json_body is not PARSE_ERROR:
            return self.name, format_json(json_body)

    # content viewでこれが選択される優先順位を指定する
    # 数字が大きいほうが優先順位が高い
    def render_priority(
        self,
        data: bytes,
        *,
        content_type: Optional[str] = None,
        flow: Optional[flow.Flow] = None,
        http_message: Optional[http.Message] = None,
        **unknown_metadata,
    ) -> float:
        if "XXXXX" in http_message.headers:
            return 2
        else:
            return 0


xxx = XXX()

def load(l):
    contentviews.add(xxx)

def done():
    contentviews.remove(xxx)
```

他にも、protobufをparseしたりできますし、androidのtermux上でも同様に動かせるので、発送次第では色々なことができます
5分で読むReact豆知識 | 入門 | 第8回: state配列の更新

# はじめに
このシリーズは、Reactの公式ページなどから特に重要と感じたトピックを選び出し、何度でも読み返せるよう簡潔にまとめたものです。:orange_book:

**進捗管理には「いいね」か「ブックマーク」がお勧め**
さらに僕のモチベーションアップになります！:thumbsup:よろしくお願いいたします！:thumbsup:

**[シリーズ一覧はこちら](https://qiita.com/mr_noppo/items/9f2841fd0bc574a4685c)**

# state配列の更新方法

配列の更新を行いたい場合は、新しい配列を作成し更新を行います。

0.なぜ新しい配列を作成するのか？
1.末尾追加
2.指定の要素削除
3.指定の要素置換
4.任意の位置への挿入
5.配列自体の変更（Sort,Reverce)

# 0.なぜ新しい配列を作成するのか？
例えば下記の場合、stateでエラーも発生せず無事に更新できているように見えます。

```jsx
const [artists, setArtists] = useState([]);
...
artists.push({
  id: nextId++,
  name: name,
}
```
ただし、この場合だと更新用関数を使用していないため、再レンダリングが発生しません。Reactの書き方としてはルール違反していることになります。
stateの配列にsetArtistsを経由して追加したいだけの場合、どうすればいいでしょうか？
そこで配列のコピー（再定義）が必要になります。

```jsx
const [artists, setArtists] = useState([]);
...
setArtists( 
  [
    ...artists, //配列をスプレット構文でシャローコピーします。
    { id: nextId++, name: name } //新しい情報を定義
  ]
);
```

# 1.末尾追加
スプレット構文を使用することで新しい配列の作成を行います。
```jsx
setArtists([
  ...artists,
  { id: nextId++, name: name }
])
```

# 2.指定の要素削除
filterを使用し、指定した要素以外を抽出し配列の作成を行います。
```jsx
setArtists(
 artists.filter(artist =>
   artist.id !== targetId
 )
)
```

# 3.指定の要素置換
mapを使用します。
```jsx
setArtists(artists.map(artist => {
  if (artist.id === targetId) {
    // 対象のnameだけ変更
    return {...artist,name: 'change'}
  } else {
    // それ以外はそのまま
    return artist;
  }
}))
```

# 4.任意の位置への挿入
sliceを使います。
sliceは半開区間で処理対象が決定します。
例えば(0,1)で指定した場合は0以上1未満となり、０が処理対象になります。

```jsx
const insertAt = 1;
setArtists([
  // インデックスが0の配列をコピー
  ...artists.slice(0, insertAt),
  // 新しい値の挿入
  { id: nextId++, name: name },
  // インデックスが1以上の配列をコピー
  ...artists.slice(insertAt)
])
```

# 5.配列自体の変更（Sort,Reverce)
配列を逆順にしたり、ソートしたりするのはmapやfilter等ではできません。
スプレット構文で配列をコピーしてから関数を使用し新しい配列をセットしましょう。
```jsx
// Sort(昇順)
const sortedList = [...list].sort((a, b) => a - b)
setList(sortedList)

// Reverce
const reverseList = [...list]
reverseList.reverse()
setList(nextList)
```

# 参考サイト
より詳しく学びたい方はこちら

https://ja.react.dev/learn/updating-arrays-in-state
Blenderのパーティクルでテクスチャーのアニメーションをさせる

質問チャットで
Blenderのパーティクルで散らしたオブジェクトで
発生時点から始まるアニメーションをさせたいという話があったので試してみたいと思います
![240051902.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/ee91dc1d-94cd-ff80-4f49-c029a08299d4.gif)


まず結論からいう単純にアニメーションテクスチャを利用する手段はない様子なので
**UVスクロール**によるアニメーションを利用します
また、この方法は現状**Cyclesレンダーのみ**で利用可能です

## UVスクロール
UVスクロールとは　UVマッピングの座標を変更することでアニメーションをさせる方法です
連続画像のアニメーションの場合、各コマを並べておいて時間の進行と共にコマ一枚分UV座標を変更します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/feb98a0f-72b4-a4e1-97a3-816ea7facccf.png)
UV座標は画像の各辺の長さを1.0とする座標なことは注意が必要です。
今回は連続画像になるよう10コマ分の画像を並べたものを用意しました
## UVスクロールのノード
まずUVスクロールの確認のノードです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/be279f6c-a68a-d6c9-9317-af22faf78226.png)
UV展開の時点で１コマ分の大きさにUVを設定してもいいのですが UV自体は画像の角に合わせた状態で
計算で全コマ数の除算をすることで一コマ分の幅にしています
その次の加算で　座標を横移動させています。
上の計算の方が分かりやすいかと思うのですが
パーティクルから得られる情報の関係で計算の順序をこう変更しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/34c30e90-ddb9-35c9-0ce9-7c27b76c55bc.png)
## パーティクル情報からのアニメーション
Blenderのシェーダーでは　パーティクル情報のノードでパーティクルが発生してからのフレーム数等の情報を取得できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/1b746b84-be2a-aff5-e258-c40752f625eb.png)
ここの **「経過時間」** がフレーム数を基準にしたパーティクルが発生してからの時間になるのですが
フレームとフレームの間の時間の間にも発生しているという計算になっているのか不要な少数値が入っているので、切り捨てで整値にしています
これは １つ前のキャプチャの「フレーム」のラベルのノードの位置につなげます

今回のノードの全体です　最初に触れたとおりCyclesでのみ動作します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/40bfa0ef-b587-5c05-4524-f18a32714043.png)

今回はパーティクルとシェーダーで実現することを考えましたが

~~ランダム配置をジオメトリノードで作成すればEeveeでも同様のことができるかもしれないですね。~~

ジオメトリノードで同様のことをやってみました

https://qiita.com/yukimituki11/items/c9f76c22b9506046f880
GitLabのAPIを叩いてグループ内のプロジェクト一覧を取得する

# はじめに
GitLabのグループ内に作成したリポジトリの一覧名前一覧を取得したいですが数が多すぎました。手動で一覧リストを作成したくなかったのでスクリプト化して一覧のjsonを作成しました。こちらの方がヒューマンエラーのなく取得できて便利だと思います。

# 環境
- Python 3.12.2

# ソース
スクリプトは下記の通りです。`gitlab_url`、`access_token`はそれぞれ環境の設定が必要です。

```python:name_list_builder.py
import requests
import json

def projects_json_builder():
    # GitLabのURLとアクセストークンを設定
    gitlab_url = "https://gitlab.com/api/v4/groups/グループID/projects"
    access_token = "gitlabのトークン"

    # リクエストヘッダーの設定
    headers = {"Private-Token": access_token}

    # APIを呼び出してプロジェクト一覧を取得
    response = requests.get(gitlab_url, headers=headers)

    # レスポンスをJSONとして取得
    projects = response.json()

    # 各プロジェクトのnameプロパティだけを含むリストを作成
    project_names = [{"name": project['name']} for project in projects]

    # JSONファイルに書き込む
    with open("project_names.json", "w") as json_file:
        json.dump(project_names, json_file, indent=4)


projects_json_builder()
```

# 実行してみる

下記コマンドを実行すると、`project_names.json`ファイルが生成されます。
```powershell
python name_list_builder.py
```

以下のような形式でjsonファイルが出力されます。

```json
[
    {
        "name": "フォルダ名1"
    },
    {
        "name": "フォルダ名2"
    },
    {
        "name": "フォルダ名3"
    }
]
```




# 参考
- https://gitlab-docs.creationline.com/ee/api/projects.html
Google App Script：GmailAppのmoveToTrash()で「Gmail operation not allowed.」

Webで見つけた手順をそのまま試したつもりだが躓いたため解決方法をメモ

# やりたかったこと
Gmailの特定のカテゴリ内のメールのうち、受信日時から一定期間経過したものを自動的に削除したい。

# 試したこと
下記のサイトを参考にGoogle App Scriptでプロジェクトを作成し実行。
https://tech.torico-corp.com/blog/delete-old-gmails-via-google-apps-script/

~~~JavaScript:NG.gs
function deleteOldGmailsPromoSocial() {
  // 下記のメールを削除する
  // ・プロモーションカテゴリまたはソーシャルカテゴリ
  // ・100日以上前
  // ・スター付を除く

  var queries = [
    'category:promotions',
    'category:social'
  ];
 
  for(var i=0; i < queries.length; i++){
    var query = queries[i];
    Logger.log('削除開始:' + query);
    var criteria = '' + query + ' older_than:100d -is:starred'
    var deleteThreads = GmailApp.search(criteria);
    Logger.log('該当スレッド: ' + deleteThreads.length + '件');
    for (var j = 0; j < 100; j++) {
      deleteThreads[j].moveToTrash();
    }
    Logger.log('' + query + 'の処理を終了')
  }
}
~~~

# 結果
~~~
Exception: Gmail operation not allowed. 
    at deleteOldGmailsPromoSocial(コード:19:24)
~~~

# 解決策
以下のようにGmailApp.searchで一度に処理する数を限定することでエラーが発生しづらくなった（数を指定しない場合は上限500件）。
~~~JavaScript:OK.gs
function deleteOldGmailsPromoSocial() {
  // 下記のメールを削除する
  // ・プロモーションカテゴリまたはソーシャルカテゴリ
  // ・100日以上前
  // ・スター付を除く

  var queries = [
    'category:promotions',
    'category:social'
  ];
 
  for(var i=0; i < queries.length; i++){
    var query = queries[i];
    Logger.log('削除開始:' + query);
    var criteria = '' + query + ' older_than:100d -is:starred'
    var deleteThreads = GmailApp.search(criteria, 0, 100);
    Logger.log('該当スレッド: ' + deleteThreads.length + '件');
    for (var j = 0; j < 100; j++) {
      deleteThreads[j].moveToTrash();
    }
    Logger.log('' + query + 'の処理を終了')
  }
}
~~~
~~~diff_JavaScript
-   var deleteThreads = GmailApp.search(criteria);
+   var deleteThreads = GmailApp.search(criteria, 0, 100);
~~~

# 残課題
- 文言から権限の問題に見受けられる「Gmail operation not allowed.」の発生頻度が処理数を増減させることで変わる原因が不明
- 処理数を減らせば減らすほど「Gmail operation not allowed.」の発生頻度が下がるが0にはならないため根本的な解決とはなっていない

## 予想
一定の処理時間が許可されていない？処理数を減らすことで処理時間が短くなるため「Gmail operation not allowed.」の発生頻度は下がるが、処理対象の容量が大きい時に処理時間が延びることによって発生を0にはできないのではないか？
streamstatsを使ってみた

`Splunk Version 9.2.1(Free_無課金)`

streamstatsは、イベント1つずつを参照して集計していく。

集計対象がどのイベントか分かりにくい時があるので、
そういう時はvalues(field名) を使うと、対象イベントが見やすい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789518/ffd92cb2-8b44-63f5-fbd8-11fa51b6ff50.png)

# window=xx (default 0) について
window=2 で集計するとこんな感じ。
注意点として、1回目の処理は window=2 にしても1つ目のデータしか参照しない。
処理対象は、(対象データ + 前のデータ) らしい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789518/ecfc7c34-f72e-2dcc-e799-05f73b6b5a79.png)

後のデータを、前のデータに持ってきたい場合は、
一旦「sort - 」で降順にして集計後、「sort」しなおす必要がある。~~多少面倒~~。
　※sortは 0 を付けないと、10000件までしか結果反映してくれないので注意。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789518/aeab9c6c-93d7-82cc-7de5-5db8416d188e.png)

# SPL例
```ruby:qiita.rb
| makeresults format=csv
data="my_cnt,my_group,my_data
1,g1,g1-aaa
2,g1,g1-bbb
3,g1,g1-ccc
4,g1,g1-ddd"
| table my_cnt my_group my_data

| sort - 0 my_cnt
| streamstats window=2 current=t first(my_data)
| sort 0 my_cnt
```

他の引数も、時間があるとき追記します。


# 公式URL
https://docs.splunk.com/Documentation/SplunkCloud/9.1.2312/SearchReference/Streamstats

# 記事終了
atcoder-cli で提出時に Matched languages were not narrowed down to one. が起きた際の暫定対応

# 環境

Mac, zsh, atcoder-cli インストール済み。

```
> acc config

oj-path: /usr/local/bin/oj
default-contest-dirname-format: {ContestID}
default-task-dirname-format: {tasklabel}
default-test-dirname-format: test
default-task-choice: all
default-template: py
```

# エラー

atcoder-cli で`acc s`を行いPythonのプログラムを提出しようとした際に以下のエラーが発生。

```
...

[INFO] PyPy is available for Python interpreter
[ERROR] Matched languages were not narrowed down to one.
[INFO] You have to choose:
5055 (Python (CPython 3.11.4))
5063 (Python (Mambaforge / CPython 3.10.10))
5082 (Python (Cython 0.29.34))
[INFO] save cookie to: /Users/***/Library/Application Support/online-judge-tools/cookie.jar
```

# 暫定対応
PyPyの番号が5078なので`~/.zshrc`に以下のエイリアスを追加。

`alias accs='acc s -- main.py --language 5078'`

`accs`で提出できるようになった。

# 参考
https://umamichang.dev/posts/2024-03-23/
SECとXRP訴訟の終焉が近づく中、Rebel Satoshi ($RECQ)のプレセールが注目の的に

専門家によれば、SECとRippleの訴訟が終結に近づいているとのことです。Rebel Satoshiの$RECQトークンは、120％の急上昇後に最も注目すべきコインとなりました。SECとRippleの法的闘争が終盤に差し掛かり、SECの最終回答がRippleの主張に対抗しています。投資家は、Rebel Satoshiの暗号通貨ICOに注目しており、これは現在最も投資に適したICOであると専門家は述べています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319526/62429c0e-33c5-99ee-2b3e-06b6d25fe001.png)


### SECが最終回答を提出
米国証券取引委員会（SEC）は、Ripple Labsに対する訴訟で最終回答を提出しました。SECはRippleの主張を争い、Rippleの行動が軽率であったとしています。裁判所がRippleの「公正な通知」防御を却下したにもかかわらず、SECはXRPの法的地位に関する不確実性が広がっていると主張しています。

Rippleの最高法務責任者であるスチュアート・アルデロティ氏は、SECの一貫性のない法律適用を批判しました。アルデロティ氏は、国際規制当局と比較して、明確な暗号通貨ライセンスフレームワークを持つSECの驚くべき行動を強調しました。このケースの結果は、暗号通貨規制のグローバルな前例となる可能性があります。

SECとRippleの法的闘争は、2020年に未登録証券提供の疑いでXRPの販売を巡って始まりました。アナリストは、最終判決が9月頃に出されると予測しています。SECの最新の回答は、訴訟の結論に向けた重要な展開であり、このケースの解決は暗号通貨業界に規制の明確さとXRPのようなデジタル資産の地位を形成する影響を与えるでしょう。

### Rebel Satoshiの$RECQが120％急上昇、さらなる184％の上昇を予測
Rebel Satoshiは、新たな分散化の時代を切り開くことを目指す新興のミームコインプロジェクトです。$RECQトークンは、Rebel Satoshiの基軸通貨であり、すべての取引、購入、賞金、および手数料の基盤となります。$RECQの最大供給量は30億枚に設定されています。$RECQは、5月に公募プレセールの第2ステージを$0.0044で開始しました。

$RECQが上場価格$0.0125に達した場合、初期ラウンド投資家は525％の利益を得ることができます。一方、第2ステージの投資家は、$RECQの価格が$0.0125に達すると184％の利益を見込むことができます。この成長の可能性により、$RECQは現在最も注目すべき暗号通貨となっています。

### 初心者向け解説:
- **ICO（Initial Coin Offering）**: 新しい暗号通貨やトークンを発行して資金を調達する方法。
- **SEC（米国証券取引委員会）**: 証券取引を監督する米国の規制機関。
- **XRP**: Rippleが開発したデジタル通貨で、迅速かつ低コストの国際送金を目的としている。

### リンク一覧:
- [Rebel Satoshi公式サイト](https://www.rebelsatoshi.com)
- [Rebel Satoshiの$RECQトークン情報](https://recq.rebelsatoshi.com/?utm_source=5491&utm_medium=News&utm_id=Fox)
- [Rebel RedのTelegram](https://t.me/RBLZRED)

### キーワード: SEC, Ripple, XRP, Rebel Satoshi, $RECQ, ICO, 暗号通貨, プレセール, 法的闘争, 規制
ESP32S3にプログラムを書き込む際のエラーとその解消方法

Title in English: Error and the solution when using ESP32S3 with PlatformIO.

## エラー内容
```zsh:terminal
A fatal error occurred: Could not open /dev/cu.usbserial-10, the port doesn't exist
```
ポートが存在しない（ので書き込めない）というエラー。
自分はESP32S3系に変えたらこのエラーが頻出していた。

### Env
- MacOS Sonoma
- ArduinoIDE
- PlatformIO (PIO)
- ESP32-S3-WROOM-1


## ESP32S3のモードについて
今まではそうではなかったと記憶しているが、ブートローダーモードと実行モードというのが存在している。[公式](https://docs.espressif.com/projects/esptool/en/latest/esp32s3/advanced-topics/boot-mode-selection.html)にもそう書いてある。

### モード切り替え
ボードにはBOOTボタンとRESETボタンが存在している。
デフォルトでは実行モードで起動する。
BOOTボタンを押しながらRESETボタンを1回押し、BOOTボタンを離すとブートローダーモードへ移行する。
この時に書き込みが可能になる。
RESETボタンを押すと実行モードになる。

電源投入時にもモード選択できる。
BOOTボタンを押しながら電源投入するとブートローダーモードになる。
書き込み終了後、RESETボタンを押すと実行モードになる。

RESETボタンの働きは電源を一旦落とし、再度投入する働きをする。

## PlatformIOでの問題
ArduinoIDEでは以上のことをすれば書き込めるようになるのだが、PlatformIOはそうもいかなかった。
PIOでは書き込み時に手動でシリアル通信をStopする必要があった。（以下の図の"Stop Monitoring"）
![Screenshot 2024-05-20 at 0.11.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123138/80ea32cb-17cc-32f7-950f-1293c899ce99.png)
ArduinoIDEではブートローダーモード時にシリアル通信がStopになるため、この問題は起こらなかったと考える。

## 終わりに
少々めんどくさいが、VScodeが使えるという利点は大きい。
マルコフ過程的な物を連想配列で作ってみた

私の勤務先では自然言語処理が非常に盛ん(なのかな？)で昔マルコフ連鎖を使った文章生成や楽曲生成などをしていた方もいらしたそうな。
そこでライブラリに頼らず(データフレームだけPandas使う)ゼロからマルコフ過程を作ってみようと思った。
自分の記憶が正しければある状態からある状態への遷移する確率だと思うのでそれベースで作りました。
# 関数
```Python3
import pandas as pd
def marcov(arr):
    dic = {}
    for i in range(1, len(arr)):
        if arr[i-1] in dic:
            if arr[i] in dic[arr[i-1]]:
                dic[arr[i-1]][arr[i]] = dic[arr[i-1]][arr[i]] + 1
            else:
                dic[arr[i-1]][arr[i]] = 1
        else:
            dic[arr[i-1]] = {}
            dic[arr[i-1]][arr[i]] = 1
    col = []
    sumval = {}
    for val in dic:
        col.append(val)
        for val2 in dic[val]:
            col.append(val2)
            try:
                sumval[val] = sumval[val] + dic[val][val2]
            except:
                sumval[val] = dic[val][val2]
    col = list(set(col))
    for val in dic:
        for val2 in dic[val]:
            dic[val][val2] = dic[val][val2] / sumval[val]
    table = []
    for val in col:
        tmp = []
        for val2 in col:
            try:
                tmp.append(dic[val][val2])
            except:
                tmp.append(0)
        table.append(tmp)
    df = pd.DataFrame(table)
    df.columns = col
    df.index = col
    return df, dic
```
## 動かしてみる
```Python3
moji = ["A", "B", "A", "A", "B", "C", "B", "A", "B"]
df, dic = marcov(moji)
df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/1df7eca8-9121-bcbd-b768-9bd45250e5c8.png)



出力されるデータフレームです。
基本情報とかの教科書だと行列だけ見ますが分かりやすく表にしました。
見方としてはインデックスが今の状態でカラムが次の状態です。
一般的には逆だそうですが自分はこっちの方が見やすいので。

### 過程のデータ
```Python3
dic
```
```
{'A': {'B': 0.75, 'A': 0.25},
 'B': {'A': 0.6666666666666666, 'C': 0.3333333333333333},
 'C': {'B': 1.0}}
```
# 発展
これを使って実際に配列(今回は配列を使っているため)を生成するには先ほどのデータフレームを降順にして確率を累積化し、0から1まで取った値の中でどのデータとどのデータの間のデータを出力する事で生成する事が出来ます。

※コンピュータの計算は2進数なので10進数に表記した時に小数点以下は誤差があるため連想配列の合計値は1にならない事が多いです。

# まとめ
自然言語処理技術難しい
ReactのuseStateについて解説したいと思います。

## React:useStateについて

### 使用する目的

React において、クライアントサイドでデータを更新するためには、再レンダリングが必要です。通常の変数管理では再レンダリングを起こすことができません。そこで useState を使用することで、指定した変数に変化があった場合に再レンダリングが発生し、画面が更新されます。

### 使い方
次に、useState の使い方について説明します。以下の例では、age という変数を管理します。

```page.tsx

import React, { useState } from 'react';

const ExampleComponent = () => {
  const [age, setAge] = useState(0);
  
  const increaseAge = () => {
    setAge(age + 1);
  };

  return (
    <div>
      <p>Age: {age}</p>
      <button onClick={increaseAge}>Increase Age</button>
    </div>
  );
};

export default ExampleComponent;

```


【POINT】
- useState の返り値は2つあり、最初の値 (age) は初期値を示し、2つ目の値 (setAge) は状態を更新するための関数です
- useState(0) は初期値を0に設定します
- 状態を更新するためには、setAge 関数を使用します。例えば、setAge(age + 1) で age を1つ増やすことができます


### 再レンダリングのタイミング
useState を使用することで再レンダリングが発生するタイミングは以下の通りです。

- 初回読み込み時
- set 関数が実行されたタイミング
- 親コンポーネントで再レンダリングが発生した場合

### Next.js での注意点
Next.js では、デフォルトでサーバーサイドレンダリングが行われます。しかし、useState を使用する場合はクライアントサイドでのレンダリングが必要です。これを指定するために、コンポーネントの冒頭に use client を宣言する必要があります


```page.tsx
'use client';

import React, { useState } from 'react';

(先ほどのコードと同じ)
```

### 補足:SSG,SSR,CSRについて
これらの用語は、レスポンスのデータをどこで、いつレンダリングするのかを示すものです。

- SSG (Static Site Generation):

・どこで: ビルド時にサーバーでレンダリング
・いつ: ビルド時に一度だけレンダリングされ、その後は静的ファイルとして配信
・特徴: 高速なページ読み込み、SEOに強い。

- SSR (Server-Side Rendering):

・どこで: リクエスト時にサーバーでレンダリング
・いつ: ユーザーのリクエストごとにレンダリング
・特徴: 動的なコンテンツが必要な場合に有効、SEOに強いがレスポンス時間が長くなる場合がある

- CSR (Client-Side Rendering):

・どこで: クライアント（ブラウザ）でレンダリング
・いつ: JavaScriptがブラウザで実行されたときにレンダリング
・特徴: 初回ロードが遅くなるが、その後のページ間移動が速い、SEOに弱い
LeetCodeを解いて自分の技術の棚卸しをする

こんばんは．ふじえもんです．社会人いちねんめとしてエンジニアの道を駆け出しました．

## はじめに
最近，自分がどんな技術をどこまで理解して自分のものとして使えるかを整理しています．
いわゆる技術の棚卸し．

整理し始めたきっかけとしては，これまでチーム開発や個人開発，様々な言語の習得をしてきたものの，だれかに説明するとき，あれ，自分って何がどこまでできるんだっけとなったからです．

あとエンジニアである以上，問題解決能力を養う場を定期的に自分の生活に取り入れてみたいと思いました．


## 最近解いている問題
[LeetCode](https://leetcode.com/)　というコードインタビュー対策のための問題を解くことができるサイトを使っています．

最近解いているのは，Dynamic Programming(DP)やSQL50です．
最初に解いたフィボナッチ数列は，思った以上に簡単にコードに起こせるんだなと気づきました．

SQLは，大学の講義でやった以来で，頭の片隅にあった断片的な知識を手繰り寄せて，なんとか勘を取り戻してきました．

今のところ，気になったStudy Planを選んで気ままに解いています．


## 何ができるようになりたいか
- 典型的なアルゴリズムの問題は何も考えず解けるようになりたい．

ある概念をコードに起こせるようになることが今の自分に必要だと感じました．

何かプログラムを書くときに，愚直なアイデアは思いついて実装できるんですが，どうにも無駄な処理だったり回り道をしているような感覚があって，サクッと解けてる感を味わったことがあまりないからです．


## これからやること

- 問題を解いて何か気づきがあったり面白いと思った問題があれば記事に書いて公開する．

普段，Scrapboxを使って自分だけが見るメモや日記を書いているのですが，「記事を公開する」ことで
- 自分の気づきや学びを人に伝える，言語化能力を養うことができる
- 自分の活動をや客観視して自己肯定する機会を増やす

と考えました．

1週間に1本ぐらいのペースで気楽に記事を出せればいいかなと思っています．


以上です．ここまで読んでいただきありがとうございます．
再び注目を集めるEigenLayerとその顧問割り当ての論争

EigenLayerはそのEIGENトークン配分に関する問題で再び注目を集めています。Ethereumの研究者であるジャスティン・ドレイクが、最近の情報公開を行いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/319526/37cfdbbe-ae54-d90c-55ee-e0571fdcf3e5.png)

顧問としての特典
ドレイク氏は、Eigen Foundationの顧問になったことを確認しました。彼は、コミュニティが透明性を求めることに同意し、EIGENトークンの配分について説明しました。ドレイク氏は、顧問として大きなEIGENトークンのインセンティブを受け取る可能性があり、これが彼の暗号資産ポートフォリオの他の資産の総額を上回る可能性があると述べました。

Ethereumエコシステムへの貢献
ドレイク氏は、顧問として得た収益をEthereumエコシステム内のプロジェクトに再投資または寄付する計画を持っています。彼は、この顧問職がEthereumの利益と一致している限り続けると述べました。

Ethereum Foundationの安全性
ドレイク氏は、EigenLayerの顧問になるまでの経緯を説明し、そのプロセスが1年以上かかったことを明らかにしました。また、マーケティング素材に載らないように求め、リスク軽減に対する建設的な批判を行うと述べました。

ドレイク氏は、EigenLayerがEthereum Foundation（EF）を腐敗させる可能性について懸念を示す人がいるかもしれないと述べましたが、EFは300人以上の大きな組織であり、3人のEF関係者がその倫理を損なうことはないと強調しました。

関連リンク
ジャスティン・ドレイクのTwitter
https://twitter.com/JustinDrake

EigenLayer公式サイト
https://www.eigenlayer.xyz/
いまから始めるRaycast vol.1 【生産性向上】

https://www.raycast.com/why

# はじめに
みなさんはRaycastをご存じですか?
最近はかなり有名になってきたような気もしますが、まだ知らない・使ったことがない方も相当数おられると思います。

そんな方に向けて、今回はRaycastについて紹介したいと思います。

## なにができるの？

Macを普段使ったり、開発をする上で便利な機能を沢山提供してくれます。
例えば、
- 昨日コピペしたテキストをもう一度ペーストしたいけど探すのが面倒
- よく使う文章があるので、毎回コピペしている
- Slackのウィンドウがどこにあるのかわからないので三本指でスライドしまくっている
- 撮影したスクリーンショット、ギリギリ間に合わなくてFinderへ探しにいかなきゃならない
- MacのFinderでドキュメントや写真を検索したいけどめちゃくちゃに時間がかかる

などなど...

これらは全て、このRaycastのアプリひとつで解決しちゃいます。

さらにさらに、Raycastは有志の方が作成した **拡張機能(Extensions)** をインストールすることでさらに多くの機能を使うことができます。(拡張機能はオープンソースなので、安全性も高いです)

## Raycastとは
ここまでで、「なんか便利なんだな」ってところはわかってもらえたかと思うので、Raycast自体について紹介していこうと思います。

アプリの分類でいうと、`ランチャーアプリ`というものに属します。
類似したアプリで言うと、`Spotlight`や`Alfred`が該当しますね。

SpotlightはMacにデフォルトでインストールされているアプリで、`⌘+スペース`で起動するやつですね。

AlfredはRaycastのライバルといったところで、かなり似たような機能を提供しているアプリです。

AlfredとRaycastどちらを使うかは正直個人の好みによるところが大きい気がしますが、以下に比較をしてみたいと思います。

|         | UI  | 操作感 | 無料版の機能 | 拡張機能 |
|:-:      |:-:  |:-:|:-:|:-:|
| Raycast | 今風 | 直感的| ほとんど無料  | 非常に豊富かつ無料  |
| Alfred  | 古い |  わかりずらい |  比較すると少ない | 存在するが有料 |

といったところでしょうか、筆者はAlfredをあまり使ったことがないので認識違いがあったらもうしわけありません🙇🏻‍♂️

UIに関しては、以下の公式サイトからどんなもんかを実際見てもらうのがいいです。
非常にスマートで、UI/UXともに優れ使用するモチベも上がります!

https://www.raycast.com/

さらに開発者向けの話をすると、拡張機能はReactで書くことができるため、Reactの知識がある方は欲しい機能を手軽に実装、使用できるのも嬉しいポイントだと感じます。(でも大抵すでに誰かが作ってる)

## インストールしてみよう
公式のDownload、または`homebrew`でインストールすることが可能です。

https://www.raycast.com/

```zsh:brewを使用する場合
brew install --cask raycast
```

インストールができたら、`option＋Space`で起動しましょう。
これだけで最低限のセットアップは完了です！

:::note info
個人的には、`⌘+space`で起動するようにしておくのがおすすめです。
![スクリーンショット 2024-05-19 23.24.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/b100e8ca-391e-1d4f-4574-8e8ce597618f.png)

この際、Macの設定(システム設定>キーボード>キーボードショートカット>Spotlight)から`Spotlight検索を表示`をオフに変更する必要があります。
![スクリーンショット 2024-05-19 23.27.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/e20927c2-9eec-564a-d79a-0211f15a3b97.png)

:::

以降、どの画面にいても`option＋Space(⌘+space)`でさまざまなことを実行できるようになります！

## 主な機能の紹介

### あらゆるアプリをその場で起動、アプリのランチャー機能
Raycastを開いて、そのまま好きなアプリの名前を入力してみてください。

出てきたら、矢印キーで選んで`Enter`で起動ができます！
すでに開いているアプリの場合、ウィンドウを移動してくれます。

これから大量のウィンドウの中、目当てのアプリを探して右往左往することはなくなります。


### いつでもどこでもコピー履歴を表示、クリップボード機能
Raycastを開いて、`Clipborard History`と入力してみてください。(実際には「clip」の段階で一番上に出ると思います)
以下の項目が表示されると思うので、`Enter`で開いてみましょう！

![スクリーンショット 2024-05-19 23.32.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2778030/66b7f3db-fbe7-58b6-66dd-8c51c88aebfe.png)

最初はカラッポかもしれませんが、試しにブラウザでもエディタでもなんでもいいのでテキストをコピーしてみましょう。

そして再度Raycastを開いて、`Clipborard History`を起動してみてください。

コピーしたテキストが新たに追加されていると思います。
こうしてコピーしたものがどんどん溜まっていくことになります！

また、上の検索バーで検索をかけることもできます。
三日前、一週間前にコピーしたものでも断片さえ覚えていれば、一瞬で探し出すことが可能です！

:::note info
ついでに紹介しますが、Raycastの左下に`Action ⌘K`と書かれているのにお気づきでしょうか?
試しに、`⌘+K`を押下してみてください。
そこからさまざまなアクションを起こすことができます。

クリップボード内なら例えば、選んだコピーを常に一番上に固定(Pin)、共有する、ファイルに保存、編集、削除などのアクションを起こすことができます。

覚えておくと色々な機能が追加で使えるような感じなのでぜひ覚えておいてください！
:::

### 終わりに
最後まで見ていただきありがとうございました！

Raycastは本当に好きなアプリなので、一応シリーズ物として書いていけたらなと思います。
次回はRaycastビルトインの機能について深掘りしていく予定です！
PostgreSQLでの別スキーマへのテーブルコピーのやり方

最近別スキーマにテーブルコピーして、検証環境を作ることがあるので、よく使うテーブルコピーのコマンドをメモしておきます。

テーブルコピーのやり方。
INCLUDING ALLによってテーブル定義などがコピーできる。
とくに、シーケンスを一緒にもっていく必要がある場合重要。
```
CREATE TABLE コピー先スキーマ名.テーブル名 (LIKE コピー元スキーマ.テーブル名 INCLUDING ALL)
```
データの移行方法。
当たり前だが、WHERE句に条件を設定すれば、直近1年分だけ移行などもできた。
```
INSERT INTO コピー先スキーマ名.テーブル名 SELECT * FROM コピー元スキーマ.テーブル名
```
[Blender]Unity用のLODを作成する

# 概要
Unityで３Dモデルを扱う時、ポリゴン数が多いモデルほど処理にかかる負荷が大きくなります。LODはその負荷を減らすためのもので、カメラから遠い位置にあるオブジェクトはポリゴン数を少なくして処理の負荷を減らそうというものです。
# 本文
UnityにはLODを作成するアセットやLODが用意されたモデルもありますが、Blenderを使えば自分でLODを用意することもできます。
## Blender
時計のモデルを用意しました。
![スクリーンショット 2024-05-19 230952.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/e4e47766-3d0f-f433-acd6-745dfea0f51d.png)

![スクリーンショット 2024-05-19 231143.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/23a0dec5-fed8-5180-5124-f7b55152cdbb.png)


モデルを選択してShift+Dで複製→そのまま右クリックして移動をキャンセル。これを二回繰り返します。
モデルが３個になって以下のようになります。

![スクリーンショット 2024-05-19 225419.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/00e360b3-3597-e263-3e0f-cb46bbc68058.png)

１つ目、３つ目のモデルを非表示にして（目のアイコンを閉じる）２つ目のモデルを選択状態にします。

![スクリーンショット 2024-05-19 225439.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/22e59d78-36cb-c2dc-c415-9c844089f551.png)

モディファイア「Decimate」を設定してRatioを適度に下げることでポリゴン数を減らします。
![スクリーンショット 2024-05-19 225650.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/0f8a58e8-485b-f1c3-002b-850001225c10.png)
値が決まったらApplyしてください。
![スクリーンショット 2024-05-19 225959.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/6633e08c-bc69-d1e6-7db5-f940cb7ffb4a.png)

次に１つ目、２つ目のモデルを非表示にして（目のアイコンを閉じる）３つ目のモデルを選択状態にします。先程と同じ手順でポリゴン数を減らします。さらに少ないRatioを設定してください。
![スクリーンショット 2024-05-19 225711.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/71a304f7-2853-e38c-9c2a-edeb8e3ad888.png)

![スクリーンショット 2024-05-19 225840.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/792ff999-c616-06ab-45fb-c340e4665a50.png)

ここまで設定したらそれぞれのモデルの名前をUnity用に変更します。
（名前）_LOD（連番）

:::note info
この名前付けのルールに従うことでUnityに持って行った時自動でLODの設定をしてもらえるようになります。上手くいかなかった時は名前を見直してみてください。

名前の変更は項目を選んだ後F2キーです。
:::
３つすべてを表示状態にして（目のアイコンを全部開く）３つを選択状態にします。
![スクリーンショット 2024-05-19 230216.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/5046e774-e61c-69dc-8da4-a7ced64582d7.png)
File > Export > FBX(.fbx)
![スクリーンショット 2024-05-19 230254.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/429ab345-af0d-7d68-c73a-5abf893525ea.png)

FBX形式で出力します。
![スクリーンショット 2024-05-19 230346.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/9c7bed81-cd8a-6258-c552-cafce7439480.png)

## Unity
プロジェクトに出力したFBXファイルをインポートします（Project内の好きなところにドラッグアンドドロップ）
それをScene上に配置して
![スクリーンショット 2024-05-19 230646.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/bff06769-9db0-74f9-f405-232a922ddc0d.png)
Inspectorを確認するとLODの設定が自動で行われていることが確認できます。
![スクリーンショット 2024-05-19 230657.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/2656e475-653b-ec9f-7388-b44f599ffdda.png)
LODの距離やFBXの細かい設定などは好みに合わせて行ってください:raised_hands_tone2:

# 補足
参考動画
[How to Optimize Game Assets with LOD Versions in Blender](https://www.youtube.com/watch?v=LsGMT9mrMYM): シンプルな解説
[GAME ASSET BEGINNER TUTORIAL - Creating LOD In Blender & Unity (Part 5/5)](https://www.youtube.com/watch?v=v1xBGuxTqfI): さらに詳細な解説
【浦島太郎状態】久々にJavaをいじってみたら色々便利になってた件

# もともとJava使い
自分がこの業界入った時はJava 1.5ぐらいでstruts 1.3でWebシステム作るのがモダンだった古き悪しき時代を乗り越えてきた、え～すけさんですよ。
そんなわけで、自分は転職組なので前の会社の話ですが、もれなく入社してから最初のプログラム研修はJavaを使って生のServlet/JSPや、strutsを使ってWEBシステムを作らされた記憶があり、大学でJavaのアプレットでなんか作るみたいなことはやってましたが、Webという覚えることだらけの魔境に四苦八苦してました。
ほら、WebってJavaだけじゃなくて、HTML/JavaScript/CSSが漏れなくついてくるから、覚えること増えるじゃん？

そんなこんなで、この業界に入ってから割とずっとJavaのプロジェクトをやってきており、実務ではJava 1.4/1.5/6/8/11ぐらいまでの記憶しかなかったですが、久々にAndroidStudioのバージョンアップしたら17以降を使えやコラみたいな警告が出てしまったので、今っていくつまで出てるのか調べたのと、Java8ぐらいが全盛期の人がどれだけうらしまたろう状態になってるかを簡単にまとめてみました。

# とりあえずラムダ
Java8から出てきたやつ。
jQueryとかNode使いからすると、なんだチェーン的なアレじゃんとなるが、基本構文しかやってない新人やJava6で満足してる勢からすると初見殺しのアレ。

```java:ラムダ
    public static void main(String[] args) {
        // アルファベット
        List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
                "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
                "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");
        // とりあえ頭からSysout
        list.forEach(System.out::println);

        // マルチスレッドでSysout
        list.parallelStream().forEach(System.out::println);

        // ソート(文字コード順)してSysout
        list.stream().sorted().forEach(System.out::println);

        // ソート(アルファベット順で(同じアルファベットなら小文字→大文字の順))してSysout
        list.stream().sorted(String::compareToIgnoreCase).forEach(System.out::println);
    }
```
メインの使い所としてはListやMapなどのコレクション系のクラスから、マッチするレコードを探すとか中身を編集して別のコレクションに変換するとか、単純にFor文書かないでループさせるとかぐらいかなと。
Java5/6ぐらいで出てきた拡張For文も結構衝撃だったけど、ラムダによってコード量が激減したのはかなりインパクトあった。

# switch文の進化
大量のif文をスッキリさせるSwitch文ですが、研修とかだとEnumや数値で判定して、その中でなにか処理を書くみたいなアレですが、今は値をそのまま返すことが出来るみたいです。

```java:Switch
    // 曜日Enum
    public enum Day { 
        SUNDAY, 
        MONDAY, 
        TUESDAY,
        WEDNESDAY, 
        THURSDAY, 
        FRIDAY, 
        SATURDAY; 
    }
    
    public static void main(String[] args) {
        Day day = Day.FRIDAY;
        String jpDayLabel = switch (day) {        
        	case SUNDAY -> "日";
        	case MONDAY -> "月";
        	case TUESDAY -> "火";
        	case WEDNESDAY -> "水";
        	case THURSDAY -> "木";
        	case FRIDAY -> "金";
        	case SATURDAY -> "日";
        	default -> "曜日不明"
        }
        System.out.println(jpDayLabel);
    }
```
->って書いてあるから、ラムダ的な書き方なのかしら？
->{ なんかの処理; return 結果;} みたいな書き方とかも出来るのかしら？

# 複数行の文字列の宣言
Javaで複数行の文字列を作るときって、
StringBuilderとかで\\nをアペンドしてとかやったり、行数分のString[]を定義してjoinとかで区切り文字に\\nとかいれてましたが、そんな可読性が下がることはしなくてもいいみたいです。
※テキストブロックっていう名前らしいです。
```java:複数行定義
    public static void main(String[] args) {
        // 文字列を宣言する時に　"""　って書くだけ。
        System.out.println("""
            1行目
            2行目
            3行目
            """);
    }
```
使い所としてはJPAとかでアノテーションにSQL書くとか、簡単なファイル出力やメール送信時のテンプレ文言などを定義するときとかに役立つかなと。

他の機能も色々とありましたが、とりあえず自分がこりゃいいやと思ったものだけとりあえず紹介してみましたよ。
【備忘録】Azure Monitorのログ検索アラート機能の仕様について

初投稿です。
業務でAzure Monitorのログ検索アラートを使用するにあたり、仕組みの理解に苦戦しましたので、備忘として残します。

# 実現したい事
・Azure Functionsがエラーログを出したらメール通知。
・メール本文にログ内容を表示。　※ここでハマりました...

## 処理の流れ
１．Functionsの関数A実行(Communication Serviceによるメール送信)
↓
２．ログ出力(Functions → Application Insights → Log Analyticsワークスペース）
↓
３．Azure Monitorによるアラートメール送信
アラートルール：
・10分間隔でクエリ発行
・クエリ内容：出力元がFunctions、かつ関数Aのエラーで絞り込み
・1件以上の場合に指定アドレスへメール送信
↓
４．メール確認(運用者)
・メール本文から、エラーログの詳細(申請ID等の値が含まれる)を確認したい。



# 課題
## メール本文にログ検索結果が記載されない。

>ログ検索アラート API バージョン 2021-08-01 以降では、アラート通知ペイロードから検索結果が削除されました。 検索結果は、以前の API バージョン (2018-04-16) で作成された警告ルールでのみ利用できます。

https://learn.microsoft.com/ja-jp/azure/azure-monitor/alerts/alerts-troubleshoot#the-search-results-are-not-included-in-the-log-search-alert-notifications

最新のAPIバージョンではメール内容にログが含まれない(以前はヒットしたログが10件まで記載されていた)。
一応、確認時点(2024/5/19)ではAPIバージョン2018-04-16を使ってデプロイも可能(コマンドライン)だが、廃止予定のため非推奨。

最新APIでもメール本文にAzure PortalへのURLが記載されていて、Azure Monitorの画面まで１クリックで移動してログを確認する事は可能。
ただメールのみで確認するよりも手間がかかる。エラー件数が多いとより不便か。

# 解決策
## Logic Appsなどの別アプリケーションでメール送信処理を実装する
https://learn.microsoft.com/ja-jp/azure/azure-monitor/alerts/alerts-logic-apps?tabs=send-email

https://www.michikusayan.com/entry/20220604/1654318819

Azure Monitorはアラートルールで定義した内容に合致したら、指定したアクショングループを実行する。
アクショングループの内容には大きく「通知」「アクション」がある。
https://learn.microsoft.com/ja-jp/azure/azure-monitor/alerts/action-groups

「通知」にはメール送信などが含まれる。
「アクション」にはLogic Appsの呼び出しなどが含まれる。

アラート発報時にアクションでLogic Appsを呼び出してペイロード(ログ検索結果などを含む)を渡し、Logic Apps側でメール内容を作り込む事が可能。
ただし、メール送信手段を別途用意する必要がある。
　・Office365アカウント
　・SMTPサーバ
　・Communication Service
 など。
 

>ログを調査する必要があるときは、アラートにある、ログ内の検索結果へのリンクを使用します。
生の検索結果、または他の高度なカスタマイズが必要な場合は、Azure Logic Apps を使用してください。

https://learn.microsoft.com/ja-jp/azure/azure-monitor/alerts/alerts-manage-alerts-previous-version#changes-to-the-log-search-alert-rule-creation-experience


https://learn.microsoft.com/ja-jp/azure/azure-monitor/alerts/alerts-manage-alerts-previous-version#changes-to-the-log-search-alert-rule-creation-experience




# 参考資料
Bicep関連

https://learn.microsoft.com/ja-jp/azure/azure-monitor/alerts/resource-manager-alerts-log?tabs=bicep#number-of-results-template-up-to-version-2018-04-16


https://learn.microsoft.com/ja-jp/azure/templates/microsoft.insights/scheduledqueryrules?pivots=deployment-language-bicep
ディスク容量確認コマンドについて

## はじめに
サーバーのログサイズが大きくなってディスクを逼迫するケースがあります。
障害対応時にはディスク容量の調査コマンドを実行する必要があります。

## 調査コマンド
### ディスク容量を確認して残りサイズを確認する
```
[root@localhost log]# df -h
ファイルシス            サイズ  使用  残り 使用% マウント位置
devtmpfs                  908M     0  908M    0% /dev
tmpfs                     919M     0  919M    0% /dev/shm
tmpfs                     919M  8.6M  911M    1% /run
tmpfs                     919M     0  919M    0% /sys/fs/cgroup
/dev/mapper/centos-root    17G  1.8G   16G   11% /
/dev/sda1                1014M  150M  865M   15% /boot
tmpfs                     184M     0  184M    0% /run/user/0
```
### ログサイズをランキング10で表示
サーバーのログサイズが大きくなった際は以下のコマンドを実行して
ログサイズが大きい順番にTop10が表示されます
```
[root@localhost log]# du -h /var/log | sort -rh | head -10
4.2M    /var/log
1.7M    /var/log/anaconda
1.4M    /var/log/audit
20K     /var/log/tuned
12K     /var/log/test_dir
0       /var/log/rhsm
0       /var/log/chrony
```
### ログを圧縮するコマンド
ログのサイズが大きい場合はログを圧縮します。
```
tar cvzf ファイル名.tar.gz ファイル名

例
[root@localhost log]# tar cvzf messages-20240121.tar.gz messages-20240121

ファイルが作成されます
-rw-r--r--  1 root   root    43760  5月 17 21:27 messages-20240121.tar.gz

元のログファイルが不要な場合は削除します
rm -i  messages-20240121
```

### フォルダを圧縮するコマンド
過去のログをフォルダに移動させて圧縮する
```
mkdir 202401
```
2024年1月のログを表示させる
```
[root@localhost log]# ls -ltr *-202401??
-rw-------. 1 root root      0  1月 18 21:34 spooler-20240121
-rw-------. 1 root root   1199  1月 21 18:36 maillog-20240121
-rw-------. 1 root root 218030  1月 21 19:30 messages-20240121
-rw-------. 1 root root   7443  1月 21 19:30 secure-20240121
-rw-------. 1 root root   4049  1月 21 19:32 cron-20240121
-rw-------. 1 root root  24984  1月 21 19:32 boot.log-20240121
```
2024年1月のログを202401フォルダへ移動させる
```
mv *-202401?? 202401
```
202401フォルダへ移動したことを確認する
```
[root@localhost log]# ls -tlr 202401
合計 260
-rw-------. 1 root root      0  1月 18 21:34 spooler-20240121
-rw-------. 1 root root   1199  1月 21 18:36 maillog-20240121
-rw-------. 1 root root 218030  1月 21 19:30 messages-20240121
-rw-------. 1 root root   7443  1月 21 19:30 secure-20240121
-rw-------. 1 root root   4049  1月 21 19:32 cron-20240121
-rw-------. 1 root root  24984  1月 21 19:32 boot.log-20240121
```
2024年のフォルダを圧縮する
```
tar cvzf ファイル名.tar.gz フォルダ名

[root@localhost log]# tar cvzf 202401.tar.gz 202401
202401/
202401/boot.log-20240121
202401/cron-20240121
202401/maillog-20240121
202401/messages-20240121
202401/secure-20240121
202401/spooler-20240121
```
圧縮後、tar.gzファイルの中身を確認する
tarコマンド tオプション　アーカイブの内容の一覧を表示する

以下のサイトが参考になりました。
https://atmarkit.itmedia.co.jp/ait/articles/1608/18/news021.html
```
[root@localhost log]# tar tvf 202401.tar.gz
drwxr-xr-x root/root         0 2024-05-17 21:34 202401/
-rw------- root/root     24984 2024-01-21 19:32 202401/boot.log-20240121
-rw------- root/root      4049 2024-01-21 19:32 202401/cron-20240121
-rw------- root/root      1199 2024-01-21 18:36 202401/maillog-20240121
-rw------- root/root    218030 2024-01-21 19:30 202401/messages-20240121
-rw------- root/root      7443 2024-01-21 19:30 202401/secure-20240121
-rw------- root/root         0 2024-01-18 21:34 202401/spooler-20240121
【Ruby】eachメソッドとmapメソッドの基本

## はじめに 
先輩から、`eachメソッドとmapメソッドは、開発現場で頻繁に使用されている` と聞き、今回それぞれの基本を初学者の自分なりにまとめてみました。プログラミング初心者の方、Rubyを学習し始めた方達のお役に立てたら幸いです。

## 結論
eachメソッドとmapメソッドの違いは、その戻り値にあります。

## eachメソッド 
eachメソッドは、一言でいうと「配列の要素を最初から最後まで順番に取り出すメソッド」です。取り出した要素をどう取り扱うかは、そのあとのブロックに記述されています。ブロックは、以下のコードだと`doからend`までです。

```ruby:each.rb 
nums = [1, 2, 3, 4, 5]
result = nums.each do |n| 
  puts n 
end

# 出力
# 1
# 2
# 3
# 4
# 5

# resultの値
#=> [1, 2, 3, 4, 5]
``` 

配列の各要素がnとして取り出され、`puts n`によって出力されています。

## mapメソッド 
mapメソッドも、ブロックを使用します。 eachメソッドとの違いは、mapメソッドは繰り返し処理の後、配列にして返してくれることです。 例えば、以下のように、配列の各要素を2倍にし、新しい配列を作るコードがあったとします。
```ruby:each.rb 
nums = [1, 2, 3, 4, 5]
new_nums = []
nums.each { |n| new_nums << n * 2 }
new_nums

# => [2, 4, 6, 8, 10]
``` 
上記のeachメソッドの場合だと、空の配列を用意して、他の配列をループ処理した結果を埋め込んでいます。このようなコードは、以下のmapメソッドに置き換えが可能です。

```ruby:map.rb
nums = [1, 2, 3, 4, 5]
new_nums = nums.map { |n| n * 2 }
new_nums

# => [2, 4, 6, 8, 10]
```
空の配列を用意する必要がなくなりました。
しかし、空の配列を用意することで、「これがメソッドの戻り値ですよ！」という意図がより明確になる場合もあるため、使い所を見極める必要がありそうです。

## おわりに 
今回は、Rubyのeachメソッドとmapメソッドの基本的な使い方を初学者の自分なりにまとめてみました。
Rubyを学習中の方達に、少しでもお役に立てたら幸いです！

コメントもお待ちしております:open_hands:(教えてください！笑)

## 参考 
[プロを目指す人のためのRuby入門[改訂2版]](https://www.amazon.co.jp/E3-83-97-E3-83-AD-E3-82-92-E7-9B-AE-E6-8C-87-E3-81-99-E4-BA-BA-E3-81-AE-E3-81-9F-E3-82-81-E3-81-AE/dp/4297124378/ref=dp_ob_title_bk)

https://qiita.com/jnchito/items/91bda171adceca23abc3
TypeScriptのインターセクション型を知る

## はじめに
TypeScriptの学習を進める中で「インターセクション型」というものに出会ったので、その概要が使い道について整理します。

## インターセクション型とは
### 概要
異なるオブジェクトの型同士を結合させて、新しい型を作ることができるものです。
要は型の拡張ができるということです。

### 定義方法
結合したい型同士`&`でつなぎます。
```typescript
type tBaseA = {
  name: string;
  age: number;
};

type tBaseB = {
  country: string;
  isOwner: boolean;
};

// 2つの型を結合
type tCombined = tBaseA & tBaseB;

const combinedObj: tCombined = {
  name: "John",
  age: 24,
  country: "America",
  isOwner: false,
};
```
上記の例ではインターセクション型として新しく定義した型を使ってオブジェクトを定義しています。
このオブジェクトは、2つの型それぞれのプロパティを合わせもったものになっています。

### 実践的な使い方
型の結合、拡張ができると便利な場面をひとつ紹介します。

```typescript
// API用設定
interface ApiConfig {
  baseUrl: string;
  apiKey: string;
}

// ログ用設定
interface LoggingConfig {
  logLevel: 'debug' | 'info' | 'warn' | 'error';
  logFile?: string;
}

// アプリの設定
type AppConfig = ApiConfig & LoggingConfig;

const config: AppConfig = {
  baseUrl: 'https://api.example.com',
  apiKey: 'your_api_key',
  logLevel: 'debug',
  logFile: 'app.log',
};
```
それぞれの機能ごとに設定情報を持たせておきつつ、アプリケーション用の設定としては一つにまとめておきたい場合、このようにインターセクション型を使って表現することができます。

機能としては分けつつ、設定情報は必要なものが確実に設定されるよう、型情報を定義しています。

なお、定義方法においては`type`を使って型定義したものを結合し、今回の例では`interface`を使って型定義したものを結合していますが、どちらでもやっていることは同じです。

### いろいろなインターセクション型
インターセクション型はオブジェクト型以外にも使うことができます。

以下は関数型に対してインターセクション型を適用した例です。
```typescript
type FuncA = (x: number) => string;
type FuncB = (y: string) => boolean;

type FuncAB = FuncA & FuncB;

const func: FuncAB = (arg: number | string) => {
  if (typeof arg === 'number') {
    return arg.toString();
  } else {
    return arg.length > 0;
  }
};
```
TypeScript(JavaScript)では、関数のオーバーロード（関数名が同じで、引数の型や個数が異なる）ができません。
インターセクション型を使うことで、擬似的なオーバーロードを実現できます。

ここでは、2つの関数を結合した新しい関数を定義しています。
そして、受け取った引数の型を判斷して、それぞれ異なる処理を実行しています。

引数が数値である場合には文字列に変換した値を返し、文字列である場合には入力済か（文字数が0より多いか）を返しています。

他にもクラス型やユニオン型に対してもインターセクション型を使うことができます。

## まとめ
インターセクション型についてその概要と機能を確認しました。
調べる中で、オーバーロードまでできるのは驚きでした。

もっと実践的な機能を学んでいきたいです。