OmniAICreator/Qiita-1.07M huggingface dataset:
TypeScript: const type parameterを用いるとas const(constアサーション)が省略できる

この記事では、TypeScriptにおける`const type parameter`という概念と、それがどのように`as const`（constアサーション）の使用を省略できるかについて説明します。これは初心者にとって少し複雑に感じるかもしれませんが、実際には非常に便利で強力な機能です。では、早速見ていきましょう。

### 基本的な関数の例

まずは、基本的な関数を2つ見てみましょう。

```typescript
function withoutConst<T>(value: T): T {
  return value;
}

function withConst<const T>(value: T): T {
  return value;
}
```

ここで、`withoutConst`と`withConst`の2つの関数があります。これらはどちらも引数をそのまま返す非常にシンプルな関数ですが、重要な違いが一つあります。それは、`withConst`関数に`const T`という型パラメータが使われている点です。

### withoutConstの動作

`withoutConst`関数を使うと、型はより一般的な形で推論されます:

```typescript
const value1 = withoutConst(123);
//    ^? const value1: 123
const value2 = withoutConst({ num: 123 });
//    ^? const value2: { num: number; }
const value3 = withoutConst([1, 2, 3]);
//    ^? const value3: number[]
```

ここでは、数値、オブジェクト、配列の3つの異なる型の値を`withoutConst`に渡しています。この場合、TypeScriptはそれぞれの型を推論しますが、オブジェクトや配列はその構成要素が変更可能であると見なされます（つまり、`readonly`ではありません）。

### constアサーションの使用

もし、変更不可能な（immutableな）型を確保したい場合、`constアサーション`を使うことができます。これは`as const`と記述します。例えば：

```typescript
const value1 = withoutConst(123 as const);
//    ^? const value1: 123
const value2 = withoutConst({ num: 123 } as const);
//    ^? const value2: { readonly num: 123; }
const value3 = withoutConst([1, 2, 3] as const);
//    ^? const value3: readonly [1, 2, 3]
```

ここで、`as const`を使うことで、TypeScriptに対して値が変更不可能であることを明示しています。これにより、オブジェクトや配列は`readonly`として扱われます。また、`num`の型は`123`のようなリテラル型にもなります。更に、配列は単なる配列型(`T[]`)ではなくタプル型として扱われます。

https://typescriptbook.jp/reference/values-types-variables/const-assertion

https://typescriptbook.jp/reference/values-types-variables/literal-types

https://typescriptbook.jp/reference/values-types-variables/tuple

### withConstの威力

`const type parameter`を使う`withConst`関数を使うと、`as const`を省略できます。つまり、同じ振る舞いをもっと簡単に実現できるのです。

```typescript
const value1 = withConst(123);
//    ^? const value1: 123
const value2 = withConst({ num: 123 });
//    ^? const value2: { readonly num: 123; }
const value3 = withConst([1, 2, 3]);
//    ^? const value3: readonly [1, 2, 3]
```

この場合、`withConst`は自動的にその引数が変更不可能であると推論し、そのため`as const`を付ける必要がありません。これにより、コードがよりシンプルで読みやすくなります。

### まとめ

`const type parameter`を使用することで、TypeScriptの型システムの強力さをさらに引き出すことができます。特に、`as const`を多用している場合には、この機能を使ってコードをよりシンプルに保つことができます。

ちなみに、本稿で扱ったサンプルコードはTypeScript Playgroundにあります:

https://www.typescriptlang.org/play?ssl=34&ssc=2&pln=28&pc=1#code/GYVwdgxgLglg9mABAdxlAFnEUDCCDOUAPACoB8AFAG4CGANiAKYBciJAlKyYgN4BQiRACdGUEEKS0GjANx8Avnz6hIsBCjTo8YQkQgEobSlKZdObXgOGjxk+kzmK+YGgFtG+AA40IjDRixcA0tBfR1DE0YARkQAXn9MbG1CCiiAJgBmdjlBAHpcwUEAPQB+RDDCREio1nSMqwqI+0Y0uITA5KgKHkQwEFdazMR5bKt8wsRS8uDItNYevoHe-oAjRiEZYYaZ5oy21ACkgwoAbSiAGkQ0y4yAXVG8gsKpxqrd1kW1oRPbhSUXdxeHx+A6JILhQAWDI1AEUMgFaGQA-DIB2hkA5wyAZ4YQtNwm9pDF4qCOsc6ogaPhMYQHohxs8yq9qoN6qEdtJWnjNATwt1lksifJiaTGhSqcUaUymHNeNYaAATBB0ACeXPpm0UjKxkT2rMO4JSZ0u10Qdz5ZKggqewuN2KYGVYImlsoVuquN1+TgBHm8vgSnQxtOauK9hMypomL1F0Xp2zVzRZAY5C369OGwepFtm80lMrA8sViDqysjlXV+00nVOFydBvuOUpZsmIqj0mtGftiEd+rufyAA
大学でスマートフォンゲームの運用について説明しました

こんにちは
この記事はKLab Engineer Advent Calendar2023の記事です

## 簡単な自己紹介

* 2013年新卒入社のサーバーサイドエンジニア
* 現在東京都八王子市に在住

## 大学で勉強会を開催してきました

ITエンジニアが外部で発表する場合、基本的には何かしらのカンファレンスで発表したり、勉強会を企業が開催することが多いと思うのですが、今回は大学で話してきました。
勉強会開催した経緯としては以下です。
* 私が住んでいる街は東京都立大(TMU)に近いため、SNS上でそこに通っている学生さんと知り合うことが出来、一緒にランチ行くなどして交流を図ることができた
  * 都立大はシステムデザイン学部情報科学科という情報系の学科が存在するため、IT知識のあり、システム運用の関する内容に興味のある学生さんもいた 
* 学祭に行くことで交流を図ることもできた
  * 都立大の学祭開催中に展示に行って学生さんと話してその後ランチ行って業界の雑談したりしました

