OmniAICreator/Qiita-1.07M huggingface dataset:
【JavaScript】オブジェクトのループ処理

## `for...in`ループ

`for...in`ループを使用すると、オブジェクトのすべての列挙可能なプロパティを繰り返し処理できます。

```js
const obj = {
    name: 'Alice',
    age: 25,
    city: 'Tokyo'
};

for (const key in obj) {
    if (Object.hasOwn(obj, key)) {
        console.log(`${key}: ${obj[key]}`)
    }
}
```

- `for...in`ループは、オブジェクトの各キーを反復処理
- `Object.hasOwn()`メソッドを使用して、オブジェクト自身のプロパティのみを処理

## `Object.keys()`メソッド

`Object.keys()`メソッドを使用すると、オブジェクトのキーの配列を取得し、それを使って繰り返し処理できます。

```js
const obj = {
    name: 'Bob',
    age: 30,
    city: 'New York'
};

Object.keys(obj).forEach(key => {
    console.log(`${key}: ${obj[key]}`)
})
```

- `Object.keys(obj)`は、オブジェクトのキーを配列として返す
- `forEach`メソッドで各キーに対して処理を行う

## `Object.entries()`メソッド

`Object.entries()`メソッドを使用すると、オブジェクトのキーと値のペアを配列として取得し、それを使って繰り返し処理できます。

```javascript
const obj = {
    name: 'Charlie',
    age: 35,
    city: 'San Francisco'
};

Object.entries(obj).forEach(([key, value]) => {
    console.log(`${key}: ${value}`)
})
```

- `Object.entries(obj)`はオブジェクトのキーと値のペアを含む配列を返す
- 配列の分割代入を使用して、キーと値を簡単に取得できる

## `for...of`ループと`Object.entries()`

`for...of`ループを使用して、`Object.entries()`から得られた配列を繰り返し処理することもできます。

```javascript
const obj = {
    name: 'Diana',
    age: 28,
    city: 'London'
};

for (const [key, value] of Object.entries(obj)) {
    console.log(`${key}: ${value}`)
}
```

- `for...of`ループを使用することで、配列の各要素に対して簡潔に処理を行うことができる
Outrun を作る(まだ作ってない、これから) - 目次

> **追記：** 頑張って作ってしまった

https://qiita.com/Kray-G/items/2184362a8b85e06b7f8e

## はじめに

さて、Outrun 作るのを[一度断念](https://qiita.com/Kray-G/items/58a1b26dc22132cc1bab)したものの、再チャレンジしようかと。そこで、そんなにすぐにはできないので、連載形式で作ってみたらゴールにたどり着けるかも、ということで、長期レンジで進めたいと思います。

### なぜ断念したか

そもそもなぜ断念したかというと、途中で **「（作り方が）こうではないな」** と気づいたからですね。

[ここ](https://qiita.com/Kray-G/items/58a1b26dc22132cc1bab) で紹介している 3D レーシングゲーム（Time Trial On Edge）は最初 Outrun を目指していましたが、何が違うと気づいたかと言えば、**操作性** です。

Time Trial On Edge では、いわゆる **レイキャスティング** という方法でコースを描画しています。そして、左右キーがステアリングなのですが、ハンドル操作した際、車体自体が回転することで向きを変えます。これはこれで操作のリアリティといった観点では全く問題なく、むしろいい感じですね。

が、Outrun は違います（と思います（ような気がします（たぶん...）））。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/b3f9e39e-152e-9ec5-970f-97a146b813cf.png)

> こんなイメージ。

Outrun での左右操作は車体の向きを実際に変える（回転させる）のではなく、コースのライン取りとして動きます（...おそらく）。つまり、向かっていく方向はあまり変化せず、コースの右側に寄ったり、左側に寄ったりする感じの操作性なので若干違うかな、と。なので、Time Trial On Edge は Outrun と違う内容として完成させたのでした。

Outrun でもカーブの際には（疑似的に）向きを変えるような動きはしていますが、これも疑似的な感じで、実際に横を向いてしまう、みたいな操作にはなりません。

### コースデータ

コースデータも、レイキャスティング方式では準備が大変です。レーシングコースのように周回するような形であればスプライトベースのレイキャスティングはかなり最適解に近いのですが、Outrun のようにどこまでも続く～、みたいな感じだとコースデータの準備もどうしようか、となってしまいます。

> **追記：** 試行錯誤が多すぎて、全然記事書けませんでした...。すみません。完成してしまったので、ぜひプレイしてみてください。

* スクラッチ

https://scratch.mit.edu/projects/1092360558

* 記事

https://qiita.com/Kray-G/items/2184362a8b85e06b7f8e

ということで、以下削除 :cry: 。

## ~~目次~~

~~ということで、作っていくにあたり、概ねイメージは以下の通り。途中行き詰ったら変えるかも。が、ひとまずこれで。~~

1. ~~Pseudo 3D スクロール~~
1. ~~直進道路を作る~~
1. ~~コースのライン取り~~
1. ~~カーブを作る (1) 曲がる~~
1. ~~カーブを作る (2) 戻る~~
1. ~~上下のアップダウン~~
1. ~~道をつなげる（コースデータを作る）~~
1. ~~背景オブジェクト (1) 道路脇~~
1. ~~背景オブジェクト (2) 地面~~
1. ~~背景オブジェクト (3) 空・その他~~
1. ~~BGM を付ける~~
1. ~~タイトル画面を作る (1) タイトル表示~~
1. ~~タイトル画面を作る (2) オプション選択メニュー~~
1. ~~サムネイルを作る~~

~~Pseudo 3D スクロールの汎用スプライトを作ってあるので、それベースでとりかかろうかと思います。では！~~
【React備忘録】APIの取得データをジャンルごとに整形する

APIで取得したデータの整形方法について覚え書き。
取得したデータを単純に整形するだけなら容易だけど、各データが持っているジャンルごとに分ける時、「？？」となったので備忘録として残しておく。

例えば、施設などを検索したとき、ジャンル別に取得したデータを表示したいとする。

＜主な仕様＞
・ジャンルは、ジャンルごとにコードが割り振られている。
・取得した各施設のデータには、「施設名」「ジャンルコード」が入っている。
・検索すると「ジャンル名」の下に該当する施設がリストで並ぶ。

全文ではないけれど、ざっくりとコードは以下の通り。

```jsx
// poiResult ... APIの取得結果が入っている
// genre_code ... 各施設のジャンルコード

// ジャンルコードとジャンル名の定数
const POI_GENRES = [
  {'01': '交通'},
  {'02': 'ガソリンスタンド'},
  {'03': '公共'},
]

const PoiSearch = () => {
    const poiList = POI_GENRES.map(() => {
        const genreCode = Object.keys(genre)[0] // ジャンルコードを取得
        const genreName = Object.values(genre)[0] // ジャンル名を取得
    
        const filterdPois = poiResult.filter((item) => {
            return item.genre_code === genreCode // ジャンルコードが同じ施設を抽出
        })
    
        return {
            genreCode,
            genreName,
            pois: filterdPois,
        }
    })
    
    return (
        <div>
            {poiList.map((item, index) => (
                item.pois.length > 0 && (
                    <div key='index'>
                        ＜{item.genreName}＞
                    </div>
                    {item.pois.map((poi, index) => (
                        <div key='index'>
                            {poi.name}
                        </div>
                    ))}
                )
            ))}
        </div>
    )
}

export default PoiSearchPanel

```

最終的に整形されたデータ（poiListの返却値）は以下のようになる。

```
[
    {
        genreCode: '01', 
        genreName: '交通', 
        pois: [
            { name: '東京駅' },
            { name: '東京バスステーション' },
        ]
    },
    {
        genreCode: '02', 
        genreName: 'ガソリンスタンド', 
        pois: []
    },
    {
        genreCode: '03', 
        genreName: '公共', 
        pois: [
            { name: '東京都庁' },
        ]
    },
]
```
表示結果は以下のようになる。
ガソリンスタンドは取得結果が０件だったので表示されない（`item.pois.length > 0`）。
```
＜交通＞
東京駅
東京バスステーション

＜公共＞
東京都庁
```

ポイントとしては、取得したデータを各ジャンルごとにグルーピングすること。

① map()メソッドを使って、定数に設定された配列の各要素に処理を実行。
② ジャンルコードとジャンル名を取得する。
③ filter()メソッドを使って、ジャンルコードが同じ施設を抽出した配列を作成。
④ 最終的に、各ジャンルごとに「ジャンルコード」「ジャンル名」「施設（配列）」の入った配列が返される。

配列の処理をガンガン使うので覚えておく。
プログラミング生活二日目

今は、企業でプログラミングをはじめて二日目の夜である。

現在、京都大学の工学部の一回生をやっており、プログラミング経験は、実務をされている方々からすればほぼ皆無と言っても良い。

C++の教科書の演習問題を解いたり、Unityで簡単なゲームを作ったり、といったぐらいである。

なぜ、バイトに受かったのか、よくわからない。実力はほぼ皆無で、実際かなり手こずっている。

自分の割り当てられた仕事は、電子玩具のプログラミングで、言語はCである。

クラスがない分、少し残念だが、実力もねえくせにプログラミングをやらせてもらえるだけでとても幸せで感謝で沢山である。

というのも、将来は任天堂で働きたいからである。(ゲームのプログラミングをやりたいが、電気電子技術などでも嬉しい)

まあ、ここまで死ぬほど汚い文章を書いてきたわけだが、今、その会社で何をやっているかというと、子供用の学習パッド(とても画面のスペックが弱い系の)の内部プログラムを書き換えて、いろいろ動かして、いろんな関数を学んでくれ、というものである。

何が一番困るのかと言うと、目的というか目標が明確にないことで、これが一番辛いのである。

関数など無数にあり、GoToDifinitionを繰り返していては、日の暮れる作業である。

一体どうしたらいいのか。

そもそも、プログラミングバイトを始めるのは普通、大学２回生以上である。それは、大学でプログラミングを学んだ後というのが理由である。

大学一回生で実力がない自分は、肩身が狭すぎて棒人間みたいになりそうなのである。

ちなみに改造はいっっっっっさいできていない。そもそもソースコードがほとんど読めない。関数だらけで何をしたらいいのか、どこからやればいいのか、どうやって焼くのか、どうやってビルドするのか、どうやってギットを使うのか、何もかもわからないのである。

正直、はっきりいうと、「何がわからないのかが分からない」という最低最悪の状況とも言える。(分からないことが多すぎてよくわかんない)

2024/10/11/22:11
CloudFormationでWAFのマネージドルールが作成できなかった話

## はじめに
アウトアプットの練習で書いてみた
Resource typeはType: AWS::WAFv2::WebACL
## 結論
マネージドルールグループをRulesに追加した時Actionが二重に指定されているのが原因でエラーになり作成できなかった。

## 問題
CloudFormationでALBにWAFを追加してせキュリティを向上させようと思いマネージドルールを追加するtmplateを作ったけど作成できない。

実際のコードの一部
``` yaml
- Name: AWS-AWSManagedRulesCommonRuleSet
          Priority: 2
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesCommonRuleSet
              ExcludedRules:
                - Name: SizeRestrictions_BODY
                - Name: CrossSiteScripting_BODY
          Action:
            Allow: {}
          VisibilityConfig:
            CloudWatchMetricsEnabled: true
            MetricName: AWSManagedRulesCommonRuleSet
            SampledRequestsEnabled: true
```
ACLのRulesに指定したマネージドルールです。
色々ためしてルールを変えたりするとstackが作成される時もあって成功する時と失敗する時の差がわからず苦労した。

## 解決方法

公式のマネージドルールグループでルールの詳細を確認すると一つのルールにActionが指定されていたのでこっちがActionを決める必要なないと思いActionを上書きしないようにした。
``` yaml
- Name: AWS-AWSManagedRulesCommonRuleSet
          Priority: 2
          Statement:
            ManagedRuleGroupStatement:
              VendorName: AWS
              Name: AWSManagedRulesCommonRuleSet
              ExcludedRules:
                - Name: SizeRestrictions_BODY
                - Name: CrossSiteScripting_BODY
          OverrideAction:    #  ここで上書きするか変更できるが
            None: {}         #  countとNoneしかサポートされてない
          VisibilityConfig:
            CloudWatchMetricsEnabled: true
            MetricName: AWSManagedRulesCommonRuleSet
            SampledRequestsEnabled: true
```
こうすると無事にstackが作成できたのでActionの二重指定が原因だったと分かった。

## 終わりに
いろんな記事とchatgptで調べていたけどchatgptは割と嘘をいうので最終確認は記事を参考にしていた。コードもOverrideAction:を使用している記事もあったが、追加したルールをAllowにしないと機能してないのではないかと勘違いしていて書き換えてしまったのが原因かなと思う。

## 参考サイト

[AWS WAFを使ってIPアドレスを制限する方法](https://blog.css-net.co.jp/entry/2024/03/07/145211)

[AWS::WAFv2::WebACL](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-wafv2-webacl.html)

[ベースラインルールグループ](https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-baseline.html)
GitHub プロジェクトボードのアクセス権限問題の解決方法

## GitHub プロジェクトボードのアクセス権限問題の解決方法

GitHubで新規プロジェクトを作成したけれど、チームメンバー（Aさんがリーダーで、BさんとCさんがいると仮定）が新しいプロジェクトボードにアクセスできない、リンクが表示されない、といった問題に直面することがあります。ここでは、その解決方法を紹介します。

#### 問題の概要

新しいプロジェクトを作成しましたが、BさんとCさんがプロジェクトにIssueを追加できないという問題があります。

以前はトレロ風に進捗管理を行い、4つのカテゴリ（Issue、作業中、プルリクエスト、マージ済み）でカードを移動して管理していましたが、それがうまく機能していません。

プロジェクトにアクセスできず、リンクが表示されないという問題です。

#### 解決するための手順

1. プロジェクトのアクセス権限を確認

Aさん（リーダー）がプロジェクトボードの「Settings」にアクセスして、BさんとCさんに正しいアクセス権限が付与されているか確認することが重要です。

プロジェクトボードの「Settings」から「Manage access」に移動し、BさんとCさんがCollaborator（コラボレーター）としてリストされているか確認します。

もしリストされていない場合、Add collaboratorボタンをクリックして彼らを招待する必要があります。

新しいプロジェクトボードの場合、プロジェクト自体へのアクセス権も適切に設定する必要があるので注意してください。

2. リポジトリとプロジェクトボードのリンク設定

現在の GitHub Projects（特に新しいベータ版）では、「Linked repositories」という明示的なセクションが存在しない場合があります。その代わりに、以下の方法で確認と設定が可能です：

プロジェクトのメイン画面確認

プロジェクトのメイン画面左側のサイドバーを確認してください。

「Items」や「Insights」などの下に、リンクされているリポジトリのリストが表示されているはずです。

新しい項目の追加方法

プロジェクト画面で「Add item」ボタンをクリックします。

ドロップダウンメニューに「リポジトリ」リポジトリが表示されているか確認します。

表示されていれば、そのリポジトリとリンクされています。

プロジェクト設定の確認

プロジェクト画面右上の「⚙️」（設定）アイコンをクリックします。

「Settings」タブを選択します。

スクロールダウンして「Create item link defaults」セクションを探します。

ここで、デフォルトでどのリポジトリからIssueやPull Requestを追加するかを設定できます。

#### リポジトリ側からの確認

リポジトリのメインページに戻ります。

「Projects」タブをクリックします。

「新規プロジェクト」プロジェクトがリストに表示されているか確認します。

組織レベルのプロジェクト確認

もし組織レベルのプロジェクトを使用している場合、組織のページに移動します。

「Projects」タブを選択し、そこで「プロジェクト NEW」を探します。

#### 手動でのリンク追加

プロジェクト設定で「Add item」ボタンを使って、手動で「ビデオシェアアップ」リポジトリからIssueを追加してみてください。

これにより、プロジェクトとリポジトリの間に暗黙的なリンクが作成されることがあります。

3. プロジェクトボードのタイプを確認

GitHubには「クラシック」プロジェクトボードと「GitHub Projects（beta版）」の2種類があります。

新しいプロジェクトボードを使っている場合、特にアクセスやリンクの設定が異なるため、どちらのバージョンを使用しているか確認してください。

新しいプロジェクトボードの場合、BさんとCさんにWrite権限が必要なことがありますので、権限設定を再度確認することがポイントです。

4. Issueの追加方法を確認

BさんやCさんが新しいプロジェクトにIssueを追加できない場合、プロジェクトボードとリポジトリが正しくリンクされていないことが原因かもしれません。

Issueをプロジェクトに直接リンクするには、Issueページで「Projects」セクションから対象のプロジェクトを選択する必要があります。

この設定をBさんやCさんが正しく行えるように、リーダーがサポートすることが重要です。

5. GitHub サポートの利用

上記の方法でも解決しない場合、GitHubの公式ドキュメントを確認するか、GitHubサポートに問い合わせることを検討してください。

#### 問題解決のまとめ

リポジトリ設定でコラボレーターの確認を行う。

プロジェクトボード設定でリポジトリのリンクとアクセス権限を確認する。

プロジェクトボードのタイプを確認し、クラシックか新しいバージョンかを見極める。

プロジェクトとリポジトリの関連付けが適切に行われているか確認し、手動でのリンク追加も試してみる。

これで、BさんとCさんが新しいプロジェクトボードにアクセスしてIssueを追加できるはずです。それでもリンクが表示されない場合は、GitHubのアクセス権限設定についてさらに調べてください。
GA4（Google Analytics 4）におけるカスタムイベントの送信ロジックの実装

こんばんは。

今日は「GA4（Google Analytics 4）におけるカスタムイベントの送信ロジックの実装」について説明いたします。

# Google Tag Manager (GTM)を使用する場合
GTMを使うことで、カスタムイベントを簡単に設定・送信できます。

手順:

## Google Tag Managerにログイン

- 自分のウェブサイトやアプリに既にGTMがインストールされていることを確認します。

## 新しいタグを作成

- GTMダッシュボードで「タグ」セクションに移動し、「新しいタグ」をクリックします。
- タグのタイプで「GA4イベント」を選択します。

## タグの設定

- 「設定タグ」フィールドで、既存のGA4設定タグを選択します。まだ設定していない場合は、新しいGA4設定タグを作成し、測定ID（GA4の「Data Streams」から取得）を入力します。
- 「イベント名」フィールドにカスタムイベント名を入力します（例: purchase_complete、form_submissionなど）。
- 「イベントパラメータ」のセクションで、イベントに追加するカスタムデータ（パラメータ）を定義します（例: product_id、price など）。

## トリガーの設定

- 「トリガー」を追加して、イベントが送信される条件を設定します（例: クリック、ページビュー、フォーム送信など）。

## タグを公開

- すべての設定が完了したら、変更を公開します。

例:
たとえば、ユーザーがフォームを送信したときにカスタムイベントをトラッキングする場合、イベント名を form_submission として、フォームのIDをイベントパラメータとして渡す設定が考えられます。

# JavaScriptを使用してカスタムイベントを直接送信する場合

カスタムイベントをGA4に直接送信することも可能です。この場合、ウェブサイトやアプリに埋め込むJavaScriptコードを使用します。

## GA4タグを設定

ページにGA4のタグ（gtag.js）が含まれていることを確認します。基本的なgtag.jsのスニペットは次のようになります：

```html
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXXXX');
</script>
```

## カスタムイベントを送信するスクリプト

例えば、ユーザーがボタンをクリックした際にカスタムイベントを送信する場合、以下のようなコードを使用します。

```javascript
document.getElementById('my-button').addEventListener('click', function() {
  gtag('event', 'button_click', {
    'event_category': 'engagement',
    'event_label': 'My Button',
    'value': 1
  });
});
```

上記のスクリプトは、my-buttonというIDのボタンがクリックされたときに、button_clickというカスタムイベントをGA4に送信します。パラメータとしてevent_category、event_label、およびvalueが含まれます。

## イベントパラメータの追加

必要に応じて、カスタムイベントにパラメータを追加して、より詳細なデータを収集できます。パラメータはGA4のインターフェースで後から確認できます。

# イベントの確認
イベントが正しく送信されているかを確認するには、GA4の「リアルタイム」レポートでイベントが発生しているかチェックします。また、Google Tag AssistantやChromeのデベロッパーツールの「Network」タブで、collectリクエストを確認することもできます。

# まとめ

## カスタムイベント設計のポイント

- イベント名: 分かりやすいイベント名を設定します。例: purchase_complete、video_played。
- パラメータの設計: 必要なデータだけを収集するようにし、意味のあるパラメータ名を使用します。
- イベント数の制限: 不要なイベントやパラメータを送信しないようにし、データの効率性を高めます。

GA4ではカスタムイベントを活用することで、標準イベントではカバーできない特定のユーザー行動を細かくトラッキングできます。この手順に従って、カスタムイベントを設定してみてください。

今日は以上です。

ありがとうございました。
よろしくお願いいたします。
【長期インターン】はじめの半年間で経験したこと

## はじめに
2024年6月から、大阪のベンチャー企業で週1,2回のペースで開発系のアルバイトに参加している。
これまでにPHP8系への改修プロジェクトに参加し、自動修正ライブラリや公式ドキュメントをしながらPHP7系で動いているサービスをPHP8系に移行させた。現在は、リリースされているサービスに新機能を加えるプロジェクトを中心に業務の一部を担当している。


## これまでの業務

### ① PHPのバージョンアップ（調査・開発）
- PHP7系で動いているシステムを8系に移行した際にブラウザ上でエラーが発生するため、バージョンを変更しても問題ないようにErrorメッセージとWarningメッセージのデバッグを行なった。
<r>

- 最初はPHPCodeSnifferとPHPCompatibilityを使って、ソースコード内でコーディング基準に違反している箇所を洗い出し、自動修正を試みた。
<r>

- しかし、自動修正された内容を細かくチェックした結果、正しく修正が行われているか不透明であったため、公式ドキュメントを見ながら「廃止される関数」「仕様が変更になる関数」「非推奨になる関数」の3つにグループ分け、それらがソースコード内で使われているか確認する方針に変更した。

<r>

- また、他のプロジェクトの改修作業にもコマンド等を再利用できるよう、調査記録をチケットに書き残したり、`git grep`コマンドを使ってソースコード内で使われている関数のうち、変更がある関数を洗い出せるよう`.sh`ファイルを作成した。

<br>

### ② 部活MaaS　デバッグ（開発）

- 特定の条件下でDBエラーが発生するバグが見つかったため、ソースコードを修正した。
<r>

- 具体的には、2日前の日付で配車予約するとDBエラーが発生してしまうという内容で、原因は予約idが空であるにも関わらずAPI側がDBに配車予約情報を登録しようとしていたためエラーが起こっていた。
<r>

- 当然、今日より前の日付で予約できない仕様になっているため、API側から送られるレスポンスオブジェクト（res）にorder_idという配車予約情報の一部があるかないかで判定し、order_idが存在しなければDBにデータを送信しないようif文で分岐させた。resにはgo、backという行きと帰りの予約情報が含まれる配列のなかに、order_idをはじめとする配車予約情報が入っていた。
<r>

- 工夫したことは、if文を2回使わずに実装するために、配列の先頭だけをチェックすることで1回のforループで判定できるようにしたこと。特にresオブジェクトのデータは膨大であったため、2回もループ処理を行うとメモリを消費してしまう。
<r>

- 最終的に4つのテストケースで正しい挙動になっているか確認した。

|  行きの予約 |  帰りの予約 |
| --- | --- |
| ○ | ○ |
| ○ | × |
| × | ○ |
| × | × |


<br>

### ③ 部活MaaS　ユーザー画面（開発）

- 2台の車両が同じタブレットを使っていて、ユーザー画面で車両の位置情報を取得したときにどっちの車両かわからないという課題があった。
<r>

- 例えば、車両Aを予約していたユーザーが車両位置情報を検索したときに、全然関係ないもう一つの車両Bが表示されどっちがどっちかわからないという状態だった。
<r>

- そのため、ユーザー画面は次のような仕様に変更した。まず、ユーザーが車両の位置情報を取得したときにuuidをlocalStrageに保持する。その後、APIリクエストを送信する関数でlocalStrageからuuidを取得し、API側に送信するようにした。私はこのフロント部分を担当した。
<r>

- 最終的にDBにuuidカラムを追加して、該当する送迎車両のみを表示するようにした。API側の処理とDBについては社員の方が対応した。

<br>

### ④ 決済システムの導入（調査）

- お客様から、チャットボットのサービスに決済システムを導入したいという依頼があったとのことで「安く、簡単に」導入するという要件のもと、決済システムを提供しているSaaSを調査した。

- 具体的には、チャットボットとのやり取りが最初の5分間は無料、5分間を超えると60分または120分のチケットを購入することを求められるような仕様にしたいとのことだった。
- 最終的にStripeを導入することを提案し、導入までの流れについてWordにまとめてクライアントに提出した。
- Stripeに決めた理由は、APIを使った開発もノーコード・ローコードでの導入もどちらも対応していたところが決め手となった。 価格的にも他のSaaSと変わらなかった。
- また、公式ドキュメントも充実していて、決済システムの導入に関しては幅広いニーズに対応しているところが魅力的だった。
- ECサイトであれば、BASEやSquareもいいかもしれない。

<br>

### ⑤ リリース前動的テスト（テスト）
- Excelシートのチェックリストに従って動的テストを行なった。
- 地味な作業かつ一日中動的テストを行なっていたためしんどかったが、エンドユーザーが目にする部分なので、責任がある仕事だと認識し、モチベーションをうまく維持しながら取り組んだ。
【TypeScript】値の型によって条件分岐を行う方法

値の型によって条件分岐を行う方法を2つ紹介します。

# typeofを使った方法

valueがプリミティブ型に限定される場合、`typeof`を使って条件分岐をすることができます。

```tsx
const divideByType = (value: string | number) => {
	if (typeof value === "string") {
		return "string型です";
	} else {
		return "number型です";
	}
}
```

# inを使った方法

valueの型がプリミティブ型ではない場合、`typeof`を使った条件分岐は難しいです。なぜなら、全て`object`型と判定されてしまうからです。

この問題を解決するには、`in`を使います。`in`を使って一方の型のみが持つプロパティが`value`に存在するかどうかをチェックすることで、型推論を行うことができます。

```tsx
interface Dog {
	dogType: string;
	name: string;
	age: number;
}

interface Cat {
	catType: string;
	name: string;
	age: number;
}

const divideByType = (value: Dog | Cat) => {
	if ("dogType" in value) {
		return "Dog型です";
	} else {
		return "Cat型です";
	}
}

const test = {
	dogType: "チワワ",
	name: "jun",
	age: 2,
}
const result = divideByType(test); 
console.log(result);　// "Dog型です"と出力される
```

# おまけ：ジェネリクスを使ってみる

関数に値を渡す時点でその値の型がわかっているのであれば、引数の型をユニオン型にするのではなく、ジェネリクスを使う方法もあります。

```tsx
const divideByType = <T>(value: T) => {
	if (typeof value === "string") {
		return "string型です";
	} else {
		return "number型です";
	}
}

const result = divideByType<string>("文字列です");
console.log(result); // "string型です"と出力される
```

ただし、ジェネリクスを使うとvalueの型がなんでも許容されるため、関数`divideByType`内の条件分岐が複雑になってしまうことがあります。一長一短ですね。
[Calculation] 誤差

# 一覧
|誤差||
|:--|:--|
|丸め誤差|四捨五入、切り捨て、切り上げによって発生する誤差|
|情報落ち|非常に大きい数と小さい数の加減算によって小さい数値が計算結果に反映されない誤差|
|桁落ち|ほぼ等しい２つの数の差を求めたときに、有効桁数が大きく減る誤差|
|打切り誤差|計算処理を途中で打切ることによる誤差|


# 参考リンク
- [R3S2_桁落ち](https://www.ap-siken.com/kakomon/25_aki/q2.html)
OCIでVMシェイプ毎にCPUベンチ取ってみた

# はじめに

OCIのVMシェイプはいくつか選択することができますが、現時点ではStandard3(Intel CPU)かE4, E5(AMD CPU)が主流になっています。
以前利用していたStandard2やE3とどのくらいCPU性能が違うのか、sysbenchでデータを取ってみました。

https://docs.oracle.com/ja-jp/iaas/Content/Compute/References/computeshapes.htm#vm-standard

:::note warn
なお、とても単純な比較なので、実際に利用する際はそのワークロードでデータとってくださいね！
:::

# ベンチマーク値

CPUの情報は lscpu で取得しています。
sysbenchは単純に以下のオプションで実行しており、スレッドを 1 と 2 で変更しています。
（いずれも3回実行した平均値）

```SHELL:sysbench
$ sudo sysbench --test=cpu run
$ sudo sysbench --test=cpu run --threads=2
```

| |VM.Standard.E5.Flex  | VM.Standard.E4.Flex | VM.Standard.E3.Flex |VM.Standard3.Flex  | VM.Standard2.1 | 
|:-: |:-: |:-: |:-: |:-: | :-: | 
|OCPU数 |1 |1 |1 |1 |1  | 
|搭載メモリ(GB) |16 |16 |16 |16 |15  |
|CPUベンダ|AMD |AMD |AMD |Intel |Intel  | 
|CPUモデル|AMD EPYC 9J14 96-Core Processor |AMD EPYC 7J13 64-Core Processor|AMD EPYC 7742 64-Core Processor|Intel(R) Xeon(R) Platinum 8358 CPU @ 2.60GHz |Intel(R) Xeon(R) Platinum 8167M CPU @ 2.00GHz  |
|CPU MHz|2596.096|2445.404|2245.78|2593.968|1995.312  |
|処理イベント数|41,644 |36,299|16,645|27,715|7,572 |
|処理レイテンシ(ms)|0.24|0.28|0.61|0.36|1.32 |
|処理イベント数：threads=2|46,740|40,885|17,491|29,592|13,158|
|処理レイテンシ(ms)：threads=2|0.43|0.49|1.14|0.68|1.52 |
|価格(円)/1OCPU・16GBメモリ|9.61|7.595|7.595|9.92|9.889<br>(15GBメモリ) |

# まとめ

初期のモデル(E3, Standard2)に比べると直近のモデル(E4, E5, Standard3)は性能が向上していますね。
特にE5はStandard3に比べても 1.5倍 以上となっており、価格を考えても E4 か E5 を利用していく形がよさそうです。
あそび大全

## 問題

- サイコロ5つをふる、そのうち6が出なかった目を全て振り直す。さらにもう一度6が出なかった目を全て振り直す。この時、全ての目が6になる確率を求めたい

## 実装

```python
import math

y = 1/6
n = 5/6

def all_pattern():
    all_patterns = []
    for i in range(0,6):
        for j in range(0, i+1):
            all_patterns.append((i, j))
    return all_patterns

def calc_prob(num, i):
    """
    num: サイコロの個数
    i: 6が出る回数
    """
    return y**i * n**(num-i) * math.comb(num, i)

for i in range(1,6):
    for j in range(0, i+1):
        print(f"サイコロの個数: {i}, 6が出る回数: {j} = {calc_prob(i, j)}")

def calc_prob_reroll(pattern):
    # 1回目に指定した個数の6が出る確率
    num_six_1 = 5 - pattern[0]
    prob_1 = calc_prob(5, num_six_1)
    if pattern[0] == 0:
        return prob_1

    num_six_2 = pattern[0] - pattern[1]
    prob_2 = calc_prob(pattern[0], num_six_2)
    if pattern[1] == 0:
        return prob_1 * prob_2

    num_six_3 = pattern[1]
    prob_3 = calc_prob(pattern[1], num_six_3)
    return prob_1 * prob_2 * prob_3

for pattern in all_pattern():
    print(f"{pattern}: {calc_prob_reroll(pattern)}")

print("確率の合計: ", sum(calc_prob_reroll(pattern) for pattern in all_pattern()))
```

## 実行結果

```
(0, 0): 0.00012860082304526745
(1, 0): 0.0005358367626886143
(1, 1): 0.0004465306355738454
(2, 0): 0.0008930612711476908
(2, 1): 0.0014884354519128181
(2, 2): 0.0006201814382970076
(3, 0): 0.0007442177259564091
(3, 1): 0.001860544314891023
(3, 2): 0.0015504535957425194
(3, 3): 0.000430681554372922
(4, 0): 0.0003100907191485037
(4, 1): 0.0010336357304950123
(4, 2): 0.0012920446631187658
(4, 3): 0.0007178025906215365
(4, 4): 0.00014954220637948678
(5, 0): 5.1681786524750625e-05
(5, 1): 0.00021534077718646097
(5, 2): 0.00035890129531076836
(5, 3): 0.00029908441275897367
(5, 4): 0.000124618505316239
(5, 5): 2.0769750886039836e-05
確率の合計:  0.013272056011374656
```

## シミュレーションによる検算

- 疑似乱数を使って実際にたくさんサイコロを振れば、上記の計算が合っているかの検算ができる
- なんとこれはchatgptがやってくれた！
    - 書いてくれたコード
    
    ```py
    import numpy as np
    
    # サイコロを5つ振り、2回振り直すシミュレーション
    def roll_dice():
        # 初回のサイコロを振る
        dice = np.random.randint(1, 7, 5)
        
        # 6が出ていない目を振り直し（1回目）
        dice[dice != 6] = np.random.randint(1, 7, np.sum(dice != 6))
        
        # 再度、6が出ていない目を振り直し（2回目）
        dice[dice != 6] = np.random.randint(1, 7, np.sum(dice != 6))
        
        # 全て6になっているかどうかを確認
        return np.all(dice == 6)
    
    # シミュレーションを実行する回数
    n_trials = 1000000
    success_count = sum(roll_dice() for _ in range(n_trials))
    
    # 全てのサイコロが6になる確率を求める
    probability = success_count / n_trials
    probability
    ```
    
    - 実際答えが正しいことが確かめられた
        

## 数学的な計算をgptにさせられないか？

- 数学的に計算するんじゃなくて、シミュレーションで計算して確率を求める方向に（指示してないのに）もって言ってたのおもろい
- その後、数学的な計算も実装されるよう指示したところ、2回目の試行で一部のサイコロが6になる可能性を考慮できていなかった。
- しかし、そのことを指摘すると、正しいコードを実装して、数学的な計算で確率を求めることに成功していた！

- 会話のリンク
    - https://chatgpt.com/share/6709125c-a26c-8011-bb47-f43f01415bb2
    - chatはうまく表示されない場合がある。更新したりするといい。
投稿 103 TCO 2024/10/11

本日の勉強時間　3H

あるシステムの購入から廃棄までにかかる
支出、時間の合計額。

「Total Cost of Ownership」の略称で、企業がITシステムを導入する際に必要となる、ソフト・ハードの購入や維持管理にかかわるすべての費用の総額を指します。
Go言語(Golang)におけるスライスの 関数lenと関数cap 

Go言語におけるスライスの `len` と `cap` の違いについて説明します。

### `len` と `cap` の違い

- **`len`**: スライスの現在の要素数を返します。これは、スライスに格納されている実際のデータの数を示します。
- **`cap`**: スライスが持つことのできる最大の要素数を返します。これは、スライスが内部的に保持している配列の容量を示します。

### スライスの動作

スライスは、配列のラッパーとして機能し、動的にサイズを変更できます。スライスの容量は、要素を追加する際に増加することがあります。具体的には、スライスの容量は以下のように変化します。

  ```go
package main

import "fmt"

func main() {
	var x []int
	fmt.Println(x, len(x), cap(x)) // [] 0 0
	x = append(x, 10)
	fmt.Println(x, len(x), cap(x)) // [10] 1 1
	x = append(x, 20)
	fmt.Println(x, len(x), cap(x)) // [10 20] 2 2
	x = append(x, 30)
	fmt.Println(x, len(x), cap(x)) // [10 20 30] 3 4
	x = append(x, 40)
	fmt.Println(x, len(x), cap(x)) // [10 20 30 40] 4 4
	x = append(x, 50)
	fmt.Println(x, len(x), cap(x)) // [10 20 30 40 50] 5 8
}
   ```

1. **初期状態**: スライスが初期化されると、`len` と `cap` は両方とも `0` です。
   ```go
   x := []int{} // len(x) = 0, cap(x) = 0
   ```

2. **最初の要素の追加**: `append` を使って要素を追加すると、長さと容量が両方とも `1` になります。
   ```go
   x = append(x, 10) // len(x) = 1, cap(x) = 1
   ```

3. **2つ目の要素の追加**: 再度 `append` を使うと、長さは `2` になりますが、容量も `2` となります。これは、スライスの内部配列が新しい要素を格納するために十分な容量を持っているためです。
   ```go
   x = append(x, 20) // len(x) = 2, cap(x) = 2
   ```

4. **3つ目の要素の追加**: 3つ目の要素を追加すると、既存の容量を超えるため、新しい配列が作成され、容量が `4` になります。
   ```go
   x = append(x, 30) // len(x) = 3, cap(x) = 4
   ```

5. **4つ目の要素の追加**: 容量が十分なため、長さは `4` で、容量は `4` のままです。
   ```go
   x = append(x, 40) // len(x) = 4, cap(x) = 4
   ```

6. **5つ目の要素の追加**: またもや容量を超えるため、内部配列が再割り当てされ、容量が `8` に増加します。
   ```go
   x = append(x, 50) // len(x) = 5, cap(x) = 8
   ```

### まとめ

- スライスの `len` は現在の要素数、`cap` はスライスが保持できる最大の要素数を示します。
- `append` 操作を行う際に、現在の容量を超えると新しい配列が作成され、容量が増加します。この動的な管理により、Go言語のスライスは効率的にメモリを使用します。
M5Stapm S3でPWM(50Hz,0%...100%)で遊ぶ。(20ms)



M5Stapm S3でPWM(50Hz,0%...100%)で遊ぶ


Arduino PWM サーボモータ M5stack M5StampS3



結果

わからないと思うけど　いろいろ、準備するのがめんどい

![o_coq509.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/5ff79fe7-b1a1-74b7-78a5-b2c9b801ab3f.jpeg)


プログラム


```c++


//PWM_ledcWrite_test_50hz_StampS3_1

#define PWM_PIN 15
#define FREQUENCY 50
#define RESOLUTION 12

//初期化
void setup() {

  //出力ピンの初期化
  pinMode(PWM_PIN, OUTPUT);

  // PWM設定（ピン番号, 周波数, 分解能）
  ledcAttach(PWM_PIN, FREQUENCY, RESOLUTION);
  
} //setup


//メインループ
void loop() {

  //4096回繰り返す
  for (int i = 0; i < 4096; i++) {

    // DUTY比を指定して、チャンネルに対して実行
    ledcWrite(PWM_PIN, i);

    delay(5); //5ms待つ

  } //for i

} //loop


```
2024/10/11_本日の勉強

## 概要
本日の勉強内容をこちらに記載していきます。
ChatGPTから自動生成されたコードを読み解く前に基本的な部分を復習するためにProgateで少し演習課題を進めてから～という予定でしたが・・・:frowning2:

## 本日学んだこと
Progateで以下の講座をやりました。
HTML/CSS入門編1
JavaScript入門編1
大体～1時間で終わりました。

ただ・・・

今日はコードを読み解こうと思っていたのですが、面白いチャンネルを見つけてしまい、気づけば一日中動画を観ていました。

https://youtube.com/@ses_ch?si=KHH3Q35GKG-Yr4ZP

業界の裏話や開発のトレンド、成功者のインタビューなど、興味深い内容ばかりで時間があっという間に過ぎてしまいました。
たまにはこういう業界の動画を見る日があってもいいですよね？ね！？

## 纏め
今日は何も進捗がなかったので明日に持ち越しです。

## 参考
↓様々な方のインタビューなどもあり、見ごたえのある動画が多いのでお勧めです。
https://youtube.com/@ses_ch?si=KHH3Q35GKG-Yr4ZP
【超入門】Entra IDで実現するゼロトラストセキュリティの認証設定

#### 初めに
　クラウドでシステムを構成するにあたって、ゼロトラストセキュリティを組み込むことが一般的になってきました。その中でも、EntraIDの設定は非常に重要です。今回はそのEntraIDで構成していくゼロトラストセキュリティについて学んでいきます。

 ## EntraIDとは
 EntraIDとは、Azureにおけるアクセス管理やID管理を包括的に行う仕組みです。そのうち、今回はゼロトラストセキュリティとして以下2点の設定を組み込むことを推奨とします。
+ 多要素認証
+ リスクベース認証

 ### 多要素認証(MFA)
 　言わずもがな、アカウントを発行したら多要素認証の設定をONにするようにしましょう。多要素認証とは、「知識情報」「所持情報」「生体情報」の組み合わせで認証を行う認証方法です。例として、銀行ATMではキャッシュカード(所持情報)と認証パスワード(知識情報)の組み合わせがマッチした場合のみ、口座の利用ができるようになっております。
  　ここでは、多要素認証の中でも、「生体認証」を活用することを強くお勧めします。指紋認証であれば、他人需要率(誤って他人の指紋で認証してしまう確率)は10万分の1と言われています。標的型攻撃が大きなリスクとなってきている現代においては、この他人需要率であれば認証が正面から突破されることはほぼ100%ありえないでしょう。
　多要素認証の設定方法については、以下をご確認ください。
 [MS Learn | チュートリアル: Microsoft Entra 多要素認証を使用してユーザー サインイン イベントをセキュリティで保護する](https://learn.microsoft.com/ja-jp/entra/identity/authentication/tutorial-enable-azure-mfa)
 
### リスクベース認証
　ゼロトラストの考え方において、全てのアクティブなユーザーを疑うことになります。過去の信用や行動を分析して対象のユーザーが信頼できるユーザーかどうかを判断することが可能です。この理想論を実現するのがリスクベース認証です。リスクベース認証をONにしておくことで、「普段と違う場所からのログイン」や「匿名IPアドレスからのログイン」などを検知してアクセスを拒否することが可能です。
 　例えば、普段は日本国内からしかアクセスしていないユーザーが事前に何の報告もなく急に南米から通信してくれば、怪しいことは明白です。そんなリスクベースの設定方法は以下になります。
[MS Lern | ユーザー サインインのリスク検出を使用して、Microsoft Entra 多要素認証またはパスワード変更をトリガーする](https://learn.microsoft.com/ja-jp/entra/identity/authentication/tutorial-risk-based-sspr-mfa)

#### あとがき
ゼロトラストセキュリティを実現するためのAzureのサービスとして、IntuneやマネージドID、Application Proxyなどほかにも様々なものがあります。またおいおい書いていきます。
小学生の学習アプリ「ドリルクエスト」をリリースしました

「ドリルクエスト」は、小学校で勉強するような問題を、ゲームをしながら学習することができるアプリです。

https://apps.apple.com/us/app/%E3%83%89%E3%83%AA%E3%83%AB%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88-%E5%B0%8F%E5%AD%A6%E7%94%9F%E3%81%AE%E5%AD%A6%E7%BF%92%E3%82%A2%E3%83%97%E3%83%AA/id6711333088

# アプリ概要
問題の分野は「数学」「国語」「社会」「理科」に分かれており、それぞれ小学生の学年別レベルで出題されます。
また、クイズには「おとも」というキャラを連れていくことができ、モンスターを倒すサポートをしてくれます。
ガチャでアプリ内のコインを使えば、新しいおともを仲間にすることができます。

# クイズ機能
クイズを解いてモンスターを倒すときにサポートしてくれる「おとも」というキャラを選ぶことができます。体力と攻撃力はおともの種類で様々です。選んだクイズによって出題される問題の難易度が変わります。
正解するとモンスターにダメージを与えて、不正解だと自分がダメージを受けます。
問題の難易度によって、ゲームクリア時の獲得できる経験値とコインが変わります。

![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597913/36047501-e667-9511-d5af-fb58a4188dba.png)

# おとも機能
クイズを解いてモンスターを倒すときにサポートしてくれる「おとも」というキャラを選ぶことができます。
体力と攻撃力はおともの種類で様々です。

# ガチャ機能
ガチャを利用しておともをゲットすることができます。
レア度は「ウルトラレア」「スーパーレア」「レア」「ノーマル」と分かれており、レアレリティが高いものほど出現率が低いです。

![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597913/b3ad9e44-94e5-3207-5745-897ca52fcb43.png)
M5Stapm S3でPWM(1kHz,0%...100%)で遊ぶ。


M5Stapm S3でPWM(1kHz,0%...100%)で遊ぶ。


Arduino PWM サーボモータ M5stack



x 過去ログを見よ!!


結果

わからないと思うけど　いろいろ、準備するのがめんどい

![o_coq508.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/70ffe106-cc07-9acf-c45c-d102820f59d3.jpeg)


プログラム


```c++


//PWM_ledcWrite_test_1khz_StampS3_1

#define PWM_PIN 15
#define FREQUENCY 1000
#define RESOLUTION 8

//初期化
void setup() {

  //出力ピンの初期化
  pinMode(PWM_PIN, OUTPUT);

  // PWM設定（ピン番号, 周波数, 分解能）
  ledcAttach(PWM_PIN, FREQUENCY, RESOLUTION);
  
} //setup


//メインループ
void loop() {

  //256回繰り返す
  for (int i = 0; i < 256; i++) {

    // DUTY比を指定して、チャンネルに対して実行
    ledcWrite(PWM_PIN, i);

    delay(20); //20ms待つ

  } //for i

} //loop



```
期待値と平均値の違いをあなたはすぐに答えられますか？？

# 期待値と平均値
統計学を勉強しているときに、出てくる期待値と平均値。
この二つの違いがあなたはすぐに答えられますか？
今からサイコロを使った例で簡単に考えていきましょう。

## 普通のサイコロ
一様で確かであるサイコロを１回振るとき、以下の確率でその値が出ると仮定します。

| 値       | 確率             |
|------------|----------------------|
| 1      | 1/6|
| 2     | 1/6|
| 3      | 1/6|
| 4      | 1/6|
| 5     | 1/6|
| 6      | 1/6|

学生時代に平均は以下のような式で習いませんでしたか？
<br>

$$
\frac{1+2+3+4+5+6}{6} = 3.5
$$
正解です。

そして、

期待値は以下の定義で計算されるため、

$$
{値}×{確率} の合計
$$

サイコロの期待値は

$$
1×\frac{1}{6}+2×\frac{1}{6}+3×\frac{1}{6}+4×\frac{1}{6}+5×\frac{1}{6}+6×\frac{1}{6}=3.5
$$

まったく同じ値になりましたね。

## いかさまサイコロ
形を細工し、サイコロを１回振るとき、以下の確率でその値が出ると仮定します。

| 値       | 確率             |
|------------|----------------------|
| 1      | 1/2|
| 2     | 0|
| 3      | 0|
| 4      | 1/6|
| 5     | 0|
| 6      | 1/3|

ん？？？
この平均の求め方ってなんだ？？？
ってなりませんか？？？

この場合、平均という値は出ません。
しかし、期待値あります。
サイコロの期待値は

$$
1×\frac{1}{2}+4×\frac{1}{6}+6×\frac{1}{3} = 3.167...
$$

## じゃあ平均値っていったい何だったんだ

> 確率が全て同じ時の期待値の計算だったんだ。下の表みたいにね。

| 値       | 確率             |
|------------|----------------------|
| 1      | 1/6|
| 2     | 1/6|
| 3      | 1/6|
| 4      | 1/6|
| 5     | 1/6|
| 6      | 1/6|
OpenStreetMapのエリアをWeb上で分割する方法

JOSMというソフトウェアによってエリアを分割する方法が紹介されていますが、アプリケーションを別途起動したりマップデータを落としてきたりと少々手間です。そこで、今回は `https://www.openstreetmap.org/edit` からエリアを分割する方法を照会します。

https://qiita.com/kochizufan/items/fe3c5163e6c0f4d2d1ed

では手順です。

1. エリアの枠上に分割するときの分界点を2つ作っておく
1. シフトを押しながら左クリックでエリアと分界点2つを選択
1. エリアの枠上で右クリック
1. ハサミのマークの「分割」を選択
1. ラインの選択を外す
1. 再度右クリック
1. 「接続解除」を選択
1. 分界点で別れているはずなので、閉じるようなラインを引く
1. 元々の方のラインのリレーションを解除
1. 8と9のラインを選択
1. 右クリック
1. 結合
1. 左のパネルで`area=yes`を入力
1. 9にあったリレーションを入力
1. 残った片割れについても同様にやる
【Swift入門】画像データをUIImageやNSImageで扱う際の注意点：生データからの変換とその影響

Swiftでのアプリ開発において、画像データの処理は避けて通れません。特に、PNGやJPEGなどの画像フォーマットを扱う際には、生データをUIImageやNSImageに変換することが多いですが、このプロセスにはいくつかの注意点があります。この記事では、画像の生データがUIImageやNSImageに変換される過程で発生する変更点や、その影響について詳しく解説します。

# PNGデータの特徴
まずは、PNG（Portable Network Graphics）形式の基本的な特徴をおさらいします。PNGはロスレス圧縮を使用しているため、画像を圧縮しても品質が損なわれません。この特性は、特にアプリのアイコンや背景画像を扱う際に大変便利です。

たとえば、アプリのアイコンには透明な部分が必要なことがありますが、PNG形式を使用することでその透明度を保つことができます。これがPNGの強みです。また、PNG画像は一般的にJPEGよりもファイルサイズが大きくなりがちですが、きちんと最適化することで、アプリのパフォーマンスを損なうことなく使用することができます。

# SwiftでのPNGデータの処理方法
次に、Swiftを使ってPNGデータを扱う方法について見ていきましょう。Swiftでは、UIImageクラスを使ってPNG画像を読み込むことが簡単にできます。以下のようなコードで、PNGファイルをアプリに組み込むことができます。

```swift
if let image = UIImage(named: "example.png") {
    imageView.image = image
}
```

このコードでは、UIImage(named:)メソッドを使用してプロジェクト内のPNGファイルを読み込み、imageViewに表示しています。このように、Swiftを使うことで、画像を簡単に扱うことができます。

さらに、Swiftでは画像を加工することも可能です。たとえば、PNG画像のサイズを変更したり、色を変えたりすることができます。以下のコードで画像をリサイズすることができます。

```swift
let resizedImage = image.resized(to: CGSize(width: 100, height: 100))
```

このように、アプリに合わせて画像を調整することで、より洗練されたデザインを実現できます。

### 生データの取得と変換
ここで、少し複雑な話に移りますが、PNG画像の生データを取得して、UIImageに変換するプロセスについて触れてみましょう。以下のように、Webから直接PNGデータを取得することができます。

```swift
let data = try! Data(contentsOf: URL(string: "https://example.com/image.png")!)
let uiImage = UIImage(data: data)!
```

このコードで、指定したURLからPNGデータを取得し、UIImageに変換しています。しかし、ここで注意が必要です。取得した生データとUIImageから再生成したデータが必ずしも同じになるわけではないのです。以下のコードを見てみましょう。

```swift
let data2 = uiImage.pngData()!
print(data == data2) // false
print(data, data2)   // 503 bytes 1134 bytes
```

この結果からわかるように、元のデータと再生成されたデータのサイズが異なります。なぜこのようなことが起こるのか、考えたことはありますか？

実は、UIImageに変換する過程で、画像のメタデータやエンコーディングが変わってしまうことがあるんです。このことを知っておくことで、画像データを扱う際のトラブルを未然に防げます。

### 画像の最適化
アプリのパフォーマンスを向上させるためには、画像の最適化が欠かせません。特にPNG形式は、適切に扱わないとファイルサイズが大きくなりがちです。以下に、PNG画像を最適化するためのいくつかの方法を紹介します。

### 圧縮ツールを使う
PNG画像を圧縮するために、TinyPNGやImageOptimなどのオンラインツールを利用するのが効果的です。これらのツールを使えば、画質をほとんど損なうことなくファイルサイズを小さくできます。

### メタデータの削除
PNG画像には、作成者や作成日時などのメタデータが含まれていることがあります。これらのデータは、表示には影響しませんが、ファイルサイズを大きくする原因になります。画像編集ソフトを使って、これらのメタデータを削除することをおすすめします。

### 画像サイズの見直し
アプリに必要な画像サイズを見直してみましょう。必要以上に大きな画像を使っていると、その分ファイルサイズも大きくなります。使用するデバイスの画面サイズに合わせて画像をリサイズすることで、無駄を省くことができます。

### アセットカタログを活用する
Xcodeのアセットカタログを活用することで、異なる解像度の画像を管理することができます。1つのPNG画像を異なるサイズで用意することで、デバイスの解像度に応じて最適な画像が表示されます。

これらのテクニックを活用することで、PNG画像の最適化が進み、アプリのパフォーマンスを向上させることができます。

# さらなる学びを得るために
ここまでPNGデータの特徴やSwiftでの処理方法、画像の最適化についてお話ししてきましたが、もっと具体的な実践例が知りたいという方も多いと思います。そんなあなたにぴったりの記事があります！ぜひこちらをチェックしてみてください。

https://android.benigumo.com/20241011/data-pngdata/

この記事では、PNGデータの特性やSwiftでの具体的なコーディング例、画像の最適化方法について詳しく解説されています。実際のコードや手法を学ぶことで、あなたのアプリ開発に役立つ情報が満載です。

# まとめ
PNGデータの扱いは、アプリ開発において避けては通れない道です。Swiftを使えば、PNG画像を簡単に扱うことができ、さらに画像の最適化を行うことでアプリのパフォーマンスを向上させることができます。これからも、Swiftを駆使してPNGデータをうまく活用し、魅力的なアプリを作っていきましょう！

https://x.com/maochanz/status/1844701830036783442
mysql 8.0.x のsql_mode

### 訳あってMySQLのsql_mode を変更したい場合

#### デフォルトの確認
~~~
SELECT @@GLOBAL.sql_mode;
~~~
~~~
+-----------------------------------------------------------------------------------------------------------------------+
| @@GLOBAL.sql_mode                                                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------------------------------------------------------+
~~~



#### 変更方法
my.cnf
~~~
[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
~~~
※ONLY_FULL_GROUP_BYを外したい場合

#### 変更結果確認
~~~
SELECT @@GLOBAL.sql_mode;
~~~
~~~
+----------------------------------------------------------------------------------------------------+
| @@GLOBAL.sql_mode                                                                                  |
+----------------------------------------------------------------------------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+----------------------------------------------------------------------------------------------------+
~~~
Vercel Cronの設定を記述したvercel.jsonのマージに失敗する問題と解決策

Vercel Cron jobs：指定した時間や間隔で、サーバーレス関数を自動的に実行できる機能。

# 問題：vercel.jsonのマージで"Deployment failed"が発生する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840466/57e41b7f-fb17-403c-91a0-b253988ed9f3.png)

テストのため、1時間おきに実行できるようなテキストを作成し、
ドキュメントで誤りがないかを確認。

<br>

``` vercel.json
{
  "crons": [{
      "path": "/api/auto-shift/run",
      "schedule": "0 * * * *"
    }]
}
```
スケジュールを記述した設定ファイルvercel.jsonを追加する

<br>


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840466/db0d5a46-9138-8cb9-f46f-b8af2d79697b.png)

ブランチをマージしようとすると、Deployment failedとなる。
詳細が表示されないため、原因の特定に時間を要しました。

何が原因だったのでしょうか？


# 原因：無料プランでは1日2回を超えるような"設定"もできない
``` pwsh
> vercel deploy --prod 
Error: Hobby accounts are limited to daily cron jobs.
This cron expression (0 * * * *) would run more than once per day.
Upgrade to pro to unlock all Cron Jobs features on Vercel. 
```

無料(Hobby)プランでは1日に2回までしかCronを**実行**できないことは明示されていますが、
1日2回以上実行する**設定** すらできないことは記述がありませんでした。



コマンドにてデプロイを試みたところ、エラー詳細が判明したため解決しましたが、
ドキュメントなどにも詳しい記述がないため、少し厄介な問題です。

解決策はシンプルです。

# 解決策：1日2回以内の実行へ変更
```
{
  "crons": [{
      "path": "/api/auto-shift/run",
      "schedule": "0 1 * * *"
    }]
}
```
実行回数を1日2回以内になるように設定することで、

<br>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3840466/6d3464a8-c27d-6309-e818-6f564488097a.png)

デプロイを行うことができます。


# 参考
Cron jobsについて

https://vercel.com/docs/cron-jobs

プランごとの制限について

https://vercel.com/docs/cron-jobs/usage-and-pricing
Amazon SageMaker Autopilotの利用料金について

# はじめに
初めまして、2024年現在は都内の大学院にて学生をしているhimaです。
普段は大学院で機械学習とかピープルアナリティクスとかデータを活用することをテーマに色々勉強しています。
その傍でデータアナリストとしてインターンをしており、AWSのAutoMLサービスであるAutopilotを触っています。
今回は自分も困ったところで、実際に触ってみないとわからない人も多いんじゃないかと思い、このテーマを執筆しています。

*AWS SageMakerやAutopilotの機能についての説明はしません。以下の記事等を参照ください。

https://aws.amazon.com/jp/sagemaker/

## 本記事の対象者
- すでにある程度Autopilotについて知っている
- Autopilotを触ってみたいが料金がどれくらいかかるのか不安なため、手をつけられない
- 費用の試算がしたい

## Autopilotとは
Autopilotについての解説記事はたくさんqitta上にも載っているため、いくつかよさそうなものをピックアップしておきます。

↓使い方などがまとまっており、非常に助かりました

https://qiita.com/ishidahra/items/cffe7484102dac9997da

https://qiita.com/mtakehara21/items/b7c16a195d29ede7c46a

# 本題の料金について
私の場合は、インターネットを検索しても[公式のサイト](https://aws.amazon.com/jp/sagemaker/pricing/?nc=sn&loc=4)やドキュメントに「従量課金制です」と書かれているくらいしかみたことがないまま、触っていました。

ですが、Autopilotのモデルを10MB〜40MBくらいのデータを用いて何回かぶん回していると、一日あたり数十ドルの請求が来ることもあるようです、、

### 料金のかかり方

以下のように変数を定義すると、
- ジョブ数 : $ N $
- 一つの処理ジョブにかかる時間 : $ x_n $
- インスタンス利用料金 : $ m $

↓こんな感じで表すことができます↓

```math
\sum_{k=1}^N x_n m
```

要は、「モデルを作成した際に同時に進行する処理ジョブの一つ一つに対して料金がかかる」ということになります。

例えば、`Taitanic`というモデルを作成した際に同時に`10個`のモデルが作成されます。
それぞれのモデルを処理するジョブに対して、`15分`ずつかかったとします。
この処理ジョブに利用されるインスタンスタイプの利用料金が`$4 / 時間`だった時の料金は、`$10`になります。
``` math
$10=10ジョブ*15/60*$4/時間
```

# 終わりに
今回ご紹介したのはあくまでも自分が遭遇した問題の答えでしたので、読み手の皆さんそれぞれの環境ではまた異なるかもしれません。
ただ、Autopilotはインスタンスの利用以外に大きく請求に影響のある因子はなかったと思いますので、何のインスタンスをどれくらい使っているのかを意識しておけば、ある程度予測はつくのではないでしょうか？

ここまで読んでいただきありがとうございました。
[Test] ホワイトボックステスト

# 一覧
|テストパターン||
|:--|:--|
|命令網羅|全ての命令を少なくとも1回実行|
|判定条件網羅|判定条件が真になる場合と偽になる場合を実行|
|条件網羅|判定条件を構成する条件が真になる場合と偽になる場合を実行<br>判定条件の真偽両面のテストはしなくて良い|
|判定条件/条件網羅||
|複数条件網羅||



# 参考リンク
- [R3S48_命令網羅](https://www.ap-siken.com/kakomon/22_haru/q48.html)
- [R4S47_判定条件網羅](https://www.ap-siken.com/kakomon/04_haru/q47.html)
人体の肩・上腕・前腕のRigging

## 前提条件
- IKが何かわかっている
- 座標系・回転について理解ができている（基本的にローカル座標系で話します）
- blenderのconstraint・driverの設定ができる

## IK or FK
昔は腕はFK、脚はIKと主張する方々が多かったような気がします。私はどちらもIKの方が良いと思います。

FKの良さ
- ドライバーが簡単に仕込める
- 動きの制限をかけやすい
- 微調整が容易

IKの良さ
- 直感的に目的のポーズを達成できる

今回は私の腕周りのIK設定を共有しますよ。

## デフォルトポーズ
Tポーズ状態を想定しています。直立状態から腕を横にまっすぐ伸ばし、手のひらは下方向を向いています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/3077fc81-179c-7b0f-3a29-3671db1e71fb.png)

## 基本のIK
簡単です。
- 上腕 > 前腕を配置
- 前腕のtailにIKコントローラーを配置
- 前腕にchain length2のIKを設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/9bf1d90f-875a-6a92-e46c-3afb7051733d.png)


## 前腕
前腕の設定についてはよほど適当な方か初心者でない限り同じようになると思います。

前腕には実際大きな骨が2本ねじるように配置されています。肘は自由度1、手首は自由度3ですが、前腕に作用するのはねじる回転のみなので前腕の自由度は2です。肘の回転は前腕全体（さらに手）に作用しますが、手首の回転は手首から肘にかけて影響が小さくなります。

IKにはZ軸方向のみ回転ができるようにリミットをかけます。これで基準の前腕のボーンは肘の動きのみの回転を持ちます。ねじる操作はお好みですが、IKのコントローラーのY軸をコピーするようにします。

### 分割したボーンを配置
Y軸（ねじる）方向の回転は手首周りが一番影響が大きく、肘に近づくにつれ小さくなります。これを再現するために前腕のボーン上に分割したボーンを配置します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/e67bcd76-6cfa-b78b-cbcd-3c6096f239f3.png)

これらのボーンは前腕のボーンを親に持つようにするので前腕と同じ回転をします。そこにさらにコントローラーのY軸方向の回転をCopy RotationのAddで足されるように設定します。Influenceを手首は1で肘に近づくにつれ0になるように設定すれば完成です。

## 上腕

上腕はかなりトリッキーです。肩回りは自由度3なのでどの方向にも動きます。そして同じくY軸の回転は肘周りが一番影響が大きく、肩に近づくにつれ小さくなります。しかし上腕にY軸の制限をかけると肘の動きが制限されてしまうため不可能です。

### ねじれのあるボーンとないボーンを用意する
ねじれのあるボーンは通常のIKで使用するそのままのボーンです。IKによってねじれを含めて計算されます。
ねじれのないボーンは元のボーンがXZ軸回転だけでどのように最終的なポーズになるかを示します。これにもう一つ前腕をターゲットとしたLength 1のIKを使います。そのボーンにY軸制限をかければXZ軸回転のみだとどのように動くべきなのかがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/148ec22e-a8b7-f87f-fca4-e9eb18174027.png)

ここまでくればあとは前腕と同じです。分割したボーンを配置して肘周りにはねじれあり、肩回りにはねじれ無しのポーズを適用させます。分割ボーンはねじれ有り無しどちらのボーンを親にしてもよいですが、ツリーが簡潔になるようにありを親にします。ねじれ無しの回転をWorld SpaceのCopy RotationでReplaceさせ、Influenceを設定したら完成です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/3af19f86-e8d6-a67a-03c1-a53e8877e467.png)
1000個のjsonファイルをSnowflakeのテーブルにロードした備忘録

# これはなに
手元に1000個のJSONファイルがありました。ファイルサイズは全部で16GB。これを効率的に分析したくてSnowflakeに突っ込んでみた記録です。

実行環境はMacOS 14.6.1です。

# おおまかな手順

1. snowsqlをインストールする
1. snowsqlでログイン
1. ステージを作る
1. ステージにファイルをアップロードする
1. ステージ上のファイルからテーブルにデータを読み込む

webuiでもデータのアップロードは可能ですが一度に250MBまでしかできません。全部で16GBありますので50回以上手動のアップロードを行うのはミスもありそうですし避けたいです。そのためsnowsqlを使うことにしました。


snowsqlを使う場合、いきなりjsonファイルからテーブルを作ることはできなかったのでステージにjsonファイルを全部アップロードして、ステージ上のjsonファイルからテーブルを作っていきます。

# 1.snowsqlをインストールする

インストール手順は以下に記載があります。

https://docs.snowflake.com/ja/user-guide/snowsql-install-config#installing-snowsql-on-macos-using-homebrew-cask

やり方は超簡単。まずはbrewでsnowsqlをインストール。

```sh
$ brew install --cask snowflake-snowsql
```

次にaliasを設定。これで終わり

```sh
alias snowsql=/Applications/SnowSQL.app/Contents/MacOS/snowsql
```

# 2.snowsqlでログイン

snowsqlでログインしていきましょう。

```sh
snowsql -a {アカウント名} -u {ユーザー名} -d {データベース名} -s {スキーマ名}
```

ステージはスキーマないに作成されるので作成するテーブルと同じ場所を指定しました。

# 3.ステージを作る

以下から記載しているコマンドはsnowsqlのコマンドラインで発行するものです。

以下のクエリでステージを作ります。

```sh
CREATE OR REPLACE STAGE my_internal_stage;
```

# 4.ステージにファイルをアップロードする

ステージができたらPUTでローカルファイルをアップロードします。

```sh
PUT file://{ファイルパス}/*.jsonl @my_internal_stage;
```

もっとかかるかと思ったけど5分くらいで終わりました。

# 5.ステージ上のファイルからテーブルにデータを読み込む

Tableの定義を作るために一度WEBUIで一つだけjsonファイルを読み込んでテーブルを作りました。テーブル名は`table_from_json`として説明を続けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200471/3dfb1571-0a15-827c-921f-8ebcc116146f.png)

その後、テーブルの中身をすべてDELETEして、COPY INTOでデータを読み込みました。

```SQL
//すべてのレコードを削除
DELETE FROM table_from_json;

//jsonを読み込むためのformatを定義
CREATE OR REPLACE FILE FORMAT my_json_format
  TYPE = 'JSON'
  COMPRESSION = 'AUTO';

//my_internal_logにあるjsonファイルを読み込み
COPY INTO table_from_json
FROM @my_internal_stage
FILE_FORMAT = (FORMAT_NAME = 'my_json_format')
MATCH_BY_COLUMN_NAME = CASE_INSENSITIVE;
```

以上で無事に1000個のjsonファイルから1つのテーブルを作ることができました。めでたしめでたし。
OAuth2-proxy と Keycloak でどんなサイトにも認証をかけられるぞ


# Overview
oauth2-proxyはただのproxyなのでどんなサービスの前にもおくことができます。[*1] サイトのソースを一切いじらずに OIDC 認証をかけられる便利なやつです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/2653de75-ba38-1781-3a26-b5e1f8b99f47.png)