その中で、都立大のサークル [GDSC TMU](https://gdsc.community.dev/tokyo-metropolitan-university/) でお話しませんか　という話になったため、実際にお話する時間をいただくことになりました

## 登壇までの流れ
主催者の学生さん(SNS上で知り合ってランチした方でもある)と話し合って決めていきました

* 時間決め
  * 平日の授業後の時間に設定(12/7 17:30から開催)
  * 外部発表も業務扱いに出来たため、その面でも平日にしました 
* 会場
  * 都立大の図書館内にあるプレゼンテーションルームを主催者の方に予約していただきました
* 設備
  * プロジェクターでスライドを映すことができる会場を確保したため、いつもの外部発表と同じように発表できました
  * マイクはないので大声を張る必要はありましたが
* その他
  * 事前に参加希望した人にmeetのリンクを送ることで、オンラインでも配信しました
  * 都立大の情報系の学科であるシステムデザイン学部情報科学科は日野キャンパスも利用するため、会場の南大沢キャンパスまで行けない人もいるためです。(私は住んでいる場所が南大沢キャンパスに近いため、南大沢キャンパスでの開催にしたかったため。大学のキャンパスとしても南大沢キャンパスのほうが広いですからね)

## 実際に発表したスライド
スライド数を少し減らしていますが、実際のものをslideshareにアップロードしました

https://www.slideshare.net/tekunmathematics/ss-4a8e

## 勉強会の様子
![PXL_20231207_090828107.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2269508/cbf08ca9-b4a6-2785-3484-570ccb4b7729.jpeg)

## 発表しての感想
発表は１時間の予定でした。確かに用意した発表分は１時間で終わりましたが、事前に募集した質問が多く更に追加した５０分程度かかったため、全体で１時間５０分という長丁場になりました。(ずっと話していたため、ペットボトルのジュースの2/3がなくなりました)
学生さんの反応が良かったです。真剣に聞いてくれていたので、実際の運用に興味を持っていただけたかな、と感じました

## 学生さんの感想
実際にアンケートを取りましたので、その結果を紹介いたします
* とても面白かった
* 本当に貴重な機会で、すごく勉強とモチベにつながった
* すごく勉強になりイメージが湧いた部分も多かった
* アプリバイナリを変更しないアップデートやアセット管理などゲームならではの部分が面白かったです! 本当にありがとうございました
* 仕事のイメージがすごく湧いたし、これから頑張る部分の指針がすごく見えた
* すごく勉強になりました、ありがとうございました。
* 貴重なお話を本当にありがとうございました。ユーザを飽きさせないために必要な技術がすごく印象的でした。

## 今後について
* 今回対象とした学生さんが、webアプリ開発経験のある方がいるなど、比較的経験のある方もあるがゆえに、簡単と感じる方もいたので、より高度な内容を再度語ろうかなと考えています。
* 将来的には、大学のサークルではなく、特定の大学や専門学校の講義の場で話せたら面白いと考えています。そんな学校があるかどうかはわかりませんし、私の本業の都合もあるので、実現できるかはわかりませんが。。
【ServiceNow】リマインダー機能を作成してみた

# 実現したいこと
ServiceNowで未承認のレコードがあった時にリマインダーを送りたい。

# 機能概要
- sysapproval_approverテーブルで未承認のレコードをユーザ毎に取得
- 未承認のレコードがあったらイベントを発火して通知を送信

# 用意するもの
- Scheduled Jobs
- Event Registry
- Email Templates
- Notification

# 作り方
## 1.Scheduled Jobsのスクリプト
```ja:Scheduled Jobs
var grUsr = new GlideRecord('sys_user')
grUsr.addQuery('active', true); 
grUsr.query();

while (grUsr.next()){

	var count = 0;
	var user = '';

	var gr = new GlideRecord('sysapproval_approver');
	gr.addQuery('approver', grUsr.sys_id);
	gr.addQuery('state', 'requested');
	gr.query();

	while (gr.next()){
		// Email Templateに引き渡す値を変数に格納
		count = count + 1;
		user = gr.approver.getDisplayValue();
	}

	if (count > 0 ){
		// 未承認のものが１件以上あったらメール送信するイベントを発火 
		gs.eventQueue('remainder', gr, count, user);
	}

}
```
## 2.Event Registrationの設定
![Event Registration.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625321/90b02376-7729-74b7-5a04-a53ae8589359.png)

## 3.Email Templateの設定
![Email Template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625321/8c5f251e-027c-45cf-daba-d18777a31f0f.png)
\\${event.parm1},${event.parm2}はイベント発火の際に引き渡された変数になります。

## 4.Notificationの設定
| 項目 | 設定値 | メモ |
|:-----------|:------------|:------------|
|Name|u_remainder_mail| 任意の値|
|Table|sysapproval_approver| 承認テーブルを指定|
|Category|Approval||
|Active|True||
|When to send - Send when|Event is fired||
|When to send - Event name| remainder|上記で作成したイベント名を選択|
|When to send - Who will receive|User/Groups in fields|Approver||
|What it will contain - Email template|remainder|上記で作成したテンプレートを選択|

# 動作確認
個人の開発インスタンスのEmailにメールが作成されていることを確認。
![Email.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625321/de7aee59-a5bc-71e1-a0d8-3a2b274a3a47.png)

簡単なソースですが、リマインダー機能を実装できました。
daisyUIでUI作ってみませんか？

## 推しのCSSライブラリを紹介させてください！

皆さん、普段CSSライブラリ何を使われてますか？
TailwindCSSのライブラリだけでも魅力的なライブラリがたくさんありますよね！
その中でも[daisyUI](https://daisyui.com/)を推させてください！

## daisyUIはtailwindCSS互換のライブラリ

daisyUIはTailwindCSSのためのライブラリなので自分のプロジェクトにTailwindCSSが導入されていない場合は先にTailwindCSSの導入が必要です

すでに導入済みのプロジェクトではもちろんTailwindCSSのclass名はそのまま使えるため移行に必要なコストは低コストです

npmを使用している場合は以下のコマンドで簡単に導入することができます

```
npm install daisyui
```

公式サイトでコードとプレビューが見れます...!


https://daisyui.com/components/



## 魅力１. 痛いところに手が届くコンポーネントがある

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1471588/defa10f7-2a60-892d-d425-7f0979f7db4c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1471588/918286fa-51db-330d-8773-965ae67383c7.png)

このようなほかのライブラリでは見ないようなライブラリもあります！
自分が特に助かったのはこのタイムラインです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1471588/cca7d260-ac6e-99d8-e25a-7dfe898c2bab.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1471588/84a6b874-8658-8087-72e5-d043b979c480.png)

他にもフォームを作るときによく使うけど自分で実装するには少し手間がかかるコンポーネントも充実しています！


## 魅力２．導入のドキュメントが豊富

NuxtやReactではなく記事執筆時点では以下のフレームワークなどへの導入ドキュメントが用意されています！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1471588/8353ba09-5d87-7e7c-0fb5-5845271e00fa.png)

## 魅力３．テーマが切り替えられる

最近のwebサイトではライトテーマとダークテーマがUI上で切り替えることができますが、このdaisyUIでは複数のテーマの切り替えができるjsが含まれているので
```
<html data-theme="dark">
  <div data-theme="light">
    This div will always use light theme
    <span data-theme="retro">This span will always use retro theme!</span>
  </div>
</html>
```
こんなに手軽にテーマの切り替えUIを作ることができます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1471588/b37c4a32-b5e1-751e-725e-5ab84ad7de0d.png)

デフォルトでは30種類ほどのテーマが用意されているほか、もちろん自分で定義することも可能です！

ちなみに更新も頻繁に行われているのでGithubも要チェックです！

https://github.com/saadeghi/daisyui/releases


## 終わりに

daisyUIの魅力的な点はほかにもたくさんありますが、その中でも特に自分が推している魅力を３つ記事にさせていただきました
これを機にdaisyUIデビューしてみませんか？？

良きデベロッパーライフを！
近似最近傍探索とメタデータフィルタリング、どうやって組み合わせるんですか？ その 1

## 概要
近似最近傍探索とメタデータフィルタリングについて調べたことをサクッとまとめます。
今回は 2 種類の愚直なアプローチとその問題点について書きます (というかここで力尽きた)。

## モチベーション
ベクトル検索が流行っている今日この頃、近似最近傍探索もよく使われてますよね。もちろん私のお勤め先でも使われております。
最近は近似最近傍探索とメタデータフィルタリングを組み合わせたデータベース※なんかもあるみたいです。すげぇ〜。
そこで気になったんですが、近似最近傍探索 とメタデータフィルタリングってどうやって組み合わせてるんでしょうか？
「近似最近傍探索 してから後処理でフィルタリング」でとりあえず要件を満たせそうですが、なんか効率悪いだろうなあ、なんかいい方法があるんだろうなぁ、と思っている感じです。 
※ [Pinecone](https://www.pinecone.io/), [qdrant](https://qdrant.tech/), [Vertex AI Vector Search](https://cloud.google.com/vertex-ai/docs/vector-search/overview) etc.

## 前提
### 近似最近傍探索

ある商品に意味的に類似する商品を**大量の候補の中から高速に**見つける、みたいなことができます。
> (僕) この商品に似た商品はどれ？
(近似最近傍探索 ｸﾝ) これだよ！ 

他にも Two tower model なんかと組み合わせてユーザーと相性の良い商品を高速に見つける、みたいなこともできます。便利だなぁ。


#### 近似最近傍探索 × メタデータフィルタリング
ある商品に意味的に類似する商品を、**大量の候補の中からカテゴリを絞り込みつつ高速に**見つける、みたいなことができます。
> (僕) この商品に似た**シャツ**はどれ？
(近似最近傍探索 ｸﾝ) これだよ！ 

便利そうです。
それに実サービスでは割とありがちな要求なんじゃないでしょうか？

## 愚直なアプローチその課題

#### 前処理でフィルタリング
近似最近傍探索の前にフィルタリングすることで要件を満たせそうですが、実は問題があります。
近似最近傍探索の前にフィルタリングする場合、フィルタリング結果ごとに近似最近傍探索のインデックスを用意してあげる必要があるのです。めんどくさそうですね。
例えば 10 種類の商品カテゴリと 10 種類の色の組み合わせで商品をフィルタリングする場合どうなるでしょうか？インデックスが 100 個も必要です。やっぱりめんどくさそうですね。
商品の価格帯をフィルタリングの条件に追加したい、ですって？もう無理ですね。


なおフィルタリングの種類が数個だけで増えることもないのであれば、この方法もアリかもしれません。
またフィルタリング後のアイテム数がかなり少なくなることが保証されているような場合には、ブルートフォースで近傍探索しちゃってもいいのかもしれませんね。であればインデックスたくさん運用しなきゃ行けない問題は発生しなさそうです。

#### 後処理でフィルタリング
近似最近某探索の後にフィルタリングすることで要件を満たせそうですが、こちらも問題があります。
例えばある T シャツに似た雰囲気のシャツを 10 個取得したいとします。
フィルタリングでいくつか除外されることを見越して 20 個の類似商品を ANNS で取得し、その後シャツのみに絞り込むとしましょう。
残念ながら ANNS で取得した 20 個のうちシャツは 5 個だけでした。10 個欲しかったのに残念です。最初に取ってくる商品数が 20 個じゃ少なかったみたいです。
今度は ANNS で取得する商品を 100 個にしてみました。するとフィルタリング後に 10 個以上の商品が残りました。やったね。
でもこれもダメですよね。だって無駄な計算がたくさん必要になりますから。

## さいごに
前処理でのフィルタリング、後処理でのフィルタリングには問題があることがわかりました。
次回以降は上記の問題を解決する方法について調べてまとめてみようと思います。
Pinecone さんや qdrant さんのブログが参考になりそうかなあ、と思っています。

## 参考文献
(順不同)
- pinecone さんのブログ: https://www.pinecone.io/learn/vector-search-filtering/
- qdrant さんのブログ: https://qdrant.tech/benchmarks/
[Haskell] do 記法の使い方と型

`do` 記法を用いると、演算子 `*>` および `>>=` と同じ計算を分かりやすく書いたり、`do` 記法の `let` を用いて変数束縛することが可能です。

参考「[[Haskell] モナド 演算子 まとめ - Qiita](https://qiita.com/kerupani129/items/333155e5e2dee644d6dc)」

参考「[3.14 Do Expressions - 3 Expressions](https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-470003.14)」

# 1. 使い方

## 1.1. 基本

```haskell
y = do
    x
```

```haskell:意味
y = x
```

## 1.2. 値を破棄して他の値に置き換える

```haskell
z = do
    x
    y
```

```haskell:意味
z = x *> y
```

## 1.3. 値に関数を適用する

```haskell
y = do
    x' <- x
    f x'
```

```haskell:意味
y = x >>= f
```

```haskell
y = do
    C x' <- x
    f x'
```

```haskell:意味
y = x >>= f'
  where
    f' (C x') = f x'
    f' _      = fail "Pattern match failure"
```

参考「[[Haskell] モナドの計算を中断する - Qiita](https://qiita.com/kerupani129/items/f6819466b49fe799b40c)」

参考「[fail - Control.Monad](https://hackage.haskell.org/package/base-4.19.0.0/docs/Control-Monad.html#v:fail)」

## 1.4. 変数束縛する

```haskell
z = do
    let y = f x
    g y
```

```haskell:意味
z = g y
  where
    y = f x
```

# 2. 型

`do` 記法はモナドであればどんな型でも扱うことができますが、1 つの `do` 記法の中では (`let` による変数束縛を除いて) 同じモナドしか扱うことができません。

```haskell
main :: IO ()
main = do
    putStrLn "Foo" :: IO ()
    string :: String <- getLine :: IO String
    putStrLn string :: IO ()
```

```haskell
f :: Maybe Int -> Maybe Int
f x = do
    x' :: Int <- x :: Maybe Int
    let y = 23 * x' :: Int
    Just y :: Maybe Int
```

# 3. おまけ: `String` 型の値は連結されない

`type String = [Char]` よりリストモナドのため、`String` 型に対して `do` 記法を使うことができますが、演算子 `*>` は連結ではないため `do` 記法を用いても連結されません。

```haskell
main :: IO ()
main = do

    putStrLn $ do
        "Foo"
        "Bar"
        "Baz"
```

※上記のコードを GHC で実行すると `_ <- "Foo"` および `_ <- "Bar"` と書くように警告が出ます。

```text:実行結果
BazBazBazBazBazBazBazBazBaz
```

もし `String` 型の値を連結したければ `do` 記法は用いず従来の方法で連結します。

```haskell
main :: IO ()
main = do

    putStrLn
        "Foo\
        \Bar\
        \Baz"

    putStrLn
        $  "Foo"
        ++ "Bar"
        ++ "Baz"

    putStrLn $ concat
        [ "Foo"
        , "Bar"
        , "Baz"
        ]
```

```text:実行結果
FooBarBaz
FooBarBaz
FooBarBaz
```

```haskell
import Data.List (intercalate)

main :: IO ()
main = do

    putStrLn $ unwords
        [ "Foo"
        , "Bar"
        , "Baz"
        ]

    putStrLn $ intercalate ", "
        [ "Foo"
        , "Bar"
        , "Baz"
        ]

    putStrLn $ intercalate "\n"
        [ "Foo"
        , "Bar"
        , "Baz"
        ]

    putStrLn $ unlines
        [ "Foo"
        , "Bar"
        , "Baz"
        ]
```

```text:実行結果
Foo Bar Baz
Foo, Bar, Baz
Foo
Bar
Baz
Foo
Bar
Baz

```

※ `unlines` は `intercalate "\n"` と異なり末尾でも改行します。
SwiftUIで追加読み込みをする方法

## はじめに
ListのViewにて、スクロールして最下部まで到達したら追加読み込みを行う方法です。

## 方法
```swift
struct ContentView: View {
  @State var numbers = Array(1...50)
  var body: some View {
    List {
      ForEach(Array(numbers.enumerated()), id: \.element) { index, element in
        Cell(index: index)
          .onAppear {
            if numbers.count - index == 1 {
              // 配列の中身を更新する処理
            }
          }
      }
      // 配列の中身を更新中はこの辺りでインジケーターを表示する
    }
  }
}

private struct Cell: View {
  let index: Int
  var body: some View {
    Text("No.\(index)")
  }
}
```
例の全体像はこんな感じです。

肝になってくるのは
```swift
ForEach(Array(numbers.enumerated()), id: \.element) { index, element in
...
}
```
この部分で配列のindexを`enumerated()`で取得して使用します。

Listでは`ForEach`でViewを繰り返し表示すると思いますが
`ForEach`内で表示しているViewの`onAppear`内で
```swift
if 配列の要素数 - 今表示されたViewのindex == 1 {
  // 何らかの処理
}
```
をすることでスクロールで最下部に到達した時に追加読み込みの処理を呼び出すことができます。

## 最後に
コード量から見てもかなり簡単にできることが分かります（View側に関しては）
他にも色々と方法はありそうですが、今回はこの方法を紹介しました。
最近インデントは半角スペース2つ派になりました。
以上です。
Firebaseでつながる！？複数プラットフォームでのアプリ開発の可能性

こんにちは！
今年も１２月に入り、いよいよ今年も終わりを迎えようとしていますね！
皆さんにとって今年はどんな１年でしたか？
私にとって今年の１年は社会人として1年目の年だったので全てが新鮮に感じる1年となりました！
少しずつ今年の振り返りなどをしてみるのも、いいかもしれませんね！

さて、今回はFirebaseについて紹介させていただきます！
最後までこの記事を読んでいただけたら嬉しいです！

## 1,Firebaseとは？
Firebaseは、開発者がクラウドでアプリケーションを構築、改善、運営するための包括的なプラットフォームです。その中でも特に注目すべきなのが、複数プラットフォームでのアプリケーション開発において、Firebaseが提供する一貫性のあるエクスペリエンスです。iOS、Android、Webアプリケーションにおいて、同じコードベースやツールを使用してアプリを開発できるというのは、開発者にとっては夢のような話ですが、Firebaseはそれを現実のものにしています。

https://firebase.google.com/?hl=ja

## 2,Firebaseでの複数プラットフォーム対応機能
Firebaseが提供する複数プラットフォームでのアプリ開発において、様々な機能が利用できます。これらの機能を理解することで、異なるプラットフォームで一貫性のある開発体験を得ることができます。
Firebaseの主要な複数プラットフォーム対応機能を紹介します。


### ・ Firebase Realtime Database（リアルタイムなデータ同期）
Firebase Realtime Databaseは、iOS、Android、Webなど様々なプラットフォームでのデータベース同期を可能にします。JSON形式でデータを保存し、1度の変更が即座に全ての接続されたクライアントに反映されます。これにより、複数プラットフォームでのリアルタイムなデータ同期が実現されます。

### ・ Firebase Authentication（一元管理の認証）
Firebase Authenticationは、様々な認証プロバイダ（Email/Password、Google、Facebookなど）をサポートし、管理された認証システムを提供します。これにより、ユーザーは複数プラットフォームで同じアカウントでログインでき、シームレスなユーザーエクスペリエンスを実現できます。

### ・Firebase Cloud Functions（クラウドでのバックエンド処理）
Firebase Cloud Functionsは、JavaScriptやTypeScriptを使用してクラウドで実行されるバックエンドの処理を定義できます。これにより、複数のプラットフォームで共通のバックエンドロジックを実現し、異なるアプリケーション間で一貫性のある動作を実現できます。

### ・Firebase Cloud Messaging（プッシュ通知の一元管理）
Firebase Cloud Messagingを使用すると、異なるプラットフォームに対して一元管理されたプッシュ通知を送信できます。ユーザーは複数のデバイスで同じ通知を受け取り、連携したユーザーエクスペリエンスを提供できます。

### ・Firebase Analytics（ユーザーエンゲージメントの一元管理）
Firebase Analyticsは、複数プラットフォームでのユーザーエンゲージメントを一元管理し、アプリの利用状況やユーザーの行動に関する洞察を提供します。これにより、異なるプラットフォームでのアプリの改善点を把握できます。

### ・Firebase Performance Monitoring（パフォーマンスの最適化）
Firebase Performance Monitoringは、複数プラットフォームでのアプリのパフォーマンスをモニタリングし、ボトルネックを特定するのに役立ちます。これにより、異なるプラットフォームでのアプリケーションの最適化が可能です。

![Qiitaimag.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/b99a4562-9c0f-7f37-8db5-a16c53893eb5.gif)


## 3,Firebaseの使いどころは!?
### ・スタートアップやプロトタイプ:
Firebaseは素早い開発と簡易な設定が可能なため、スタートアップやプロトタイプの構築に適しています。

### ・リアルタイムなアプリケーション:
チャットアプリ、リアルタイムな協力アプリ、オンラインゲームなど、データ同期が即座に反映されるアプリケーションに適しています。

### ・モバイルアプリケーション:
FirebaseはiOSやAndroidのネイティブアプリケーション開発にも対応しており、モバイルアプリのバックエンドとして利用されます。

### ・ウェブアプリケーション:
Firebase Hostingを使用して、静的なウェブサイトを簡単にホスティングできます。動的な要素やデータベース連携もサポートされています。

### ・サーバーレスアーキテクチャ:
イベント駆動型のタスクやバックグラウンド処理を必要とする場合、Firebase Cloud Functionsを使用してサーバーレスアーキテクチャを構築できます。

### ・ユーザーエンゲージメントの向上:
Firebase Cloud Messagingを使用してユーザーに対してプッシュ通知を送信し、アクティブユーザーやリテンションを向上させることができます。

## 4,終わりに
今回はFirebaseについて紹介させていただきました！
自分はまだFirebaseを実際に使用したことがないので、今回記事を書くに当たって早く使ってみたいと思いました！
今回も最後まで読んでいただき、ありがとうございました！
ではまた次の記事で〜！
return 0はreturn falseではない？

formのvalidationをするためのBest Practiceを探しています。
是非いいアイデアがあれば教えてください。

フォームの中に複数のinput要素等があって、それぞれのvalidationを行う必要がある場面というの多々あると思います。
しかも一つのページに複数のformがある場合などは、validationは関数化する方がいいと思割れますので、いつも私は下記のような手法を取っています。その際に、少しつまづいた点があるのでメモを残します。

フォーム内に位置する複数のinputのうち、一つでも空欄があれば送信しないという単純な仕組みです。


```html
<form>
     <input class="aaa">
     <input class="bbb">
 </form>

 <script>
     $('form').submit(function(){
          return validate();
    });

    function validate(){

        let flag;

        const val1 = $(".aaa").val() ? 1 : 0 ;
        const val2 = $(".bbb").val() ? 1 : 0 ;

        flag = val1 * val2

        return flag;

}
 </script>
```

このように、掛け算の中に1つでも0が含まれていると、答えは必ず０になるという性質を利用して、validationを行っています。

そして、0がfalseと評価されることから、そのままflagを返り値にすれば、入力エラーがあればsubmitされず止まると思ってたのですが、、、ダメでした。
retun falseとしないとダメみたいですね。

```html

validate() ? true : false;

```

これならうまく行きました。

みなさん入力値のvalidationはどうされているのでしょうか？
Owner権限でも消すことができないGCPプロジェクトを作成する方法

組織内のGoogle　Cloudプロジェクトのライフサイクルを適切に管理しないと、以下の記事のように「野良」プロジェクトが大量に発生してしまいます。

https://techblog.zozo.com/entry/the-case-book-of-gcp-admin-beginner

上記の記事では作成権限を制限しましたが、一方で削除権限も制限したい場合はどうするのがよいでしょうか。プロジェクト管理者にOwner権限を渡している場合は、その人の権限で自由にプロジェクトを削除できてしまうのですが、その権限を剥奪したいです。

# 解決策: DENYポリシーの活用
DENYポリシーを作成すると、このようなOwner権限でもプロジェクト削除ができないような設定を入れられます。

https://cloud.google.com/iam/docs/deny-overview

DENYポリシーはプロジェクト・フォルダ・組織のいずれかにアタッチできますが、プロジェクトにアタッチしてしまうとOwner権限でDENYポリシーを外せてしまうので、フォルダか組織にアタッチする必要があります。ここでは組織にアタッチする場合を例示します。

```terraform
data "google_organization" "my-org" {
  domain = "Organizationのドメイン"
}

resource "google_iam_deny_policy" "limited-users-can-delete-project" {
  provider = google-beta

  # フォルダにアタッチする場合は以下を参照に書き換え
  # https://cloud.google.com/iam/docs/deny-access#attachment-point
  parent = urlencode("cloudresourcemanager.googleapis.com/organizations/${data.google_organization.my-org.org_id}")
  name   = "limited-users-can-delete-project"

  rules {
    deny_rule {
      denied_principals    = ["principalSet://goog/public:all"]
      exception_principals = ["削除が許可された人"]
      denied_permissions   = ["cloudresourcemanager.googleapis.com/projects.delete"]
    }
  }
}
```
Azure ML studio endpointにおける「ライブ トラフィックの割り当て機能」は必要なのか？というお話

Azure Machine Learningを使って作った学習モデルと推論用プログラムをOnline EndpointとしてREST API化し、Power Apps・Power Automateから呼び出せるようにする仕事をしていた。
そんな時に表題に関わる罠に嵌まったので経緯と対策を紹介しておく。

# ひとつのエンドポイントに複数のデプロイしたモデルを共存させたかった
Azure ML Studioの場合、まず最初にエンドポイントを建ててその中にモデルと推論用プログラムをデプロイする二段構えの処理が必要である。
更にひとつのエンドポイントの中に複数のデプロイ先を用意して共存させることも仕様上可能である。
現業務でも顧客の要望に応じて、いつでも旧モデルに戻せるようこの仕様を活かした運用をしている。

ここで、Azure ML Studioのエンドポイント詳細画面を抜粋した画像を添付する。
![エンドポイントの画面.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204315/4559ca6b-797f-1b9a-f5fe-3aefba3fe218.jpeg)
上図の右側、展開の概要のところにライブトラフィックの割り当て、左上にはトラフィックの更新とあり、一見するとこの値を弄ってやれば、エンドポイントを呼び出す側にデプロイ先を意識させる必要が無く、Azure ML内だけでどのモデルをどういった割合で選択できるかのように見える。

が、実際はそのような方法でトラフィックの割り当てを弄っても上手く流れてくれないことが分かった。

# ホントはデプロイ先も指定しないといけないのかよ

エンドポイントへのデプロイが成功すると、使用タブに「こうやって使うといいよ」というサンプルコードが生成される。
本件に関連する部分を抜粋し以下に記載する。
```example.py
#=========# 省略 #=========#
url = 'https://{エンドポイントの名前}.japaneast.inference.ml.azure.com/score'
# Replace this with the primary/secondary key or AMLToken for the endpoint
api_key = '{認証用のキーが入る}'
if not api_key:
    raise Exception("A key should be provided to invoke the endpoint")

# The azureml-model-deployment header will force the request to go to a specific deployment.
# Remove this header to have the request observe the endpoint traffic rules
headers = {'Content-Type':'application/json', 'Authorization':('Bearer '+ api_key), 'azureml-model-deployment': '{デプロイ先の名前}' }

req = urllib.request.Request(url, body, headers)
#=========# 省略 #=========#
```
header変数を定義する箇所に注目してほしい。
`'azureml-model-deployment': '{デプロイ先の名前}'`
とあるので、APIを呼び出すときに実際にはデプロイ先も指定する必要がある。

この仕様を失念しており、APIの利用先へ「使用するモデルを変更する際はエンジニア側でトラフィック先を変更するだけでOKです」と伝えてしまったことで混乱を来す結果となった。

# 対策
「30％はモデルAで残りの70％はモデルBにデータを流す」としたい場合は当てはまらないものの、今回のようにどちらかだけ使いたいとなった場合はエンドポイント呼び出し側でもデプロイ先の指定をする必要がありそうだ。

もっと賢くAPIを呼び出す方法はあるはずで、その方法を使えばライブトラフィックも使えそうだが調査不足のためここで一旦お開きとする。
Xcodeでプロジェクト内の全ての文字列を置換したい時の方法

# 概要
プロジェクト内のクラス名などを一斉に変更する際、今までは`Refactor>Rename`を使っていたのですが、偶にエラーが出て置換できない例が出てきたり、何故か置換対象として表示されないことがあります。

一般的な対処法である`Derived Data`の削除などをしても上手くいかず、違う方法を調べたらXcodeの機能で入力したキーワードでプロジェクト内を検索し、一斉置換する方法があったので方法を残しておきます。

- - -
# 詳細

以下の画像のように左上のナビゲータバーの虫眼鏡アイコンを選択し、検索機能に遷移します。

![スクリーンショット 2023-12-08 18.52.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/72123cd4-a2fd-a650-9564-812484410c39.png)

`Find>Text>Containing`の内、`Find`をクリックすると、選択肢が表示されるため、`Replace`を選択します。

![スクリーンショット 2023-12-08 18.53.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/7810cd25-aeee-0923-e366-fbad81bf1642.png)

上の入力フィールドに検索したい値を、下の入力フィールドに置換したい値を入れ、上から順に置換したい場合は`Replace`を、一斉置換したい場合には`Replace All`をクリックします。

(Replace All実行前)
![スクリーンショット 2023-12-08 18.54.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/05d0631f-59e1-3cc0-2cbd-1b07566aa47e.png)


実行時にどの要素の置換に成功したかが表示されます。
下記の画像のように、置換に成功した場合には**緑色のアイコン**で表示され、失敗した場合には**赤色のアイコン**で表示されます。

(Replace All実行時)
![スクリーンショット 2023-12-08 18.53.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/d00ed1df-9491-58f1-719a-2f0300b6b6be.png)


再度同じ検索条件で検索してみると、確かに置換に失敗した箇所のみが表示されています。
また、プロジェクト情報に関しては変更されると支障が出やすい情報であるためか、一斉置換はできないようです。

(Replace All実行後)
![スクリーンショット 2023-12-08 18.54.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/d92c08cc-24e9-b1d4-7cb4-4daf9e8d47c7.png)


`Refactor>Rename`が上手く動作しない時にはこちらの方法を試してみるのもありかもしれません。
【Unity】とりあえずQuest 2を繋げてみた。Quest Linkで実行する方法

## まえがき
Unityを使ったVR開発は刻一刻と変化しており、ネット上には古くなった記事も多い。特にOpenXRに対応してから... という話は置いといて、今回は初歩の初歩！2023年12月にUnityでVR開発を始めて、実行するまでについて書いておこうと思う。

## この記事でやること
この記事では、Oculus Quest 2とPCを接続し、Unityエディタ上で実行するところまでを解説。
実際にビルドして実行するよりも、PC接続でテストする方に手間取ったので、ビルドについては書かない。

## Quest 2の準備
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638306/d68c67f3-01a9-5733-35a2-8b0f5cb7ec67.png)

Oculus Quest 2（私のは古いのでMetaではない。）を開発者モードにしておく。なぜかスマホのMeta Questアプリからしかできないクソ仕様。このアプリが嫌いなので、次はMeta以外のHMDを買おうと思っている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638306/940b2ac9-0dec-4f78-3382-9667435d9126.png)

続いて、PCの方に関連ソフトを入れる。
一つは「Oculus」。PCVRモードでゲームする人は既に入れていると思う。
公式サイトからDL: https://www.meta.com/jp/quest/setup/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638306/57ed3a44-ace3-bc3b-9e3e-d4c5ef2391f5.png)

もう一つは「Meta Quest Developer Hub」
指示に従って初期設定をする。私はこれをダウンロードするだけで忘れていたので、エラーが出たw
DL: https://developer.oculus.com/downloads/package/oculus-developer-hub-win/
この辺はMetaの公式チュートリアルが分かりやすいのでおすすめ。ありがたいことに日本語化されている。
チュートリアル: https://developer.oculus.com/documentation/unity/ts-odh/?locale=ja_JP
あと、Quest 2を起動したらUSBなんとかを許可するか聞かれるから、許可しておこう。

## VRテンプレート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638306/da9faec3-f07d-542d-dbc3-06121d95342d.png)

UnityハブからVRテンプレートを開く。使用しているUnityのバージョンは2022.3.7f1。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638306/df3bcbe7-2a66-b645-20d0-b59f9cf72128.png)
かなり重いので、私のパソコンだと高確率で落ちるが、大体2回目で開くことができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638306/525f8ca5-4101-8093-e200-7844c4b6a6b6.png)
VRテンプレートを開くと、サンプルが出てくる。少し前まではここまでお膳立てしてもらえなかったようで、この後コントローラー等の設定をする必要があったようだが、2023年12月現在はテンプレートを開いた状態で実行できる状態だ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638306/1debb99c-d7c3-0275-b0d2-b00b61162ec7.png)
通常通り実行すると、Linkケーブルで繋いだQuest 2上でチュートリアルのようなものがプレイできると思う。