oauth2-proxyはTLS termination必須なので、前段のreverse proxyでTLS offloadするか、oauth2-proxyに証明書を渡してTLS terminationする必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/7f96171e-6ac9-b871-1468-e5c7df3b682d.png)


# 実装例
この例ではdocker で2つのコンテナを起動しています。oauth2-proxyとweb serviceです。OIDCは外部にある前提です。前段のreverse proxyでTLS terminationされており、oauth2-proxy コンテナには http 通信が来ている想定です。

github

> https://github.com/kujiy/oauth2-proxy-playground

```yml:docker-compose.yml
version: '3.8'

services:
  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    ports:
      - "4180:4180"
    command:
      - --provider=keycloak-oidc
      - --oidc-issuer-url=https://keycloak.example.com/realms/master
      - --client-id=my-client
      - --client-secret=${CLIENT_SECRET}
      - --cookie-secret=${COOKIE_SECRET}
      - --redirect-url=https://service.example.com/oauth2/callback
      - --upstream=http://nginx:80
      - --email-domain=*
      - --errors-to-info-log

  nginx:
    image: nginx:latest
```

```rb:.env
CLIENT_SECRET=keycloakから発行する
COOKIE_SECRET=任意の 32 文字
```

Note: upstream server(実際にweb serviceが稼働してるendpoint) は docker networkで oauth2-proxy container -> nginx へのルーティングがされているのでこういう書き方になってます。 (docker-composeのサービス名で名前解決できる） 実際の利用では外部のドメインなどになることでしょう
```rb
      - --upstream=http://nginx:80
```

# oauth2-proxy + keycloak の罠
## Keycloak は Audience を追加しなければいけない 
1つ大きな引っ掛けがありました。こんなエラーが出ました

> oauth2-proxy-oauth2-proxy-1  | [2024/10/11 09:11:38] [oauthproxy.go:902] Error creating session during OAuth2 callback: audience from claim aud with value [...] does not match with any of allowed audiences map[<cliend-id>:{}]

これは keycloak が default で access tokenの `aud` に client-id を入れないのが原因らしいです。それが入ってるのが一般的だと chatGPT はいう。そうなんだ。

keycloak管理画面の

> Client scopes > {client_id}-dedicated > Add mapper > By configuration > audience 

 これで一つ audience を作ります。`Name` は任意ですが `Included Client Audience` または `Included Custom Audience` に oauth2-proxy に渡している `--client-id` と同じ文字列が入る必要があります。ymlの例だと `my-client` が入るべきです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/18029aa5-2fad-941a-d389-1c4b817782e6.png)