## まとめ
**現在は面倒な設定をしなくてもOpenXRでQuest 2を使える**。これがこの記事で伝えたいこと。
rails チュートリアル5.1.2のcustom.scssのbootstrapがインポートできない時の解決策

assets:precompileをどこかのタイミングでやってしまい、ずっとcustom.scssの
```
@import "bootstrap-sprockets";
@import "bootstrap";
```
が反映されず、詰っていたので解決策を記しておきます。
(先にページのスーパーリロードを試してみてください。Shift+リロードボタンでできます。)

<h1>環境</h1>
・Windows11Home<br>
・WSL2<br>
・Ubuntu 22.04.3 LTS

<h1>結論</h1>

sample_app/public/にあるassetsというディレクトリを消したら無事反映されました。
（sample_appはrailsチュートリアル中に作ったプロダクト名）


①```bundle exec exec rails assets:clean```の実行。
②sample_app/tmp/cache/assetsの削除。（assetsのディレクトリごと削除```rm -r assets```）
③sample_app/public/にあるassetsというディレクトリを消す。

①と②に関してはやらなくても反映できるみたいですけど、念のためやっておきました。

ちなみにapp/assets/stylesheetsにはチュートリアル通りに進めていると、
application.css と custom.scssの二つのファイルしかないはずなので、余分な他のファイルがある場合は消しましょう。
FTPサーバー導入してみた

# 今回行ったこと
aws上にあるubuntuに対して、vsftpdをインストールして
ネットワーク越しにファイル転送ができるようにしました。

# 今回行わなかったこと
- WFの設定
- ftps化
※接続については、awsのセキュリティグループにてIP制限を行っているため
セキュリティの考慮は厳密に行っていません

# 導入手順
## vsftpdのインストール
今回はvsftpdを採用しました。

既にaptが利用できる状態だったので、そのまま利用しました。
```
sudo apt install vsftpd
```

```
vsftpd -v
vsftpd: version 3.0.5
```

## FTP用ユーザーの作成
無事、インストールができたことを確認したので
FTP用のLinuxユーザーを作成します。

```
sudo useradd -m hoge
sudo passwd hoge
```

## 起動と自動起動の設定
```
sudo systemctl start vsftpd
sudo systemctl enable vsftpd
```

```
● vsftpd.service - vsftpd FTP server
   Loaded: loaded (/lib/systemd/system/vsftpd.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2020-05-19 12:55:57 JST; 18min ago
 Main PID: 8044 (vsftpd)
    Tasks: 1 (limit: 9479)
   CGroup: /system.slice/vsftpd.service
           mq8044 /usr/sbin/vsftpd /etc/vsftpd.conf
```

## vsftpd.confの設定
サービスがアクティブになることを確認したので、
設定を進めていきます。

今回は、FTPを利用できるユーザーの制限と
ディレクトリの閲覧に関する制限、パッシブモードの設定のみ行なっています。
```
# ログインディレクトリを変更
chroot_local_user=YES
# FTP許可をホワイトリスト方式にする
userlist_deny=NO
# ログイン時のディレクトリを指定
local_root=/home/hoge
# ファイルアップロード時のパーミッションを指定
local_umask=002
allow_writeable_chroot=YES
# パッシブモード
pasv_enable=YES
# パッシブモードのポートに50000~60000を割り当てる（※適宜読み替えてください）
pasv_min_port=50000
pasv_max_port=60000
```

## FTP接続できるユーザーをホワイトリストで指定します
vsftpd.user_listを作成して
中身には「`hoge`」とだけ記載しました。

## 再起動
設定を反映させるために再起動します。
```
sudo systemctl restart vsftpd
```

`ftp localhost`してみて接続が確認できました。
ログについては、デフォルトだと`/var/log`に出力されるようです。