これで、無事 service.example.com にアクセスすると keycloak 認証を求められ、認証成功すると web serviceが表示されるようになりました。認証session は cookie 保存されるので reload するとストレートにweb serviceが見えます。便利。

ちなみに login後の cookie には  `_oauth2_proxy_0` `_oauth2_proxy_1` という値がありました。local storage, session storage は空でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/677cf5f5-6ec5-ac0f-22cc-d39058b45f72.png)


終わり！！　簡単だったぜ！


*1 oauth2-proxy は `/oauth2` というpathを利用するのでサービス側はこのpathを使わないほうがいいです


# keycloak以外との連携
一般的なOIDCとの連携もできました
https://github.com/kujiy/oauth2-proxy-playground/tree/master
C#でExcelのセルのマージとアンマージ

セルのマージは、同じ行または列にある2つ以上のセルを1つのセルにマージする操作である。複数の列や行にまたがってヘッダーを適用する必要がある場合によく使われます。
本記事では、C#でExcelファイル内のセルをマージしたりアンマージしたりする方法を、無料のライブラリを使って紹介する。


## 無料の.NET Excelライブラリをインストールする
[**Free Spire.XLS for .NET**](https://www.e-iceblue.com/Introduce/free-xls-component.html)ライブラリをインストールするには2つの方法があります。1つは[**Nuget**](http://www.nuget.org/packages/FreeSpire.XLS/	)経由でインストールする方法、もう1つは下記のリンクから製品パッケージをダウンロードし、ローカルパスから手動でプロジェクトにdllを追加する方法です。

https://www.e-iceblue.com/Download/download-excel-for-net-free.html

## C#でExcelのセルをマージする
無料のAPIを使用すると、特定のセル範囲にアクセスし、**`XlsRange.Merge()`** メソッドを使用してそれらを1つにマージすることができます。

> **C#のサンプルコードです：**
```csharp
using Spire.Xls;

namespace MergeCells
{
    class Program
    {
        static void Main(string[] args)
        {
            // Excelファイルを読み込む
            Workbook workbook = new Workbook();
            workbook.LoadFromFile("Sample.xlsx");

            //最初のワークシートを取得する
            Worksheet sheet = workbook.Worksheets[0];

            //セルA1:F1を1つのセルにマージする
            CellRange range = sheet.Range["A1:F1"];
            range.Merge();

            //結合されたセルのテキストを中央に配置する            range.Style.HorizontalAlignment = HorizontalAlignType.Center;

            //結果ファイルの保存
            workbook.SaveToFile("MergeCells.xlsx", ExcelVersion.Version2016);
        }
    }
}
```

![MergeExcelCells.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811193/f82abdfe-0e89-1281-14b7-fd2278fd5a9f.png)


## C#でExcelのセルの結合を解除する

Excelで指定したセルの結合を解除するには、**`CellRange.UnMerge()`** を使用します。

>**C#のサンプルコードです：**
```csharp
using Spire.Xls;

namespace UnmergeCells
{
    class Program
    {
        static void Main(string[] args)
        {

            //Excelファイルを読み込む
            Workbook workbook = new Workbook();
            workbook.LoadFromFile("MergeCells.xlsx");

            //最初のワークシートを取得する
            Worksheet sheet = workbook.Worksheets[0];

            //セルA1:F1のアンマージ
            CellRange range = sheet.Range["A1:F1"];
            range.UnMerge();

            //結果ファイルの保存
            workbook.SaveToFile("UnMergeCells.xlsx", ExcelVersion.Version2013);
        }
    }
}
```
* * *

**C#によるエクセル処理機能をもっと知りたい方は、こちらをご覧ください：**

https://www.e-iceblue.com/Tutorials/Spire.XLS/Spire.XLS-Program-Guide/Spire.XLS-Program-Guide-Content.html
UE5のプロジェクトをGithubで管理する方法

# この記事について
UE5のプロジェクトをGithubで管理したいが、プロジェクトの容量が大きすぎてpush出来ない。
.gitignoreを導入することによって、この問題を解決する。

# 環境
Unreal Engine 5.4.4

# リポジトリを作成する際に
作成する際の設定画面で.gitignoreを追加しましょう。
![unreal engine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513172/8c6f55c3-3475-3960-8e9f-e96dd3c5407c.png)

# 一度リポジトリに存在する.gitignoreをpullする
まずは初期化
```shell:shell
$ git init
```
次にリモートリポジトリを追加。

```shell:shell
$ git remote add origin リポジトリのhttpsアドレス
```
リポジトリから.gitignoreをPullする。
```shell:shell
$ git pull origin main
```
以下いつものgitの操作。commitに少しだけ時間がかかるはず。
```shell:shell
$ git add .
$ git commit -m "hoge"
$ git push -u origin main
```
# P.S
別のプロジェクトをpushした後、別のファイルでリポジトリをpullするとなぜか以下のエラーが発生。
```shell:shell
ERROR: Repository not found.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```
プロジェクトに発生していた.gitファイルをゴミ箱に送って`git init`からやり直すと上手く行った。
![Screenshot 2024-10-11 184113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513172/2b81d130-72ec-9478-0d0e-d75dec42688a.png)