# 参考
[Vsftpd、FTPサーバーのインストールと基本構成](https://ubunlog.com/ja/vsftpd-instalar-un-servidor-ftp-ubuntu/)
[UbuntuでFTP（vsftpd）が行えるようにする方法](https://intellectual-curiosity.tokyo/2019/07/19/ubuntu%E3%81%A7ftp%EF%BC%88vsftpd%EF%BC%89%E3%81%8C%E8%A1%8C%E3%81%88%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/)
FreeRTOSを動かしてみる(5)

ちょっとオレンジ軍の猛攻がすごいので先にstubを作る。

# スタブの仕組み
qemuを動かしているホストPCとの通信が出来ると思われるので
ホストPCに仕組みを作る

最終的にはLinuxのIPとFreeRTOSのIPをつなげたいのでこんな感じにする

## 最終的な理想
![理想イメージ](https://kroki.io/graphviz/svg/eNp9T90KgjAUvvcpxq7zCUKvJAiCoryTiNkObmRzHCcY4bvn5i9anYvDdr4fvo_LDJkWJCJvj7SDTD24RBKQONq6iyo4JKVgGgKaFjXdkNK88vaDRaU4cHp1tLJKO6d7XpUG8HaQqqp7Vzs5SyEPqDtTbzyvdDHDDMxMOBObDmNazxwcLnXSc_anPtGkHTEwAlCBWTKk9kNLa5vbkkbMCI037VXYHQKc4-Nl3XNA6Ajgj4z4Nx_abPglW2NXl6p4wuBg30ZIlfWstpUfOsJSj1-R5gNg45bh)

## とりあえずの実装

![とりあえずの実装イメージ](https://kroki.io/graphviz/svg/eNqNU11vgjAUfedXNH0ev4DAkzMxMdmyuSezLBWu0gil3rabxvjfRz9wgLjYB0LvOef2tKct-A6ZLMmMnCPSDmRiX3AkKVnNElcR8GOLqUYDSeRLTQFrVTIJKd00R_pElD5V7QQbIwoo6KejKbPxzfPKKA34teTCHMNCdlRsA1VKXZlG1_KNbsVwB7on7Im1x5iUvQ4O53IdOIvX4OhPe8VAl4AC9JjBZZxZWnsYdpO67BEu960Wpq5P0049JLHJQSk6YKimhs7Qx2zsVjZV1aF5CfkecMSw-jhzvJAEF99cPWb5ALWZdmyRodFt52O-WD637Ycuxp3nCPC2enkfde-t0DHoDQH_ic9f1IkIyVSbOMM7OQ4Pxv_5b5s8iTN3rlEXQZxdd2-nXOxCKw-2kvCCnHQbdcHYWc8tx0PQuTvmoh-F5tCze4upYjUklprYvSTB3xBkFmTT4DZx_pJLdPkFeIASMQ==)

つまり IPoverIP という無駄

## Linux側ドライバ
target appからは普通にIPを使用するように使いたいので
仮想NICドライバを実装する。

# スタブの実装
## 仮想ethernet ドライバ

まずは送信だけ
[https://github.com/tyano461/virtether](https://github.com/tyano461/virtether/tree/a01513a6c4cf924b65edd36cbdd6d73d80da2aa4)

以下コマンドで /var/log/syslog にarp requestが飛んでいるのが見えるはず
受信は実装していないので ping疎通はできない。
```
make
sudo insmod virtether.ko
sudo ifconfig eth0 up
sudo ip a add 192.168.100.1/24 dev eth0
ping 192.168.100.2
```
削除は以下

```
sudo rmmod virtether
```

----
今回はFreeRTOSほぼ関係なし

# 参考
https://groups.google.com/g/linux.kernel/c/7X7ZPRifIWc?pli=1
LINEBotでメッセージを送信できる状態にする

## 背景

- 情報の更新を検知したら、LINEへ告知できるような状態にしたい。

## 参考
https://developers.line.biz/ja/docs/messaging-api/getting-started/#step-four-create-channelh

下記のScrapのまとめ記事です。

https://zenn.dev/rurucun/scraps/659658c11609b0

## 料金

無料では、200件まで。
無料でWebhookで送信できる、DiscordやSlackのほうがおすすめな気がします。

![](https://storage.googleapis.com/zenn-user-upload/1102d998a6d8-20231130.png)


https://www.lycbiz.com/jp/service/line-official-account/plan/

## LINE Developer 登録を済まし、開発者として登録する。

https://developers.line.biz/ja/docs/line-developers-console/login-account/

## チャネルを作成する。
新規プロバイダーから 「Create a new channel」を選択。
「Messaging API」を作成する。
![](https://storage.googleapis.com/zenn-user-upload/ccd999d30d94-20231120.png)

Bot名と説明、その他必要事項を入力し、Botを作成する。

## Botの確認をする

作成されたBotをクリック
![](https://storage.googleapis.com/zenn-user-upload/2d95a13fc444-20231120.png)

タブの「Messaging API」をクリック

Bot informationに、QRコードが表示されているので、これをLINEで読み込むとBotを友達追加ができます。
![](https://storage.googleapis.com/zenn-user-upload/c880bc5b933c-20231120.png)
## あいさつを編集する

「友達追加」すると、自動で下記の文章が送信されるので、これを編集する。

```
ともさん
はじめまして！Bot名です！
友だち追加ありがとうございます😉

このアカウントでは、最新情報を定期的に配信していきます💌
どうぞお楽しみに🎁☆
```

### LINE Official Account Manger で編集する
LINE Developerではなく、LINE Official Account Manager で編集する必要があるので、Basic settings からLINE Official Account Manger にアクセスする。

![](https://storage.googleapis.com/zenn-user-upload/eafe0aa9d71a-20231120.png)


サイドバーから応答設定>あいさつメッセージ>「あいさつメッセージの設定画面を開く」をクリック。

![](https://storage.googleapis.com/zenn-user-upload/cdd68b64c7cf-20231120.png)

あいさつメッセージの設定画面が開くので、ここでメッセージを編集します。

![](https://storage.googleapis.com/zenn-user-upload/f5e63ab45459-20231120.png)
Dynatrace OneAgent 診断情報の展開bash関数

Dynatrace OneAgent は以下のような多くの機能を持っています。

- Java アプリへのAPM自動インジェクション
- Node.js アプリへのAPM自動インジェクション
- その他言語のアプリへのAPM自動インジェクション
- Web Server へのJavascript注入
- OSデーモンプロセス群監視
- OSログの差分送信
- その他一般プロセス監視
- 重要プロセスのログファイルの自動認識
- 重要プロセスのログファイルの増分自動送信
- ネットワーク監視
- 被観測プロセスへの通信観測
- インフラhardware観測
- OS観測
- Kubernetes/Openshift固有機能監視

なのでoneagent自体のログも多くのファイルに分けて出力されます。
これらOneAgentログファイル群とOSの基本情報などをまとめて取得してzipする機能があります。

https://docs.dynatrace.com/docs/shortlink/oneagent-diagnostics

このzipファイルをDynatraceに送信していただくと自動的に診断されます。

自分で調べたい場合もあるかと思います。OneAgent SupportArchive zipファイルは入れ子になっていて少し手間がかかるのです。なので私は以下のbash関数でzipを展開しています。

```terminal
$ declare -f suparc_oa
suparc_oa ()
{
    unzip $1 -d ${1/.zip//};
    cd ${1/.zip//};
    unzip -O Shift_JIS support_archive.zip;
    rm -f support_archive.zip;
    unzip -O Shift_JIS diagnostic_files.zip -d d;
    rm -f diagnostic_files.zip;
    //cd ..
}
```

参考になれば！
MySQLの日付検索とLaravel Eloquentでの実装方法

### MySQLでの日付検索

`datetime` 型は日付と時間を含むため、特定の日付での検索には `DATE()` 関数が便利です。

#### 具体的なSQL例

```sql
SELECT *
FROM items
WHERE DATE(datetime_column) = '2023-12-05';
```

このクエリは、`items` テーブルの `datetime_column` カラムの日付部分だけを取り出し、指定日付と比較します。

#### `items` テーブルのサンプルデータ

| id | name       | datetime_column       |
|----|------------|-----------------------|
| 1  | アイテムA   | 2023-12-05 10:00:00   |
| 2  | アイテムB   | 2023-12-05 15:30:00   |
| 3  | アイテムC   | 2023-12-06 08:20:00   |
| 4  | アイテムD   | 2023-12-04 21:45:00   |

上記のクエリにより、idが1と2のレコードを取得します。

### LaravelでのEloquent使用例

Laravelでは、`whereDate` メソッドを使って日付で検索します。

#### コード例

```php
$records = Item::whereDate('datetime_column', '2023-12-05')->get();
```

ここで、`Item` モデルの `datetime_column` カラムで指定日付のレコードを取得しています。このEloquentでは、同じくidが1と2のレコードが取得されます。


### 参考
https://laravel.com/docs/10.x/queries#additional-where-clauses
SwiftUIでリストに対してPull to refreshを実装しようとしたら勘違いからハマりかけた話。

# 概要

Listに対してPull to refresh(画面を下に引っ張った時に何かしらの処理を行うこと。gifアニメーション参照)を実装しようとした時に、ちょっとした勘違いから誤った実装方法を試してしまい、時間を使ってしまったので自戒を込めて記事を書きます。

![Simulator Screen Recording - iPhone SE (3rd generation) - 2023-12-08 at 18.05.51.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/dbb867fd-8d5b-83a3-062f-e63930869bff.gif)

- - -
# 詳細

事の経緯としては、
1.Pull to refreshの実装方法を調べた時にたまたま出てきた情報を鵜呑みにしてscrollviewとrefreshableモディファイアはセットで使わなければならないと思い込んでいた(pull to refreshにはスクロールする動きが必要だと思っていたのもあり)

2.いくらScrollviewとセットでListを使用しても画面が表示されるおかしいと思い始める

3.Listで実装している例を発見し、試してみたところ、無事解決！

という流れです。
以下では正しい実装例と私が間違って実装していた方法の両方を説明します。

## 正しい実装例

まず、実装例のコードですが、以下のように書きます。
至ってシンプルで、`List`に対して`.refreshable`を付与し、クロージャ内で処理を記述するという流れです。
これは`Form`などの違う部品に対しても付与可能です。
より詳しい仕様について知りたい場合は、備考欄にある公式ドキュメントを参考にしてください。

```swift:ContentView

import SwiftUI

struct ContentView: View {
    
    let fruits:[String] = [
        "りんご",
        "スイカ",
        "みかん"
    ]
    
    var body: some View {
            List{
                ForEach(0 ..< fruits.count){ index in
                    Text(fruits[index])
                }
            }
            .refreshable {
                // リフレッシュ時の処理を書く
        }
    }
}

```

ただ、私の場合には誤って以下のように記述をしてしまい、そもそもViewが正しく描画されない現象が起こりました。

```diff_swift:ContentView
import SwiftUI

struct ContentView: View {
    
    let fruits:[String] = [
        "りんご",
        "スイカ",
        "みかん"
    ]
    
    var body: some View {
+        ScrollView{
            List{
                ForEach(0 ..< fruits.count){ index in
                    Text(fruits[index])
                }
            }
            .refreshable {
                // 処理
+            }
        }
    }
}
```

上記のように`ScrollView`の中に`List`を記述すると以下のように画面が真っ白な状態になります。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/8522024a-71da-8b04-df48-3ee4b84ef579.png">

この状態では画面を下に引っ張ったとしても何も画面が反応しない状態になります。

View Hierarchyで画面を参照してみると、以下のような構造になっており、そもそもView以降の要素が表示されていないことが分かります。

![スクリーンショット 2023-12-08 18.28.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576326/af3bb43e-8e86-3b50-25a7-386fb5cd09d7.png)
(View Hierarchyで確認した構成要素。`SystemScrollView`が一番上に表示されている構成要素になっており、List以降の要素が存在しない状態になっている。)

このことから、`ScrollView`の中に`List`を記述するのはあまり好ましくない使用法であると考えられます。


# 結論
- `refreshable`はPull to refreshを仕掛けたい要素に対して付与するモディファイア
- 原則、`ScrollView`の中に`List`を書くのは避けるべき

# 備考
公式ドキュメント
[refreshable(action:)](https://developer.apple.com/documentation/swiftui/view/refreshable(action:))

[List](https://developer.apple.com/documentation/swiftui/list)

[ScrollView](https://developer.apple.com/documentation/swiftui/scrollview)
遂に開設！Box Japan Youtubeチャンネル！

## はじめに
Box アドベントカレンダー2023、11日目の記事です。
本記事ではBox JapanのYoutubeチャンネルについてまとめています！


## Box Japan Yotubeチャンネルを開設しました！

おかげさまで2023年に10周年を迎えたBoxJapanですが、
より日本のお客様にBoxの事をお伝えするため、Box JapanのYoutubeチャンネルを開設いたしました！
「製品関連」「事例」「会社紹介」など様々なカテゴリーの動画を掲載する予定です。

https://www.youtube.com/@box-Japan




## どのような動画あるの？
現在4つのプレイリストを用意しております！
そもそもBoxってなんだろう？の方、Boxを検討中の方、古くからBoxをご愛顧いただいている方、
どのような方にも参考としていただける動画を用意しています。


### 1.Boxには便利な機能がたくさん！Box機能紹介
Boxには「こんなこともできるの？」というような便利な機能がたくさんあります！
そんなBox機能紹介の動画です。

https://www.youtube.com/playlist?list=PLqWqe3QC4ZFq-JXQ3GC-zAPTVNsw3ioMb

### 2.Box製品デモンストレーション動画
Box製品の実際の画面等をご覧いただけます！

https://www.youtube.com/playlist?list=PLqWqe3QC4ZFpgpZMZRbSQfx-p59MJ4DMZ

### 3.Boxユースケース
こんな悩みありませんか？Boxの機能を使って解決できるかも知れません！

https://www.youtube.com/playlist?list=PLqWqe3QC4ZFrYjXKRWLeLz5GQF5nUU7H2

### 4.Box on Box
Box社内ではどのようなBoxの使い方をしているの？
Box社員のBox活用についてご紹介しています。

https://www.youtube.com/playlist?list=PLqWqe3QC4ZFrilR3kPR5ikithb2ltPiTo



## おすすめ動画 2本
プレイリストの中でも特におすすめの2つの動画はこちらです。

### ①Box AIのデモ動画

まずはこれからリリース予定のBoxAIのデモ動画ですね！是非一度ご視聴いただきたいです！

コンテンツクラウドに高度なAIモデルをネイティブ統合する強力な新機能であるBoxAI。
Boxの高度なセキュリティ・権限設定に従って、AI活用ができるので安心ですよね。


Box AI for Notes、Box AI for Documentsに関する3分ほどのデモ動画です。
小腹が空いた際のカップラーメン、年越しそばが出来上がるまでの間での時間で
サクッとご視聴いただける内容になっております。

https://www.youtube.com/watch?v=Z-tqLc_sBnQ

>Box社員のBox AIの使い方に関してはこちら！
>Box Advent Calendar 2023 2日目の記事です。

https://qiita.com/kamadhi/items/e4eb8b8740aecb618a12


### ②Boxで生まれる新たなアイディア（BoxCanvasの活用）

お次は2023年にリリースしました仮想ホワイトボード機能、**BoxCanvas**に関する動画です。

>仮想ホワイトボード機能 Box Canvas
https://www.box.com/ja-jp/canvas
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3634291/56fddee9-0d06-dd17-02cf-aa7ec7a61972.gif">


リモートと出社、勤務形態が混在する現代において
社内関係者の方とのアイディア出しや、出たアイディアのまとめ作業に苦労されている方はいませんか？

https://www.youtube.com/watch?v=d3mkyr-aYYg&list=PLqWqe3QC4ZFrilR3kPR5ikithb2ltPiTo&index=1

そんな時、BoxCanvasとBoxNotesを組み合せることでより効率的に業務を行うことができると考えております。
動画の中ではBox社の活用事例を交えながらご紹介いたします。


## さいごに


:::note warn
**引き続き皆様にとってお役立ちするコンテンツを作成していきます！
チャンネル登録をお願いいたします！**
https://www.youtube.com/@box-Japan
:::
【UEFN】Verseを利用してランダムチーム分けトリガー作ってみた

## 背景
もともとチームの振り分け方をVerceでどのようにやるのか知りたかったので調べました。結構簡単にできそうだったのでちょっとひねりを加えてランダムに振り分けるトリガーを作ってみました。

## 実装
### 動画
<iframe width="560" height="315" src="https://www.youtube.com/embed/SinYYATqaPE?si=-fZehvm-h3NnQB07" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

### 利用するDevice
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692976/a348714d-e057-50c4-4ed0-70b1e16aadf6.png)

### 全体コード

```
using { /Fortnite.com/Devices }
using { /Verse.org/Random }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }


team_random_trigger_device := class(creative_device):

    @editable
    random_trigger:trigger_device=trigger_device{}

    OnBegin<override>()<suspends>:void=
        random_trigger.TriggeredEvent.Subscribe(GetRandomTeam)

    GetRandomTeam(QAgent:?agent):void=
        randmNum := GetRandomInt(0, 2)
        if (Agent:=QAgent?):
            TeamCollection := GetPlayspace().GetTeamCollection()
            TeamArray := TeamCollection.GetTeams()
            for (TeamNumber->Team : TeamArray):
                if (TeamNumber = randmNum):
                    if (TeamCollection.AddToTeam[Agent, Team]):
                        Print("Team {TeamNumber} になったよ！")
```
:::note warn
GetRandomIntを使うときは以下のusingを忘れずに書いて！
`using { /Verse.org/Random }`
:::


#### 処理の流れ
1. トリガーを検知
1. チーム数分のランダムな数を生成
```
        randmNum := GetRandomInt(0, 2)
```
1. このゲームのチームを全部取得
```
        # チーム関連の情報オブジェクト取得
        TeamCollection := GetPlayspace().GetTeamCollection()
        # チームを取得
        TeamArray := TeamCollection.GetTeams()
```
1. foreach文で生成した数字と同じ順番にあるチームを取得
```
        # インデックスとチーム型を扱えるようなforeach文を利用
        for (TeamNumber->Team : TeamArray):
                # インデックスと生成した数字が同じかどうか
                if (TeamNumber = randmNum):
```
1. `AddToTeam`でそのチームに指定する
```
        # チームに設定する
        if (TeamCollection.AddToTeam[Agent, Team]):
                 Print("Team {TeamNumber} になったよ！")
```

## まとめ
はじめはplayer型などがチームに関する属性持っているのかと思っていましたが全く別のチームオブジェクトがプレイヤーを持っている感じで面白かったです。
大人数で遊ぶワールドで生かせそうなデバイスになるかもですね！

## 余談
この度UEFN/Verseに関するオープンコミュニティサーバーを建ち上げました。ちょっとでも興味があれば奮ってご参加くださいませ。

https://discord.gg/cK4daQyD
FiveMのRPサーバ(QBCore)でローディング画面をカスタムする方法

## まえがき
自分用メモ書きです。

前提として、私自身はサーバサイドにはある程度…ですが
クライアントサイドの言語には明るくないので独自解釈交えますご容赦ください

## 前提
1. QBCore Frameworkでサーバを立てていること
1. resourceディレクトリに[qb]サブディレクトリが存在すること

## ローディング画面で好きな音楽 & 好きなムービー流したい！
とても簡単

```
[qb]/qb-loading/assets/
```
ディレクトリ下にいくつかフォルダがあるが、それぞれにmp3なりmp4なりを置く

:::note info
audioフォルダにはmp3を、imagesにはpngを、videoにはmp4を、てな感じ
:::

## 入れ替えたファイルを指定する
次にHTMLを編集して指定するファイルを変更していく

QBcoreは基本的なWEB開発と同じで、HTML, CSS, JavaScriptで成り立っている(※例外あり)ので、当然指定するファイルの入れ替えもHTMLから行う
(ESXとかの他のフレームワークは全く知らないので同じか不明)

:::note info
面倒な人は元から入っているファイルを削除して、入れ替えたファイルに**消したファイル名と全く同じ名前**をつけてやれば、わざわざHTML編集しなくて良いのでオススメ
:::

まぁそれだと入れ替えたファイルの名前が変わってしまうので今回はHTMLまで編集する

### 修正する箇所

オーディオ
``` diff_html
- <audio autoplay loop id="audio" src="/assets/audio/noncopyright.mp3"></audio>
+ <audio autoplay loop id="audio" src="/assets/audio/入れ替えたファイル名.mp3"></audio>
```

スライドショー(静止画)をOFFにしてムービーを再生
``` diff_html
-             <q-carousel animated v-model="firstslide" :autoplay="secondap" ref="carousel" infinite>
-                <q-carousel-slide :name="1" img-src="/assets/images/1.png"></q-carousel-slide>
-                <q-carousel-slide :name="2" img-src="/assets/images/2.png"></q-carousel-slide>
-                <q-carousel-slide :name="3" img-src="/assets/images/3.png"></q-carousel-slide>
-                <q-carousel-slide :name="4" img-src="/assets/images/4.png"></q-carousel-slide>
-            </q-carousel>
+            <q-parallax :height="1080">
+                <template v-slot:media>
+                    <video id="video" width="1920" height="1080" autoplay loop muted>
-                    <source type="video/mp4" src="/assets/video/freestockvideo.mp4"></video>
+                    <source type="video/mp4" src="/assets/video/入れ替えたファイル名.mp4"></video>
+                </template>
+            </q-parallax>
```
:::note info
imagesディレクトリのスライドショーか、videoディレクトリのムービーの
**どちらか**しか再生不可なので、コメントアウト等しておくと良い
:::

### FiveMからテストサーバにconnectしてみる

ドラクリオットOPムービーを拝借しました
**ドラクリオットは神**
~~(ローカル環境で公開していないので大目に見てください…)~~

他にもキーバインドの削除やらダイアログの削除なんかもカスタムしているのでそちらはおまけで

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2697232/5c1a9057-8f53-3b63-63b4-4659d853d7f9.png)

__※黄色丸印で囲んでおかないとFiveMのローディング画面に見えないと思ったので一応__

## おまけ

41行目～211行目までコメントアウト等で消すと、
キーバインドのヒントなり、ダイアログなり、ローディングバー等が消えます

どのブロックがそれぞれどれに対応しているのかは、
class名等をちゃんと読んでもらえたら英語で書いてあるので頑張ってください

## 引用
なにか参考にしたっけな？と思ったのですが、
多分コードを読んで独自解釈したので引用はない…はず
IBM Cloudのwatsonx.dataにアクセス可能なAPIKEYをサービスIDから作成

IBM CloudのAPIKEYは自分のIDに紐ついたものは[IBM Cloud APIKEYの作成(取得)方法](https://qiita.com/nishikyon/items/fa8b951267efbe911fe7)で取得できますが、このAPIKEY、自分のIDに紐つく権限が全てついてしまっています。

watsonx.dataのみにアクセス可能なAPIKEYは、サービスIDから作成することが可能です。この記事はIBM Cloud上のwatsonx.dataにアクセス可能なAPIKEYをサービスIDから作成する方法について説明します。
また[IBM Cloud APIKEYの作成(取得)方法](https://qiita.com/nishikyon/items/fa8b951267efbe911fe7)で取得したAPIKEYはIBM Analytics Engine Sparkからのカタログアクセスができなかったので、IBM Analytics Engine Sparkからアクセスする場合はこちらで作成してみてください。

# 1. サービスIDの作成
参考:公式ドキュメント「[サービス ID の作成](https://ondeck.console.cloud.ibm.com/docs/account?topic=account-serviceids&interface=ui#create_serviceid)」

### 1. IBM Cloud コンソールで **「管理」** >  **「アクセス (IAM)」** と移動し、 **「サービス ID」** を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/89d70406-c595-0cf6-5d8d-b384b0771a2e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/5f48c297-0b2f-a79e-26cb-0b9ea2011c96.png)



### 2. **「作成」** をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/7e1d3784-94b9-c9c3-6567-b8a2a0ad7d19.png)

### 3. サービス ID の名前を入力し、「作成」をクリック
必要に応じて説明も記入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/ba320693-8b08-6c30-26af-54024168143f.png)

### 4. 「アクセス」タブの「アクセス ポリシー」から「アクセスの割り当て」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/a373f16c-1888-753e-9acf-f015775c66cd.png)

「xxxxへのアクセスの割り当て」（xxxxはサービスIDの名前）ページが開きます。

### 5. 「アクセス ポリシー」を選択し、「ポリシーの作成」セクションで、［UI］タブを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/a6178b95-175d-a6d9-0ac7-642bd266bccc.png)

### 6. サービスの検索フィールドから`watsonx.data`を検索してクリックして選択し,
「次へ」をクリックします。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/e1516afc-6fd9-bcbc-d4fe-3762a14bb625.png)

### 7. リソースを設定し、「次へ」をクリックします。 
ここでは「すべてのリソース」を選択していますが、特定のリソースに制限したい場合は「特定のリソース」を選択し、詳細を設定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/ad6170dd-3465-4ab9-6003-dcdc910ffcbc.png)

### 8. 役割とアクションを設定し、「次へ」をクリックします。
下記のイメージは一例です。必要な役割にチェックを入れてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/b18e380e-a4a9-a0b7-ac71-77b3ab752b51.png)

### 9. 「追加」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/3306b008-b21d-e6d8-99d3-223735090ccc.png)

### 10. 「割り当て」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/eb026eab-d6f0-3880-7bc0-5b4f3c8b406a.png)

### 11. 「APIキー」タブをクリックし、「作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/b13f1ee8-d969-48e9-9a12-7c0156d17706.png)

### 12. APIキーの名前を入力し、「作成」をクリック
必要に応じて説明も記入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/1fd3d98b-1dbe-4b47-5485-4e5655a2ec91.png)

### 13. 作成されたAPIキーをコピーするか、「ダウンロード」をクリックしてキーを保存
この API キーは再度表示されないため、後で取り出すことはできません。API キーは300秒後に表示されなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294516/67b2804a-be72-b633-fd64-860ba14c5ff3.png)

以上です。
[9日目] 学校の食堂をIT化させる話　PythonからPayPay APIを使う

こんにちは、なりかくんと申します。
この記事はなりかくん Advent Calender 2023の9日目の記事です。

この話は、1日目から始めた学校の食堂をIT化させる話の続きとなります。今回は技術系の記事です。キャッシュレス決済システムを導入するにあたって、システム制作にPythonを利用します。

PythonでPayPay APIを扱っていくのでメモを残します。

# PayPay APIをPythonから触れるようにする
では、PythonからPayPay APIを触れるようにするために今回はライブラリを使います。
PayPayはなんと公式からPython SDKが提供されているので有効活用させてもらいます。

https://github.com/paypay/paypayopa-sdk-python

インストールは、このコマンドだけです。
```
pip install paypayopa
```

コードに関しては、公式ドキュメント通りにすると決済画面のURLが返ってきます。
```python
request = {
    "merchantPaymentId": "cb31bcc0-3b6c-46e0-9002-e5c4bb1e3d5f",
    "codeType": "ORDER_QR",
    "redirectUrl": "http://foobar.com",
    "redirectType":"WEB_LINK",
    "orderDescription":"Example - Mune Cake shop",
    "orderItems": [{
        "name": "Moon cake",
        "category": "pasteries",
        "quantity": 1,
        "productId": "67678",
        "unitPrice": {
            "amount": 1,
            "currency": "JPY",
        },
    }],
    "amount": {
        "amount": 1,
        "currency": "JPY",
    },
}

client.Code.create_qr_code(request)
```

QR画像だけ抜き出したいなーなどとも思いましたが、方法が分かりませんでした
（もしかしたら、決済画面に表示されるQRコードをキャプチャすればいいのかな。）

詳しい使い方は、以下の記事が分かりやすいです！ （他の記事任せ）

https://qiita.com/4geru/items/595922097ac186a1ee17

# 最後に
あれ、PayPay APIを使うってタイトルを最初に決めたら書くことがほぼ無くなってしまいました。
明日以降もっとしっかり記事を書いていきます。

最後までお読みいただきありがとうございました。
ListのオブジェクトをRemoveで削除しようとしたらできなかった話

# 初めに
C#でテスト用のツールを作成している際に、想定していない結果になったので処理を追ってみたところ、あるList要素の削除に失敗していることがわかりました。

# 原因
このListに格納していたのは独自に定義したクラスです。
このクラス定義にて、等価性に関する記述が抜けていたために、Listで削除対象のオブジェクトを評価することができていませんでした。

# 解決方法
以下のように、等価性をチェックする処理を追記します。

```cs
public class MyClass
{
    public int Value { get; }

    public MyClass(int value)
    {
        this.Value = value;
    }

    // 追記
    public bool Equals(MyClass other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Value == other.Value;
    }
    
    // 追記
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((MyClass)obj);
    }

    // 追記
    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }
}
```

# 終わりに
見つけるのが一苦労なバグを生み出してしまうので、今後繰り返さないように注意したいです。