OmniAICreator/Qiita-1.07M huggingface dataset:
Go言語の「go pprof」

## 1. go pprofとは？
**go pprof**は、Go言語のツールチェーンの一部で、プログラムのパフォーマンスに関するさまざまな問題を迅速に特定するために使用されます。特に以下のような分析が可能です：

- **CPU消費量の分析**
- **メモリ割り当ての分析**
- **ブロッキング（待機時間）の分析**

これらの分析を通じて、プログラムのボトルネックを特定し、最適化することができます。

---

## 2. runtime.pprofパッケージを使った基本的な分析方法
パフォーマンス分析を行うには、まず**runtime.pprof**パッケージを使ってプログラム内にコードを埋め込む必要があります。以下の手順で進めます。

### 2.1 サンプリングの設定
runtime.pprof パッケージは、プログラムの実行時に1秒あたり100回のサンプリングを行います。これにより、プログラムのどの部分がリソースを多く消費しているのかを正確に把握できます。

### 2.2 コードの埋め込み例
```go
package main

import (
    "fmt"
    "os"
    "runtime/pprof"
)

func main() {
    f, err := os.Create("cpu.prof")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer f.Close()

    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()

    // ここに対象の処理を記述
    sampleFunction()
}

func sampleFunction() {
    for i := 0; i < 100000000; i++ {
        _ = i * i
    }
}
```

---

## 3. go pprofコマンドの実行方法
コードに runtime.pprof パッケージを埋め込んだら、次は**go pprof コマンド**を使ってプロファイルデータを解析します。

### 3.1 プロファイルデータの生成
以下のコマンドを使って、生成したプロファイルデータを分析します：
```sh
go tool pprof cpu.prof
```

### 3.2 インタラクティブモードでの分析
コマンド実行後、インタラクティブモードに入ります。以下のコマンドが利用可能です：

- **top**：CPU使用率の高い関数を表示
- **list [関数名]**：特定の関数の詳細を表示
- **web**：結果をGraphvizを使って可視化

---

## 4. よくある課題と対策
### 課題1：プロファイルデータが大きすぎる
**対策**：対象のコード範囲を絞る、サンプリング時間を調整する

### 課題2：Graphvizが正しく動作しない
**対策**：Graphvizのインストールを確認し、パスが通っているかチェックする

---

## 5. まとめ
go pprof は、Go言語で効率的なパフォーマンス分析を行うための強力なツールです。本記事で紹介した方法を使えば、CPU使用率やメモリ割り当てのボトルネックを簡単に特定し、プログラムのパフォーマンスを大幅に向上させることができます。

Go言語の開発において、性能改善は避けて通れない課題です。**it-kiso.com**では、これからもGo言語に関する実践的な記事を公開していきますので、ぜひご期待ください！

---

**参考記事**

https://it-kiso.com/programming-language/golang/go-build/go-pprof%e3%82%b3%e3%83%9e%e3%83%b3%e3%83%89go%e8%a8%80%e8%aa%9e%e6%80%a7%e8%83%bd%e8%a7%a3%e6%9e%90%e3%82%b3%e3%83%9e%e3%83%b3%e3%83%89%e5%ae%8c%e5%85%a8%e6%94%bb%e7%95%a5/
KDocのアノテーションメモ

# はじめに
今回はAndroid実装でよく使うKDocを書くときに利用するアノテーションに関することを紹介していきます
### 本文
よくあるのが関数やSealedClassにKDocを書くときに引数の説明のためにアノテーションを使うことだと思います。
そこで@Propertyと@Paramの違いを紹介していきます
プロパティはDataクラスやEnumに持っているオブジェクトでSealedClassなどのKDocに使うのが適してます。
パラメータは仮引数でこちらは関数にたいしてのKDocに使うのが最適です
補足としてですが、たまにパラメータとオーグメントが認識として混在している人がいますがオーグメントは実引数なので注意が必要です
### さいごに
備忘録としての役割が強いですがどなたかのお役に立てれば幸いです
convert_hashes_to_parameters(key, value)

# Parametersクラスにして格納させる
```rb
      # ハッシュをparametersに変換する
      def convert_hashes_to_parameters(key, value)
        converted = convert_value_to_parameters(value)
        @parameters[key] = converted unless converted.equal?(value) # 同一のオブジェクトであるか実施されず、parametersクラスであれば実施される
        converted
      end
```
https://github.com/rails/rails/blob/6df15e25c875e9c1ee39acb0bc161b6c47531331/actionpack/lib/action_controller/metal/strong_parameters.rb#L1172C5-L1177C1
#### convert_value_to_parameters(value)　　Parametersクラスでなければそれになって返ってくる
https://qiita.com/masatom86650860/items/225a63f64a6769a41a1a

### 感想
他の関数がまたこれを使うから二つに分けていると思う
マッチングアプリで学ぶGit：恋愛で分かるバージョン管理


# マッチングアプリで学ぶGit：恋愛で分かるバージョン管理

Gitは、ソフトウェア開発で使われるバージョン管理システムです。しかし、「Gitって難しそう」「イメージが満ちない」と感じる方も多いでしょう。そこで今回は、**マッチングアプリの恋愛体験**を例に、Gitの基本概念を楽しく学んでみましょう。

---

## 1. **マッチング＝`init`**
マッチングアプリでお相手とマッチした瞬間。それはまさにプロジェクトが始まるときと同じです。

### Gitの`init`コマンド
リポジトリを新規に作成します。恋愛に例えるなら「マッチした瞬間」に相当します。

```bash
git init
```

## 2. メッセージ＝`git commit`
マッチング後のメッセージのやり取り。Gitでいうコミット作業です。

### Gitの`commit`コマンド
作業内容（恋愛の進展）を記録して積み重ねる行為です。

```bash
git commit -m "初めてのメッセージを送った"
git commit -m "共通の興味が見つかった！"
```

## 3. 並行した出会い＝`git branch`
複数の相手と同時進行でやり取りするイメージ。恋愛での「同時進行」はGitのブランチ管理に似ています。

### Gitの`branch`コマンド
同じプロジェクト内で並行して作業を進めるための機能です。

```bash
git branch main         # 本命
git branch branch-a     # 気になる相手A
git branch branch-b     # 気になる相手B
git branch branch-c     # 気になる相手C
```

## 4. デートの進展＝`git merge`
複数の相手とデートを重ね、本命1人に精緻する瞬間。Gitでいう「ブランチの統合」に当たります。

### Gitの`merge`コマンド
1つのブランチに変更を取り込んで統合する操作です。

```bash
git checkout main
git merge branch-b
git branch -d branch-a  # 不要になった相手を削除
git branch -d branch-c  # 不要になった相手を削除
```

## 5. 別れ＝`git revert`
うまくいかなかったデートを取り消す・振り出しに戻すイメージです。

### Gitの`revert`コマンド
過去の特定のコミットを取り消して新たなコミットを作る操作です。

```bash
git revert HEAD
```

## 6. けんか＝`conflict`
同じ変更範囲が競合することは、恋愛でいう「言い合いによるすれ違い」に相当します。

### Gitのコンフリクト
ブランチ統合（mergeやrebaseなど）で衝突した場合、手動で解決が必要です。

```bash
# けんか(コンフリクト)を確認し手動で修正
git add .
git commit -m "和解した"
```

## 7. ゴール＝`git push`
恋愛が成就して「私たちお付き合いしています！」と公言するイメージ。Gitでいうリモートへの反映です。

### Gitの`push`コマンド
ローカルのコミットをリモートリポジトリに反映（公開）します。

```bash
git push origin main
```

## 8. 近況報告＝`git pull`
恋愛でいう「最近どう？」と相手に最新の状況を聞くイメージ。Gitでいうとリモートから最新の変更を取得する操作です。

### Gitの`pull`コマンド
リモートリポジトリの変更を手元に取り込んで同期を保ちます。

```bash
git pull origin main
```

## 9. 実際の流れ
1. **`git clone`**：最初から恋愛（プロジェクト）が存在しているとき
   - → そこをまるごとクローンして恋愛を始めるシーン
2. **`git init`**：マッチング成立
3. **`git commit`**：やりとりを記録（関係を深める）
4. **`git branch`**：複数の出会いを並行進行
5. **`git pull`**：相手の最新情報を取り込んで、気持ちを合わせる(同期を保つ)
6. **`git merge`**：その中で一人に精緻する（ブランチ統合）
7. **`git revert`**：うまくいかなかったら過去の状態に戻す
8. **`git conflict`**：けんかしたら手動で和解
9. **`git push`**：周りにお知らせ
rust　ファイル分割

内容とは関係ないが[Rustチートシート](https://cheats.rs/)  
よさそう  

---

# Rust モジュールのファイル分割

参照

https://doc.rust-jp.rs/book-ja/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html

[【ゼロからはじめる】プログラミング言語 Rust 集中講座 / The Book (The Rust Programming Language)
](https://youtu.be/tw2WCjBTgRM?si=y_lqxAD02ojdp72E)

---

パスはダブルコロン::で区切られる

second.rs
```
pub fn hello() {
    println!("hello");
}
```

main.rs
```
pub mod second;

use second::hello;

fn main() {
    println("Welcome");
    hello();
}
```
output
```
Welcome
helo
```
## 階層が深くなる場合
mod.rsを用いる。
階層図
```
.
├── main.rs
├── returnMultiply.rs
└── subfiles
    ├── mod.rs
    ├── returnSub.rs
    └── returnSum.rs
```

main.rs
```
pub mod returnMultiply;
mod subfiles;

use returnMultiply::returnMul;
use subfiles::returnSum;
use subfiles::returnSub;

fn main() {
    let x = returnMul(3, 4);
    println!("{}", x);
    let y = returnSum::returnSumVal(3, 4);
    println!("{}", y);
    let z = returnSub::returnSubVal(3, 4);
    println!("{}", z);
}
```

returnMultiply.rs
```
pub fn returnMul(x:u32, y:u32) -> u32 {
    x * y
}
```

subfiles/mod.rs
```
pub mod returnSum;
pub mod returnSub;
```

subfiles/returnSub.rs
```
pub fn returnSubVal(x:i32, y:i32) -> i32 {
    x - y
}
```

subfiles/returnSum.rs
```
pub fn returnSumVal(x:u32, y:u32) -> u32{
    x + y
}
```

実行
`rustc main.rs`
`./main`

output
```
12
7
-1
```
python分析メモ

## モデルの作成方法デモ

### モジュールの読み込み
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report, roc_auc_score, roc_curve
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import StandardScaler
import numpy as np

### データの読み込み
data = pd.read_csv("/content/train.csv", encoding = "shift_jis")

### データ準備
X = data.drop(columns=['PassengerId', 'Survived'])
y = data['Survived']

### 訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)

### XGBoostモデルのトレーニング
xgb_model = xgb.XGBClassifier(
    use_label_encoder=False,
    eval_metric='logloss',
    random_state=42
)
xgb_model.fit(X_train, y_train)

### テストデータでの予測
y_pred = xgb_model.predict(X_test)
y_pred_prob = xgb_model.predict_proba(X_test)[:, 1]

### 評価指標
conf_matrix = confusion_matrix(y_test, y_pred)
class_report = classification_report(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_pred_prob)

### 混同行列の出力
print("Confusion Matrix:")
print(pd.DataFrame(
    conf_matrix,
    index=["Actual Negative", "Actual Positive"],
    columns=["Predicted Negative", "Predicted Positive"]
))

### Classification Reportを表示
print("\nClassification Report:")
print(class_report)

### AUCスコア
print(f"\nROC AUC Score: {roc_auc:.2f}")

### ROC曲線の描画
fpr, tpr, thresholds = roc_curve(y_test, y_pred_prob)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, label=f"ROC Curve (AUC = {roc_auc:.2f})", linewidth=2)
plt.plot([0, 1], [0, 1], 'k--', label="Random Guessing", linewidth=1)
plt.xlabel("False Positive Rate", fontsize=12)
plt.ylabel("True Positive Rate", fontsize=12)
plt.title("ROC Curve (XGBoost)", fontsize=14)
plt.legend(fontsize=12)
plt.grid(True)
plt.show()

## データ加工

### 文字列の取得
#### Extract the string after the first comma in the "Name" column
df['Extracted_Name'] = df['Name'].str.split(',', n=1).str[1].str.strip()

#### Extract the string from the start until the first period ('.')
df['Title'] = df['Extracted_Name'].str.split('.', n=1).str[0].str.strip()

Name列の,の後の文字列を取得する。
→文字列の最初の.の前後に分けて、前を取得する。

### 標準正規化
#### 標準正規化する列を指定
columns_to_normalize = ['A', 'B']

#### スケーラーの作成と適用
scaler = StandardScaler()
data[columns_to_normalize] = scaler.fit_transform(data[columns_to_normalize])

標準正規化する列を指定して、複数の列を一括で変換する
データ構造の学習にとても有用な問題[group-anagrams]

:::note warn
コレは個人の備忘録です、この記事の情報を参考に場合はしっかりとしたソースのチェックをお願いします
:::

お疲れ様です。steveです。最近毎日leetcodeを解いています。
昨日も例に漏れず、同じ大学のON先輩とleetcodeを解いていました。neetcodeのロードマップにしたがった有用問題をgptに選抜してもらい。その日は久々のmediumの問題でした。

こんな問題です。

```
Given an array of strings strs, group the 
anagrams
 together. You can return the answer in any order.

 

Example 1:

Input: strs = ["eat","tea","tan","ate","nat","bat"]

Output: [["bat"],["nat","tan"],["ate","eat","tea"]]

Explanation:

There is no string in strs that can be rearranged to form "bat".
The strings "nat" and "tan" are anagrams as they can be rearranged to form each other.
The strings "ate", "eat", and "tea" are anagrams as they can be rearranged to form each other.
```
同じアナグラムをリストでグループ分けした、グループのリストを返すらしいです。

自分の発想としては、
```
{"eat":{a:1,e,1,t,1},
 "tea":{a:1,e,1,t,1},
 "tan":{a:1,n:1,t:1}
 ・・・}
```
というオブジェクトを作成して
```
maps =
{{a:1,e,1,t,1}:["eat","ate","tea"],
 {a:1,n:1,t:1}:["nat","tan"],
・・・
}

result = list(maps.values())
```
とすればいけるかな？
と思っていました。
結果は・・・

```
TypeError: unhashable type: 'dict'
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    if values_list[i] in temp_total:
Line 26 in groupAnagrams (Solution.py)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ret = Solution().groupAnagrams(param_1)
Line 69 in _driver (Solution.py)
    _driver()
Line 84 in <module> (Solution.py) 
```
**unhashableってなんだろう・・・**

:::note info
辞書（dict）のキーに関する制約
    Pythonでは、辞書（dict）のキーとして使用できるオブジェクトは ハッシュ可能 でなければなりません。ハッシュ可能性とは、次の2つの条件を満たすことを指します：

1,ハッシュ値を持つ:

オブジェクトが固定された一意のハッシュ値（__hash__() メソッド）を持つ必要があります。
ハッシュ値はオブジェクトが同じなら一貫している必要があります。
2,不変性:

オブジェクトが変更可能（ミュータブル）でない必要があります。ミュータブルなオブジェクトの場合、ハッシュ値がオブジェクトの状態によって変化するため、ハッシュテーブル（辞書やセット）での整合性が保てなくなります。

(gptより)
:::

要は、dictのキーにdictを持ってこようっとしたことがいけなかったポイです。

ここで、問題になる、dictのキーの性質ですが、dictのキーは「一意に定まる」必要があります。
ですので、例えば

```
hashmap = {"a":1,"b":2,"c":3}
という構造は、それぞれキーが異なるオブジェクトなので、問題ありません。
例：
id("a") #140732487488816
id("b") #140732487488848
id("c") #140732487488880
→idが異なる

```
ので、一つのidに対してオブジェクトが保証されています。
ですが
```
hashmap = {[1,2,3]:b,[1,2,3,4]:c}
という構造は、キーが同じ可能性があり、一意に定まるオブジェクトと断言できません

例えば
ex_list = [1,2,3]
print(id(ex_list))
# 235948574589

ex_list.qppend(4) #この時点で、ex_list = [1,2,3,4]
print(id(ex_list))
# 235948574589

```
**つまり、同じ　id = 235948574589**
**でも、異なるリストを表現できるというわけです。**
→この構造では、一つのidに対して一意に定まる　とは言えません。

したがって、一つのidに対して一意に定まる　ことを期待しているdictのキーに対して
このようなデータ構造を持つオブジェクトは格納できないのです

:::note info
このように、同じidでも可変であるデータ構造をミュータブル　と言います
逆に、一つのidに対して、一つのオブジェクトが保証されているデータ構造を、イミュータブルと言います
:::

| 1 | 2 | 3 |
|:-:|:-:|:-:|
| 特徴  |  	ミュータブル（mutable） | イミュータブル（immutable）  |
| 変更可能性  | 内容を変更可能  | 内容を変更不可  |
| IDの変化  | 変更してもIDは変わらない  | 変更できないため新しいオブジェクトが作られる  |
|  例 | list, dict, set  | int, float, str, tuple  |

なので、今回の問題は
```
maps =
{((a:1),(e,1),(t,1)):["eat","ate","tea"],
 ((a:1),(n:1),(t:1)):["nat","tan"],
・・・
}#キーをタプルにしている

```
のようにしないと、参照できないようです。

こういうプログラミングの根本となるデータ構造を勉強できたのは、いい体験でした。

:::note warn
今となっては、先に、inputのリストをソートしてから構造作成したほうが楽だと思います
:::
convert_value_to_parameters

# 特定のクラスで返す値が違う
```rb
      def convert_value_to_parameters(value) # キーに対応したバリューが引数
        if value.is_a?(Array) && !converted_arrays.member?(value)
          converted = value.map { |_| convert_value_to_parameters(_) }
          converted_arrays << converted　# 配列でも返す
          converted
        elsif value.is_a?(Parameters) || !value.is_a?(Hash) # パラメータを持つか八種ではなければそのまま返す
          value # 値を返す
        else
          self.class.new(value) # Parametersのインスタンスとして返される
        end
      end
```
https://github.com/rails/rails/blob/9ab2d030209d9608a6c866d83210f5b3b7d2319e/actionpack/lib/action_controller/metal/strong_parameters.rb#L461C7-L471C10

#### converted_arrays　nilかfalseであればsetクラスを新しく生成する

https://qiita.com/masatom86650860/items/9dbe0067ea09ad9c5439

#### member?　key?のエイリアス
https://qiita.com/masatom86650860/items/87e871806f9eaa0c8f7e

#### !を先頭につけると真偽値は反転する
https://qiita.com/masatom86650860/items/060d9e375a6a1eae2e62

### `!converted_arrays.member?(value)`　`@converted_arrays`がnil、falseならばfalseを返すのでtrueになる 
```rb
irb(main):004:0> @converted_arrays ||= Set.new
=> #<Set: {}>
irb(main):005:0> @converted_arrays ||= Set.new
=> #<Set: {}>
irb(main):006:0> @converted_arrays
=> #<Set: {}>
...
irb(main):008:0> @converted_arrays.member?("fafa")
=> false
```

When assigning attributes, you must pass a hash as an argument, String passed. (ArgumentError)
Linuxでとにかく何も考えずにテキストをコピペして編集して保存しておきたい

作業中、リモートシェルのコマンドの実行結果や、SQLの実行結果などをコピーしてどこかに貼っておきたくなる事はよくある。
そこまでなら標準のメモアプリなどでもよい。

ただし、それに対して、正規表現での置換などをしたくなる。そうなると標準のメモアプリでは物足りない。

また、その時その作業が済み、アプリを閉じる。「保存しますか」と聞かれるが、作業は終わっているのでいらないと思い閉じてしまう。
しかし翌日、漏れがあって追加作業をする際に、昨日の作業メモが見たい、となってしまう事がよくある。

保存しておこうにも、「ファイル名」と「保存先ディレクトリ」を聞かれる。私の作業は終わって、次の作業に取り掛かりたいのだ。そんなものどこでもよく、非常に煩わしい。メモ置き場に、今日の日付か何かで勝手に置いておけと思う。

世の中にはObsidianなどメモ用のアプリもある。私も試してみようとした。だがどうにも重厚で、指に合わない。
起動が遅いので開いておく必要があり、テキストをコピーし、「これを貼りたい」と思った時に、Obsidianのウィンドウを探すAlt-Tabで探す手間が煩わしい。これはWebベースのメモアプリも同様。

------

シェルスクリプトを書いた。
``` newmemo.sh
#!/bin/bash

DT=`date +%Y%m%d_%H%M%S` 
mkdir -p ~/Documents/memo
code ~/Documents/memo/ ~/Documents/memo/${DT}.txt
```

こいつを起動すると、VSCodeが、~/Documents/memo を作業ディレクトリとし、そこに現在のタイムスタンプのファイル名の新規ファイルを開いてくれる。
![2025-01-04_14-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148579/3599d157-8c2f-1366-9c17-fa2ccdf17b55.png)

あとはこれをショートカットキーに登録する。
![2025-01-04_14-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148579/5429793f-599a-72eb-b575-1f16e76762b8.png)

これで、作業中にテキストをコピーしたら、
* Win+M でメモ用のVSCodeが開くので
* そこに Ctrl-V で貼り付け、
* Ctrl-S を押せばとりあえず保存

できる。

VSCodeのプロジェクトエクスプローラに過去のファイルも並ぶので、ワンクリックで調べられる。
あとは時々気が向いたときに、不要なものは消したり、有用なものを他に転記するなり、ファイル名を変えてわかりやすくしておくなりすればよい。
ただのファイルなので、どうとでもできる。
ゲームボーイ RGBDS 開発メモ 第11回 hUGETracker

### サウンドを鳴らしたい

サウンドをならすためにはどうすればよいか調べてみたところ、
[hUGETracker](https://github.com/SuperDisk/hUGETracker) を使うとよいようです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733735/e9996994-31f2-6e59-a9b8-9b990db9518a.png">

GUIでぽちぽち入力していって、.asm 　ファイルを出力できて、

```
INCLUDE "include/hardware.inc"

SECTION "Header", ROM0[$100]

	jp EntryPoint

	ds $150 - @, 0 ; Make room for the header

EntryPoint:
	;; enable sound playback
	ld a, $80
	ld [rAUDENA], a
	ld a, $FF
	ld [rAUDTERM], a
	ld a, $77
	ld [rAUDVOL], a

	ld hl, coffee
	call hUGE_init

Main:
    ld a, [rLY]
    cp 144
    jp nc, Main
	call hUGE_dosound
WaitVBlank:
    ld a, [rLY]
    cp 144
    jp c, WaitVBlank
	jp Main
```

のようにhlに曲データのラベルを渡して初期化して、hUGE_dosound をメインループで呼んであげるだけでBGMがなりました。かなりお手軽にBGMをならすことができます。

曲データをつくるのが大変そうですが、おもしろそう。

ゲームボーイかなり開発環境が整っていますね！
Javaで「フレームの背景色を設定する」の動作を確認してみた。

# 概要

Javaで「フレームの背景色を設定する」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/tutorial/jframe/index9.html

# 実装

以下のファイルを作成しました。

```Sample.java
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.Container;
import java.awt.Color;
import java.awt.BorderLayout;

class Sample extends JFrame{
  public static void main(String args[]){
    Sample frame = new Sample("MyTitle");
    frame.setVisible(true);
  }

  Sample(String title){
    setTitle(title);
    setBounds(100, 100, 728, 400);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JButton btn = new JButton("Open");

    Container contentPane = getContentPane();
    contentPane.setBackground(Color.ORANGE);
    contentPane.add(btn, BorderLayout.NORTH);
  }
}
```

以下のコマンドを実行しました。

```
$ javac Sample.java 
$ java Sample
```

# まとめ

何かの役に立てばと。
NFC な名刺カードを自作する

昨今では NFC タグに情報を書き込んでおき、相手のスマートフォンで読んでもらうというのがあるそうです。せっかくなので自作してみました。

## 買った NFC タグ

https://www.amazon.co.jp/dp/B0CRHKCGBB

貼れるものを選びました。名刺(縦50mm)と比べたときの様子。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/1c3935b7-a878-1c03-d6d1-0f127c2410ae.png)

## 書き込み方

NFC Tools を NFC 読み書きができるスマートフォンにインストールして、書き込みます。

Android https://play.google.com/store/apps/details?id=com.wakdev.wdnfc&pcampaignid=web_share
iOS https://apps.apple.com/jp/app/nfc-tools/id1252962749

### 書き込む情報

* ポートフォリオサイト（自己紹介ページ）がある方なら **"URL/URI"** で URL を指定
    * → スマートフォンでの読み取り時は、Web ブラウザが起動します(※1)
* VCARD 形式で連絡先情報に読み込んでもらいたいなら **"連絡先"**
    * → スマートフォンでの読み取り時は、連絡先へのインポートがされます

※1 正確にはURIのスキーマに対応したアプリが起動する

### 複数レコードを書き込んだ場合の挙動

NFC タグには、容量の限り複数のレコードを書き込むことができます。ですが、スマートフォン側での読み取りは、最初のレコードのみ読み取ってくれます。情報交換用の用途では、２レコード目以降は意味が無いのでご注意。

### URL/URI と カスタム URL/URI の違い

NFC Tools には「URL/URI」と「カスタム URL/URI」があります。結論、違いはありません。
※書き込んでみた後に NFC Tools で読んでみると、相違無いことがわかります。

## 消去・書き換え・ROM 化

内容の書き換えが可能です。耐久回数は製品によって異なるため、仕様を確認してください。NTAGシリーズは10万回とのこと (https://www.e-garde.co.jp/product/iccard/ntag/ )。
書き換えといっても、基本的には上書きです。

消去（フォーマット）も NFC Tools でできます。 "その他" メニューから "メモリーを初期化" で行えます。

ROM 化もできます。NFC Tools の "その他" > "タグをロック" でできます。
ROM 化後は、元に戻せません。

## おまけ：スマートフォンと共に持ち歩く場合

名刺切れの緊急避難用として使う予定です。そうなると普段から持ち歩くことになるのですが、スマートフォンケースに入れておくと、自分のスマートフォンで毎度読み取ることになりかねません。

スマートフォンの NFC リーダー/ライター位置を把握しておき※2、そこに干渉しないように持ち歩くようにしましょう。ちなみに私は名刺のこの辺りに貼っておくことにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22754/e0165707-3d6b-2791-f6d2-92e39dddace1.png)

※2 物理的に何度か読ませれば位置特定ができるでしょう

[EoT]
javascriptのみでフリーワード検索

jQueryでチェックボックスの絞り込みを実装しているが、フリーワード検索機能も追加したいと考えています。
部分一致したものだけを表示させるような仕様のものにしたいのですが、調べて出てきたものでうまく実装ができません。
何か解決案や、参考になる記事等ありましたら教えていただきたいです。
GitHub Actionsの共通化について（Composite ActionとReusable workflowsの違い）

# はじめに
業務で、CI/CDをGitHub Actionsで自動化する取り組みに挑戦しています。
ワークフローを作成する中でなく、メンテナンス性や拡張性を高めるために、ワークフローファイルのモジュール化に取り組みました。

調べる中で、共通化する方法が二つあることを知ったので、それぞれについてまとめることにしました。

# Composite Action

### 共通化部分

```action.yml
name: test-action

inputs:
  version:
    default: '3.8'
    required: false

runs:
  using: "composite"
  steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up Docker Buildx
      id: buildx
      uses: docker/setup-buildx-action@v2

    - name: Set Up
      uses: actions/setup-python@v2
      with:
        python-version: ${{ inputs.version }}

```

### 呼び出し

```test-workflow-action.yml
name: test-call

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: test-action
        uses: ./.github/workflows/buildx
        with:
          version: '3.9.1'

```

### 解説

- ファイル名は`action.yml`にする。（フォルダを分けることで、区別するようにするのが一般的。今回は、 `./.github/workflows/buildx`内に`action.yml`をおく）
- `using: "composite"`を指定する
- 値を渡すときは、`inputs`を使って渡す
- **secrets**を渡すことができない

# Reusable workflows

### 共通化部分

```_test-action.yml
name: test-action

on:
  workflow_call:
    inputs:
      version:
        default: '3.8'
        required: false
    secrets:
      secret-version:
        default: '3.8'
        required: false

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: sample
        with:
          secret-version: ${{ secrets.secret-version }}
          version: ${{ inputs.version }}

```

### 呼び出し

```test-workflow-action.yml
name: test-call

jobs:
  test:
    runs-on: ubuntu-latest
      steps:
        - name: Checkout code
          uses: actions/checkout@v3

    call-reusable-workflow:
      uses: ./.github/workflows/_test-action.yml
      with:
        version: '3.9.1'
      secrets:
        secret-version: '3.9.1'

```

### 解説

- ファイル名は任意（ハイフンから始めることが多そう）
- `on`の値に`workflow_call`を指定する
- 呼び出す時はファイル名まで書く（Composite Actionはフォルダ名まで）
- 呼び出されたワークフローから**さらに別のワークフローを呼び出すことはできない**（`_test-action.yml`から、さらに別のワークフローを飛び出すことはできない）
- 値を渡すときは、`inputs`か`secrets`を使って渡す

# 参考

Composite Action
https://zenn.dev/tmrekk/articles/5fef57be891040
https://qiita.com/M_Kagawa/items/98f154477b8b5bd3d315

Reusable workflows
https://zenn.dev/jerome/articles/618af7cc934f2f
https://tech.speee.jp/entry/terraform-reusable-workflow
変数をnullで再代入するのはなぜか（Java）

## この記事を書いた経緯
以前Javaを使っているある現場に参画したのですが、変数をnullで再代入するコードを見かけました。見慣れないことをやっていたため、その理由を探るべくちょっと調べてみました。

## 調べてみたら
結論としては、ガベージコレクターに拾ってもらうために変数をnullに書き換えているようでした。この実装の真意については実装者に聞いてみないとわかりません。本当はビジネスロジック的に意味があったのかもしれません（そんなわけあるか）。
2010年のJavaドキュメント（参考文献1）によると「不要になった変数に明示的にNULLを代入すると、安全に再生できるメモリー上の部分をガベージコレクタが識別しやすくなります。」とあります。同様の件に言及した2010年前後のネット記事もいくつか確認できました。おそらく近年のJavaではガベージコレクタの性能が向上したり、変数の再代入は良くないという思想も相まってこのような実装はされなくなり見られなくなったのでしょう。

### 参考文献
1.不要になった変数に NULL を代入する
https://docs.oracle.com/cd/E19528-01/820-1613/abebi/index.html
Dateオブジェクトで時間部分を削除する方法(GAS)

Dateオブジェクトを生成すると時刻まで設定されますが日付比較ができないなど時間部分を削除したいことがあると思います。

# getメソッド
```javascript:date1.gas
function dateTest1() {
  var today = new Date();
  today = new Date(today.getFullYear(), today.getMonth(), today.getDate());

  console.log(today);
}
```
```console:console
11:47:20 AM	Notice	Execution started
11:47:20 AM	Info	Sat Jan 04 2025 00:00:00 GMT+0900 (Japan Standard Time)
11:47:21 AM	Notice	Execution completed
```

# setメソッド
```javascript:date2.gas
function dateTest2() {
  var today = new Date();  
  today.setHours(0);
  today.setMinutes(0);
  today.setSeconds(0);

  console.log(today);
}
```
```console:console
11:50:02 AM	Notice	Execution started
11:50:02 AM	Info	Sat Jan 04 2025 00:00:00 GMT+0900 (Japan Standard Time)
11:50:03 AM	Notice	Execution completed
```

# toDateStringメソッド
toDateStringメソッドで日付文字列を取得して時間情報を付加する
```javascript:date3.gas
function dateTest3() {
  var today = new Date();
  console.log(today.toDateString());
  today = new Date(today.toDateString() + " 00:00:00 GMT+0900 (Japan Standard Time)");
}
```
```console:console
12:06:27 PM	Notice	Execution started
12:06:27 PM	Info	Sat Jan 04 2025
12:06:27 PM	Info	Sat Jan 04 2025 00:00:00 GMT+0900 (Japan Standard Time)
12:06:29 PM	Notice	Execution completed
```


# getTimeメソッド
getTimeメソッドで1970年1月1日からの経過ミリ秒を取得して
86400000ミリ秒(1日)で剰余算して時間に当たるミリ秒を引き
32400000ミリ秒(9時間)で日本時間(UTC+9)を引く
```javascript:date4.gas
function dateTest4(){
  var today = new Date();
  // 現在日時-時分秒-9時間(日本はUTC+9)
  today = new Date(today.getTime()-(today.getTime()%86400000)-32400000);

  console.log(today);
}
```
```console:console
11:52:10 AM	Notice	Execution started
11:52:11 AM	Info	Sat Jan 04 2025 00:00:00 GMT+0900 (Japan Standard Time)
11:52:11 AM	Notice	Execution completed
```

<!-- 
# 
```javascript:date.gas

```
```console:console
```
-->
APIパラメーターを整理した

普段何気なく使ってますが、改めて整理してみました、備忘録です。

## パラメーターとは
`処理結果に影響を与える外部から投入される変動要素`のこと
数学の世界でも使われますが、プログラムの世界では`引数（仮引数）`のこと。

## APIのリクエストパラメータ
API引数とも呼ばれる。
APIはアプリケーション同士が通信するためのインターフェースであり、リクエストパラメーターはその通信を具体的に指示する役割を果たします。

主に以下の3種類がある
・パスパラメーター
・クエリパラメーター
・ボディパラメーター（リクエストボディ）

## パスパラメーター
パスパラメータは、URLパスの一部を利用するパラメータ
URLでリソースを特定するのに使われる。

```
https://hoge.com/api/employees/15
```

## クエリパラメーター
クエリパラメータはURLの最後に「?」から始まる形式で付け加える。
クエリ文字列は、`KEY＝VALUE`形式
複数のパラメータを記載したい場合には、`&`で区切る

```
https://hoge.com/api/employees?role=sales&gender=male
```

## ボディパラメーター（リクエストボディ）
リクエストの**本文**に含まれるパラメーターで、POST、PUTなど大量のデータを送信する場合に使用されます。JSON形式で渡す。
```
例:POST /employees
{ "name": "Taro Yamada", "age": 30, "role": "engineer", "gender": "male" }
```
【Power Automate】クラウド版とデスクトップ版の違いを一覧化してみた

## はじめに
本記事では、Power Automateのクラウド版（Power Automate）とデスクトップ版（Power Automate Desktop）のちがいをまとめます。
Power Automateにはクラウド版とデスクトップ版があり、それぞれ使い方や得意なことが異なるので、ちがいを理解した上で選択すると良いでしょう。

## クラウド版とデスクトップ版のちがい一覧
クラウド版は、一般的にPower AutomateやPA（ピーエー）と呼ばれ、クラウド環境上で開発し、クラウド環境上で実行されます。一方、デスクトップ版はPower Automate Desktop や PAD と呼ばれ、ローカルPCにインストールしたツールを使用して、PC上での操作を実行します。
これは、OutlookやExcelにも見られる、Web版とデスクトップ版の違いに似ています。クラウド版とデスクトップ版では、それぞれに特有の機能があり、どちらかの環境でしか実行できない操作が存在します。

この違いを次の表にまとめました。

| 項目 | クラウド版 |デスクトップ版 |
|:-----------|:------------|:------------|
|名称、略称  |Power Automate、PA（ピーエー）|Power Automate Desktop、PAD（パッド）|
|インストール要否|不要|要|
|ロボが動く場所|Web上（クラウド上）|ローカルPC上|
|実行時のPC起動|不要|要|
|起動方法|イベントトリガー、 手動、スケジュール実行|手動（有償ライセンス購入でスケジュール実行やラウド版から呼び出すことが可能）|
|得意なこと|クラウド上のファイルやサービスの操作（Microsoft Graph API でできることがアクションで用意されているイメージ）|マウス・キーボード操作、Web スクレイピング、ローカルで起動するツールの操作、Excel操作（クラウドよりも細かい操作が可能）|
|操作対象フォルダ、ファイル |クラウド上のファイル|ローカル上のファイル（OneDrive や Outlookなどクラウドとローカルで同期されているものも操作可能）|
|例|特定のメールが届いたら、添付ファイルを保存する、毎日10 時にTeamsに投稿する、承認依頼する|Web ページの検索結果をExcel に転記する、ファイルを移動する、Excelデータを別のExcel へ転記する|

## どのように選択するか
両者はどちらも自動化ツールであり、機能が重複する部分もあります。
しかし、デスクトップ版はマウス操作やキーボード操作によって、PC上で実行できるほとんどの操作が実行できるというメリットがありますが、手動で起動しなければいけない、実行中はほかの作業ができない、といった致命的なデメリットがあります。
クラウド版は、Web上の操作という制約があるものの、PCの起動状態に依存せず実行でき、自動化のメリットを最大限に享受できます。
そのため、**どちらでも実現できるのであれば、クラウド版を選択し、デスクトップ版でしか実現できない特定の作業がある場合に限り、デスクトップ版を使う**ことが望ましいと考えます。

## Power Automate開発のノウハウをまとめてみた
本記事の内容も含むPower Automteの開発ノウハウを [エンジニアのためのPower Automate開発入門](https://tamcom.booth.pm/items/6432888, "エンジニアのための　Power Automate開発入門") という本にまとめました！
[BOOTH](https://tamcom.booth.pm/items/6432888, "エンジニアのための　Power Automate開発入門")にて販売中ですので、気になる方はぜひご覧ください。

本記事の内容は、「1.2 クラウドとデスクトップ」からの抜粋です。

目次↓
```
はじめに
本書の内容
想定読者
免責事項
1. Power Automateの基礎
1.1 Power Automateとは
1.2 クラウドとデスクトップ
    1.2.1 クラウド版とデスクトップ版の違い
    1.2.2 どちらを選択するか
1.3 トリガーとアクション
    1.3.1 代表的なトリガーとアクション
    1.3.2 トリガーとアクションの組み合わせ例
1.4 コネクタ
    1.4.1 コネクタとは
    1.4.2 コネクタの種類
1.5 テンプレート
1.6 Copilot

2.Power Automateの仕様
2.1 変数、データ型
    2.1.1 変数の宣言・初期化、上書き
    2.1.2 データ型
2.2 インクリメント、デクリメント
    2.2.1 クラウド版でのインクリメント、デクリメント
    2.2.2 デスクトップ版でのインクリメント、デクリメント
2.3 日付・時間
    2.3.1 クラウド版での日時の扱い
    2.3.2 デスクトップ版での日時の扱い
    2.3.3 代表的な書式指定子
2.4 式・演算子
    2.4.1 条件
    2.4.2 繰り返し
    2.4.3 クラウド版の繰り返し処理
    2.4.4 デスクトップ版の繰り返し処理
    2.4.5 式
2.5 グループ化、コメント
    2.5.1 クラウド版のグループ化
    2.5.2 クラウド版のコメント
    2.5.3 デスクトップ版のグループ化
    2.5.4 デスクトップ版のコメント
2.6 関数（子フロー、サブフロー）
    2.6.1 クラウド版の子フロー
    2.6.2 デスクトップ版のサブフロー
2.7 例外処理
    2.7.1 クラウド版の例外処理
    2.7.2 デスクトップ版の例外処理
2.8 テスト
    2.8.1 クラウド版のテスト
    2.8.2 デスクトップ版のデバッグ実行
2.9 フローのオン、オフ
おわりに
```
一方向ハッシュ関数


下記の暗号化技術に関する記事の続きです。
- [共通鍵暗号](https://qiita.com/tom_S/items/e5c6e183064354fee8b2)
- [公開鍵暗号](https://qiita.com/tom_S/items/e29f4cc3efb6bef40daf)

今回は一方向ハッシュ関数について説明します。

# 一方向ハッシュ関数とは

伝送されたデータが改ざんされていないことを確認するための暗号技術です。
一方向ハッシュ関数は、入力として渡されたデータからハッシュ値という値を出力します。

![一方向ハッシュ.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/c6107a51-b59b-d802-db7c-d9c7fa64e32f.png)

ハッシュ値とは伝送データから指紋のようなもので、伝送データと同時に通信相手に送ります。

![一方向ハッシュ関数シーケンス.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/94381716-eeba-29b1-b10f-88d6e3dd1541.png)

上記の図のように、伝送データとハッシュ値を受け取った通信相手は、伝送データからハッシュ値を再度計算し、送られてきたハッシュ値と比較します。そして、計算したハッシュ値と送られてきたハッシュ値が同じになれば、データは改竄されていないことになります。
このようにして、一方向ハッシュ関数とハッシュ値を改ざんの検知に使用します。

## 一方向ハッシュ関数の性質

一方向ハッシュ関数によって出力されるハッシュ値は下記の性質を持つ必要があります。

- あるデータAから出力されたハッシュ値は、他のデータから出力されたハッシュ値と同じ値にならない(衝突耐性)
  - 衝突耐性がないとハッシュ値を偽装できる可能性があり、伝送データが改ざんされていないことの証明ができないため
- 一方向ハッシュ関数に渡されるデータのサイズ、中身によって、出力されるハッシュ値が変わらない

![ハッシュ性質.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/fa29326e-7304-ff64-ee24-256f103b7b39.png)

- ハッシュ値から入力データへの逆変換はできない(元データの推測はできない)

![一方向ハッシュ_逆変換.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/8294f48c-f413-861e-5d7f-2124ab7ec0ca.png)


# 一方向ハッシュ関数の種類

ここからは、一方向ハッシュ関数の種類についてそれぞれ説明していきます。
ここで説明するもの以外にも、MD4,MD5(メッセージダイジェスト)というものもあるのですが、衝突耐性に問題があり非推奨とされているため、割愛しています。

## SHA-1

160bitのハッシュ値を算出する一方向ハッシュ関数です。
使用されていることも多いですが、2005年、SHA-1に対する効果的な攻撃法が発見されています。互換性維持を目的とする場合以外は使用するのを推奨されておらず、SHA-1使用者にはSHA-2への移行を推奨されています。

## SHA-256, SHA-384, SHA-512 (SHA-2)

256,384,512ビットのハッシュ値を算出する一方向ハッシュ関数（SHA-256は256ビット、SHA-384は384ビット、SHA-512は512ビット）です。
CRYPTREC暗号リストでは、電子政府推奨暗号リストにSHA-2が掲載されており、使用が推奨されていると言ってもよいかもしれません。
ただ、ハッシュ値のビット数が少ないと、酷似した誤った文章を作成し、ハッシュ値が同じものを見つけ出される可能性が上がるので注意が必要です。できるだけ、SHA-512のようにビット数が長いものを使用したほうが安全です。

## RIPEMD

ブロックチェーン、ビットコインなどでよく利用されている一方向ハッシュ関数です
RIPEMD-128、RIPEMD-160、RIPEMD-256、RIPEMD-320という種類があり、末尾についている数字が算出されるハッシュ値のビット数となります。RIPEMD-160がよく使われています。


## SHA-3

コンペによる選定で選ばれた一方向ハッシュ関数です。SHA-2とはアルゴリズムが違い、KECCAKというアルゴリズムが使われています。
クリーンな構成で、多様なデバイスにも対応するため、組み込み用途にも適しています。SHA3-224、SHA3-256、SHA3-384、SHA3-512の四種類があります。
KECCAKについてはGitHubに、[KECCAKツール](https://github.com/KeccakTeam/KeccakTools )というソフトウェアも公開されています。ここで詳しい説明は割愛しますが、時間があれば別記事でまとめてみたいと思います。


# 参考文献

- 暗号技術入門 秘密の国のアリス(書籍)
https://cr.hyuki.net/

https://geechs-job.com/tips/details/37

https://gaiax-blockchain.com/ripemd-160
【Angular】TimeoutErrorが発生する単体テストを作成する。

わざとTimeoutErrorを発生させる記事が探しても見当たらなかったため、備忘録として残しておきます。

# テスト対象クラス
以下、HelloWorldServiceに対してテストを行います。

```hello-world.service.ts
@Injectable({ providedIn: 'root' })
export class HelloWorldService {

  item: string = '';

  constructor(private requestService: RequestService) { }

  public setItemList(): void {
    // getItem() → Observable型のレスポンスDtoが返却されるメソッド
    this.requestService.getItem().pipe(
      map(
        response => {
          this.item = response.item
        }
      ),
      // Timeoutを2秒に設定
      timeout(2000),
      catchError((error) => {
        return throwError(() => error);
      })
    ).subscribe()
  }
}
```

# テストクラス
続いて、テストクラスです。
Mock化したクラスに対して、返却される値（Observable型のレスポンスDto）を指定します。
返却される値に対して、pipe(delay(3000)) を使用します。
上記を行うことで、3秒処理を遅らせることができるため、TimeoutErrorが発生します。


```hello-world.service.spec.ts
describe('HelloWorldService', () => {
  let helloWorldService: HelloWorldService;
  let requestService: jasmine.SpyObj<RequestService>;
  let response: Observable<Item>;

  response = of({
    item: 'テスト'
  });

  beforeEach(() => {
    TestBed.configureTestingModule(
      { providers: [HelloWorldService, { provide: RequestService, useValue: jasmine.createSpyObj('RequestService', ['getItem']) }] }
    )

    requestService = TestBed.inject(RequestService) as jasmine.SpyObj<RequestService>;
    // mock化して返却される値をテスト内で指定する。
    // pipe(delay(3000))を付与して、処理を3秒遅らせる。
    requestService.getItem.and.returnValue(<any>response.pipe(delay(3000)));
    helloWorldService = new HelloWorldService(requestService);
  });

  it('テスト', fakeAsync(() => {
      helloWorldService.setItemList();
  }));
});
```

# 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2280983/2930bb33-b59b-f4c8-25b8-1399fd7a9c93.png)

無事、TimeoutErrorを発生させることができました。
単体テストの参考になれば...
XserverにBaserCMS5をインストールしてみた　(1)レンタルサーバーの準備

XserverにBaserCMS5をインストールした記録をまとめておきます。

まずは、インストールの前に、レンタルサーバーの準備からです。

# 1.レンタルサーバーを契約
ここでは、[Xserverレンタルサーバー](https://www.xserver.ne.jp/)を例にして説明しています。
プランは、よっぽど大容量のデータを扱うのでは無ければ、「[スタンダード](https://www.xserver.ne.jp/price/)」で十分だと思います。

契約が完了すると、「管理ツールのログイン情報」や、「FTP情報」が記載されたメールが届きます。
このメールは大切に保存しておいてください。

長期契約することで、[独自ドメインが１年・永年無料](https://www.xserver.ne.jp/manual/man_order_present_domain.php)になる特典もあります。

独自ドメインを取得することで、abc.com や abc.site のように、自分の好きな名前をつけたURLにすることができます。ドメイン名は早い者勝ちなので、すでに使われているドメイン名は使用できません。

# 2.Xserverの設定

Xserverでは、初期ドメインが自動的についてきます。
申し込み時に サーバーIDを abc という名称で申し込んでいれば、abc.**xsrv.jp**というドメインを無料で利用できます。
独自ドメインを申し込んでいれば、さらに 独自ドメインも使うことができます。

独自ドメインがあれば、初期ドメインは不要ですが、独自ドメインを本番用、初期ドメインを練習用というように使い分けることもできるでしょう。

https://www.zero-blo.com/server/xsv-initialdomain/

以下の作業は、[Xserverのサーバーパネル](https://secure.xserver.ne.jp/xapanel/login/xserver/server/)を利用して行います。

## 2-1.ドメイン設定
Xserverに独自ドメインを設定するための作業です。
独自ドメインの場合は、「ドメイン」メニューの「ドメイン設定」にドメインを追加します。
設定が完了して、ドメインが反映されるには１時間ほどかかります。
初期ドメイン .xsrv.jpの場合は、すでに設定済みですので、ドメイン設定は不要です。

https://www.xserver.ne.jp/manual/man_domain_setting.php

## 2-2.独自SSLを有効にする
![SSL.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/2c00cc07-fe1e-9e70-d14d-21b461b72f51.png)
- 「変更」をクリックし、OFF→ONになればOK

## 2-3.PHPのバージョンを確認
BaserCMSは、PHPというプログラムで動作します。
- 「PHP」メニューの「PHP Ver切り替え」を選択	
- 現在のバージョンを控えておきます
![PHPver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/29ae86bb-57af-5085-42e6-b55d1767e7e8.png)


## 2-4.MySQLを設定（ユーザ）
BaserCMS上で登録した各種データ・コンテンツは、MySQLにデータとして登録されます。

- 「データベース」メニューの「MySQL設定」を選択
- 「MySQLユーザ追加」タブを選択

![mysql1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/d9c85d13-6573-a51f-f289-d9fd03cde52f.png)
- MySQLユーザID：任意の文字でよいです。英数字を指定できます

MySQLユーザIDとパスワードは、後で行うBaserCMSのインストールで使用しますので、忘れないように控えておきます。通常は使うことはありませんが、データを復旧するときに必要になることがあります。
<br>

![mysql2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/6d453820-dd23-3bae-fa4d-eec9470e18ff.png)

- 「追加する」を選択すると、ユーザが追加されます

## 2-5.MySQLを設定（データベース）
- 「MySQL追加」タブを選択

![mysql3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/5075ba02-45c2-180f-5fa0-8ee19b762a2f.png)

- MySQLデータベース名：任意の文字でよいです。英数字を指定できます
- 文字コード：UTF-8 のまま
<br>

![mysql4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/bbb37aab-5293-910e-c167-813aebe1ff3a.png)
  
- 「追加する」を選択すると、データベースが追加されます

## 2-6.MySQLを設定（アクセス権を付加）
- 「MySQL一覧」タブを選択

![mysql5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/9cc39ce2-9509-3c80-084b-bf5120aa8ffc.png)

- アクセス権所有ユーザが空になっています
- 「アクセス権未所有ユーザ」のプルダウンにて、2-4で作成したユーザーを選択し追加

![mysql6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23994/55595e6c-9f1b-60af-42ef-e021dd097f3f.png)

<br>

https://www.xserver.ne.jp/manual/man_db_setting.php

---

https://qiita.com/rosenzu/items/4a9970cae7f3dd43bea7

https://qiita.com/rosenzu/items/a8d44dd0aa080650a489

https://qiita.com/rosenzu/items/a80afdbf8fa1ec039263
画面幅によってファーストビュー画像を切り替える

画面幅によってファーストビュー画像を切り替える方法をメモに残しておきます。

```html
<div class="p-fv">
<picture>
<source media="(min-width: 768px)" srcset="./assets/img/fv.png" />
<img src="./assets/img/sp/fv.png" alt="かっこいいサイトだよ" />
</picture>
</div>
```
## ポイント
• `min-width: 768px`はスマホ幅とPC幅のブレークポイントです。

• `picture`タグと`source`タグを使用することで、画面幅が768px以上になると、`srcset`で設定した画像に切り替わります。

•`img`タグで指定した画像は、画面幅が条件に合わない場合や、古いブラウザで`picture`タグがサポートされていない場合にデフォルトで表示される画像です。

この方法を使えば、画面幅やデバイスに応じて最適な画像を表示できます。
CodeIQ:「ルーム・アンド・ルーフ」問題 の解答    

Kawazoe(@riverplus)さんのCodeIQの問題です。
問題はこちらにお世話になりました。解答例もあります。
~~->https://blog.goo.ne.jp/r-de-r/e/1dcaa86093bd1be808d7e846faeb269d~~
->https://sangaku0418.hatenablog.com/entry/2017/02/16/100100
(2025.5.2削除、2025.5.3追加)
こちらにも情報があります。
->https://togetter.com/li/1079332

ーーー2025.5.2追加
下からn番目の大きさの正方形Pnの面積の整数部分を求める問題です。
図形がないと問題の説明が難しいですが、問題に沿って正方形の面積Snを求めると、
容易にSn=(2+√3)^nとなるのがわかりますので
要するに大きな数nに対して(2+√3)^nの整数部分を求める問題です。
ーーー

挑戦した川添さんの問題でこれだけは自力で解けませんでした。
二項定理利用などさんざん失敗した挙句、
心ならずもtogetterにあったコメントを参考に、
wikipediaの「ほとんど整数」を参照
->https://ja.wikipedia.org/wiki/%E3%81%BB%E3%81%A8%E3%82%93%E3%81%A9%E6%95%B4%E6%95%B0

a=2+sqrt(3),b=2-sqrt(3)としますと、
a+b=4、a\*b=1なのでa^n+b^nは整数で、b^n <1なのでfloor(a^n)=a^n+b^n-1  
またa^(x+y)+b^(x+y)
　　=(a^x+b^x)\*(a^y+b^y)-(a^x\*b^y+a^y\*b^x)
　　=(a^x+b^x)\*(a^y+b^y)-(a\*b)^x*(a^(y-x)+b^(y-x))　
　　=(a^x+b^x)\*(a^y+b^y)-(a^(y-x)+b^(y-x))　　　　　(y>=x)
ですので、指数を小さくすることができます。
（a^(y-x)+b^(y-x)　が　x=yの時2、y=x+1の時4）

```JUlia
#julia ver 1.41,1.53,1.10.3

function solve(n,a)
 m = 10^6
 function fn(n)   # a^n+b^n
  if n == 1
    return 4
  else
    d,r = divrem(n,2)
    if r == 0
       return mod(fn(d)^2 - 2,m)
    else
       return mod(fn(d)*fn(d+1)-4,m)
    end   
  end
 end 
 ans = fn(n) - 1  
 s = a == ans ? "ok " : "no "
 println(s,ans)
end 

solve(1, 3)
solve(2, 13)
solve(3, 51) 
solve(4, 193)
solve(10, 524173)
solve(601, 868003)
solve(749501, 493043)
solve(987654, 378701)
solve(999600, 1)
```

別解
上の式より
f(1)=4,f(2)=f(1)*4-2,
f(n)=(a^(n-1)+b^(n-1))\*(a^1+b^1)-(a^((n-1)-1)+b^((n-1)-1))
　　=f(n-1)*f(1)-f(n-2)
上の解のほうが速いですがこの解でも十分な速さです。
この解をPrologで書きました。

```Prolog
%SWI-Prolog ver 7.42,7.64,9.25
%:-initialization(start).   %ideone,Online Prolog Compiler
%start.

fn(2,_,_,R,R).
fn(N,M,X,Y,R):-Z is (4*Y-X) mod M,N1 is N-1,fn(N1,M,Y,Z,R).

solve(1,_,3).
solve(N,M,R):-Y is 4*4-2,(N =:= 2->R1=Y;fn(N,M,4,Y,R1)),R is R1-1.

start:-M=1000000,f(N,A),(solve(N,M,R)->(R==A->Str=" ok  ";Str=" no  ");
    write(fail)),write(" "),write(Str),writeln(N->R),fail.
start.

f(1, 3).
f(2, 13).
f(3, 51).
f(4, 193).
f(10, 524173).
f(601, 868003).
f(749501, 493043).
f(987654, 378701).
f(999600, 1).
```
三項演算子を活用してコードを簡潔に：使いどころと注意点

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94360/b3228141-e603-cf29-79e5-8306df2f06fc.png)

## はじめに

三項演算子は、条件分岐を簡潔に記述するための便利なツールですが、使い方を誤ると可読性を損なう原因にもなります。特に、文字数が多い行や特殊な文字列が含まれる場合には考慮が必要です。この記事では、三項演算子の基本的な使い方、改善例、さらに特殊なケースでの注意点について解説します。

---

## 三項演算子の基本構文

三項演算子は以下のような構文で記述します：

```php
条件式 ? 真の場合の値 : 偽の場合の値;
```

### 例：基本的な使い方

```php
$evenOrOdd = ($number % 2 === 0) ? 'Even' : 'Odd';
```

- **条件式**: `$number % 2 === 0` （偶数かどうか）
- **真の場合の値**: `'Even'`
- **偽の場合の値**: `'Odd'`

この例では、変数 `$number` が偶数の場合に `'Even'`、そうでない場合に `'Odd'` が `$isEven` に代入されます。

---

## 冗長なループ処理の例

次に、三項演算子を使う前の冗長なコードを見てみましょう。

```php
foreach ($products as $product) {
    if ($product['category'] === 'important') {
        $processedProducts[] = [
            'id' => $product['id'],
            'name' => $product['name'],
            'categoryLabel' => 'Special',
        ];
    } else {
        $processedProducts[] = [
            'id' => $product['id'],
            'name' => $product['name'],
            'categoryLabel' => 'Other',
        ];
    }
}
```

このコードには以下の課題があります：

- **重複**: `processedProducts[]` への追加処理が2回書かれている。
- **読みにくさ**: 同じ構造のコードが条件ごとに繰り返されており、意図が一目で理解しにくい。

---

## 三項演算子を使った改善例

同じ処理を三項演算子を使って簡潔に書き直すと、以下のようになります。

```php
foreach ($products as $product) {
    $processedProducts[] = [
        'id' => $product['id'],
        'name' => $product['name'],
        'categoryLabel' => $product['category'] === 'important'
            ? 'Special'
            : 'Other',
    ];
}
```

### 改善ポイント：

1. **重複の排除**

   - `processedProducts[]` への追加処理が1箇所にまとめられました。

2. **簡潔さ**

   - `categoryLabel` の設定が三項演算子で簡潔に記述されています。

3. **可読性の向上**

   - 全体の構造がスリムになり、何をしているのかが直感的に理解できます。

---

## 三項演算子を使うときの注意点

### 1. 情報量が多い行での埋もれ

三項演算子は情報量が多い行に埋もれやすいという欠点があります。以下はその例です：

```php
$results = array_map(fn($product) => "$product[name] (ID: $product[id]) is in category " . ($product['category'] === 'important' ? 'Special' : 'Other'), $products);
```

#### 問題点：

- **文字数が多い**: 文字数が多い記述の中で三項演算子の `?` と `:` が埋もれ、視認性が低下しています。ここで三項演算子が使われていることが分かりにくいです。
- **ロジックの混在**: 配列マッピング、文字列結合、条件分岐が一行に詰め込まれています。

### 特殊文字列との混同

三項演算子を含むコードで、同じく `?` と `:` を含む文字列を処理する場合、混同が発生しやすくなります。

#### 悪い例

```php
$results[] = "Condition: " . ($isConditionMet ? "Yes?" : "No:") . " Check details.";
```

#### 問題点：

- **混乱の原因**: 三項演算子の `?` や `:` と、文字列内の `?` や `:` が混ざることで、コードを読む際に混乱を招きます。

---

## 三項演算子を使うときのポイント

1. **条件が短い場合に限定**

   - 三項演算子はシンプルな条件分岐に適用するのが最適です。

2. **情報量が多い箇所での使用に注意**

   - 情報量が多い行で三項演算子を使用することは避けるべきです。特に、文字数が多い変数名やメソッド名を含む処理では、三項演算子が埋もれてしまい、かえって可読性が低下します。三項演算子を書く以前に、変数名・メソッド名の改善を検討しましょう。

3. **特殊文字列との併用を避ける**

   - `?` や `:` を含む特殊な文字列を処理する場合は、三項演算子を使用すると混乱が生じやすいことに注意が必要です。

---

## まとめ

三項演算子は、適切に使えばコードを簡潔にできる強力なツールですが、使用方法を誤ると可読性が低下する原因になります。

- 短く単純な条件分岐には積極的に活用する。
- 情報量が多い場合や特殊な文字列を扱う場合は慎重に利用する。

この記事を参考に、三項演算子を効果的に活用し、可読性と保守性の高いコードを目指してください！
Firebaseをデプロイすると「Uncaught Error: supabaseUrl is required.」になる(GitHub Actions、React)

# 問題
- GitHub Actionsのログでエラーがないのに、ページが真っ白になる
- GitHub Actionsを通して、Firebaseをデプロイすると、workflowはパスする
- ローカルでは問題なく動く
- GitHubのsecretキーは設定済
- GitHub Actionsのymlファイルは前回のものを踏襲

上記にも関わらず、ブラウザの検証ツールでコンソールを確認すると、「Uncaught Error: supabaseUrl is required.」のエラーが出る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881019/c57b95ee-02fb-d23c-3320-6f80ed045412.png)

# 解決方法
firebase.jsonの設定漏れでした。

<details><summary>firebase.json</summary>

```firebase.json
// ※念の為ですが、jsonファイルではコメントアウトできません

{
    "hosting": {
    // Firebase Hosting にデプロイするディレクトリを指定
      "public": "dist",
      "ignore": [
        "firebase.json",
    // 先頭がピリオドのファイルはシステムから隠す
        "**/.*",
    // サイトの作成に使用されるが、実行はされない依存関係が含まれる
        "**/node_modules/**"
      ],
    //   特定のURLパスを別のパスにリダイレクトするために使用される
      "rewrites": [
        {
    // リダイレクトの対象となるURLパターンを指定する
    // "**"はワイルドカードで、すべてのURLパスにマッチ
          "source": "**",
    // SPAでは、すべてのURLリクエストを/index.htmlにリダイレクトすることで、クライアントサイドのルーティングが正しく機能する
          "destination": "/index.html"
        }
      ]
    }
}

```
</details>

Firebaseのドキュメントを改めてみたところ、GitHubアクション時のデプロイの方法がありました。
前回から修正したファイルも記載します。


<details><summary>cicd.yml</summary>

```cicd.yml

name: CICD

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    env :
      VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
      VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: Install dependencies
        run: npm install
      - name: Run Prettier
        run: npx prettier --write .
      - name: Run tests
        run: npm run test
      - name: Build project
        run: npm run build
      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-artifacts
          path: dist

  deploy:
    name: deploy
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      - name: Download build artifacts
        uses: actions/download-artifact@v4
        with:
          name: build-artifacts
          path: dist
      - name: Deploy to Firebase Hosting
        uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT }}"
          projectId: "${{ secrets.FIREBASE_PROJECT_ID }}"
          channelId: live

```
</details>


# おわりに
2ヶ月前に自分で記事を書いていました。
https://qiita.com/like-mountain/items/292f219eef4ce14b161b#%E5%AE%8C%E6%88%90%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB

前回と同じように、ymlファイルと vite.config.tsを設定しているのに、デプロイできずに困りました。
理解が浅いと応用が効かないことを学びました。

次回は対応できるようにします。

# 参考
ホスティング動作を構成する(ドキュメント)
https://firebase.google.com/docs/hosting/full-config?hl=ja
GitHubActionsを使ってFirebaseにCI/CDできない。Error: supabaseUrl is requiredなど
https://qiita.com/like-mountain/items/292f219eef4ce14b161b#%E5%AE%8C%E6%88%90%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB
rust match 

# match statement
イメージとしてはswitch ?

## 基本的な使い方
```
fn main() {
    let number = 3;

    match number {
        1 => println!("1 です"),
        2 => println!("2 です"),
        3 => println!("3 です"),
        _ => println!("その他の値です"), // `_` はデフォルトケース（ワイルドカード）
    }
}
```

## Option\<T\>のマッチ
```
fn main() {
    let value: Option<i32> = Some(10);

    match value {
        Some(v) => println!("値: {}", v),
        None => println!("値がありません"),
    }
}

```

## Result\<T, E\>  
エラーハンドリングに使われる Result<T, E> の Ok(T) と Err(E) を処理できます。  
```
fn divide(x: i32, y: i32) -> Result<i32, String> {
    if y == 0 {
        Err("ゼロで割ることはできません".to_string())
    } else {
        Ok(x / y)
    }
}

fn main() {
    let result = divide(10, 2);

    match result {
        Ok(value) => println!("結果: {}", value),
        Err(err) => println!("エラー: {}", err),
    }
}
```

## 複数のパターンをまとめて判断
（ORをとるように）
```
fn main() {
    let value = 2;

    match value {
        1 | 2 | 3 => println!("1 か 2 か 3"),
        _ => println!("それ以外"),
    }
}
```

## 範囲指定
```
fn main() {
    let value = 5;

    match value {
        1..=5 => println!("1 から 5 の間"),
        6..=10 => println!("6 から 10 の間"),
        _ => println!("それ以外"),
    }
}
```

## 構造体・列挙型
structやenumも判定可能
```
enum Color {
    Red,
    Green,
    Blue,
}

fn main() {
    let color = Color::Green;

    match color {
        Color::Red => println!("赤"),
        Color::Green => println!("緑"),
        Color::Blue => println!("青"),
    }
}
```

## タプルのmatch
```
fn main() {
    let point = (3, 4);

    match point {
        (0, 0) => println!("原点"),
        (x, 0) => println!("x 軸上, x = {}", x),
        (0, y) => println!("y 軸上, y = {}", y),
        (x, y) => println!("座標 ({} , {})", x, y),
    }
}
```

## ifを用いる（matchのガード）
```
fn main() {
    let value = 8;

    match value {
        x if x % 2 == 0 => println!("偶数: {}", x),
        x => println!("奇数: {}", x),
    }
}
```

## matchの戻り値
matchは式だから、値を返すことができる
```
fn get_message(value: i32) -> &'static str {
    match value {
        1 => "1 です",
        2 => "2 です",
        _ => "その他",
    }
}

fn main() {
    let message = get_message(2);
    println!("{}", message);
}
```

## matchとwhileを組み合わせる
```
fn main() {
    let mut numbers = vec![1, 2, 3];

    while let Some(x) = numbers.pop() {
        println!("取り出した値: {}", x);
    }
}

```

## if letを使う
matchに似た操作として.
Some(x) = value の部分は value が Some であるかどうかをチェックし、もし Some(x) なら x にその値が代入されます。
```
fn main() {
    let value = Some(42);

    if let Some(x) = value {
        println!("値は {}", x);
    }
}
```
「パーセンタイルランク」とは何なのか

私はデータ加工の週1チャレンジ問題集である「[PreppinData](https://preppindata.blogspot.com/)」に、
2023年11月頃から継続チャレンジしているのですが、
その中で何気なく使っていた「パーセンタイルランク（百分位ランク）」のことをちゃんとわかってないことに気づいたので、
今更ながら整理してみたいと思います。

PreppinDataの詳細についてはこちらを参考にどうぞ
[Preppin’ Data入門 by TableauPrepユーザー会](https://note.com/tableauprep_user/n/n1b83ca4b5bc2)

尚、パーセンタイルランクについては他にも分かりやすい記事がありますし、
全然難しい内容ではないので、個人の備忘録的なものに留まっています。

## 「パーセンタイルランク」の定義

これについてはネットで検索すると私の場合GoogleなのでAIが真っ先に答えてくれました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/6c656b9d-5255-93a4-ee3e-2a90f45e2779.png)

これで理解できた方はもうこれ以上読まなくて大丈夫です（笑）
要するに、**その順位が相対的に何%か**ということなんですね。

実際にどういうことなのか、サンプルスーパーストアを使ってTableau Prepで検証してみましょう。

## スーパーストアで検証

サンプルスーパーストアをTableau Prepに読み込ませてみました。
スーパーストアデータは十分綺麗なデータなのでPrepでなんかする必要なさそうですが、
Desktopで計算を作る前にPrepで必要な列を作っておくと便利だと思います。

で、今回は顧客ごとに売り上げのランクを見ていきたいのですが、
スーパーストアは商品の売上ごとにデータが記録されていて、
「顧客一人当たり」のデータでは無いので、
「顧客一人当たり」にするためにまず集計したいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/c03b3678-8710-f6e4-07e2-45ef443feed8.png)

はい、集計できました。
グループ化は顧客名だけでもいいのですが、
同姓同名の人がいるとややこしいので顧客IDも加えました。
（大企業なんかの実務データでは同姓同名、結構ありますよね！）

### 普通のランクを作る

まずは普通のランクを作って確認していきましょう。
「「売上」の３点リーダーをクリック>計算フィールドの作成>ランク」をクリックしてくと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/2495f9d4-90ba-d9cd-232c-b9961146ce9f.png)

ランク作成画面が出ます。
ここでは単純に顧客の売上の降順ランクなので、
フルテーブル、売上のランクの降順でOKです。
（ランクの細かい作り方の説明は割愛します）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/edd877e7-7221-240f-a1a7-0e4b08f0815b.png)

はいできました。
ちゃんとランクができてますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/b72df956-eff9-6fc2-0908-c558514a82e7.png)

### パーセンタイルランクを作る
では、同じように「パーセンタイルランク（百分位ランク）」を作ってみましょう。
先ほどのやり方と同じで、
「ランク」を「百分位ランク」に変えるだけでOKです。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/abfb7034-5d9b-70db-baa6-af06980f073b.png)

出来上がったランクをデータビューで見てみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/c45bbe96-bc23-33ea-f970-2e1110747f35.png)

なんか、小数点がやたら多くてわかりにくくないですか？
そんな時は、100かけて%表示にし、小数点も一桁までに丸めてしまいましょう。
計算フィールドから、このような式を作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/ec70d44c-de31-0846-d0de-2a468c8aaa90.png)

で、出来上がったのがこちら。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/753f14bf-df1d-19ef-1179-ad9ffab8ae8a.png)

無事、一桁台で表示されています。
これをよく見ると、先ほど生成AIであるGeminiが説明してくれた、
「自分のスコアや値が全体のどの位置にあるのかを示す値」というのがわかってきます。

例えばこの「塩井香」さんを見てみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/8277e31c-c099-ec93-c015-e9e3de9ce110.png)

売上で見た際に全体の上位3%の位置にいることが分かります。
もう少し下を見てみると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3666377/c5861bb2-4b91-a53e-26f8-ed59e40217af.png)

「橋口優太」さん「武田遥」さんあたりが、
売上で見たときに全体のちょうど真ん中に位置することが分かります。

このことから、上位10%以上とそれ以下でプロモーション戦略を分けるといったアクションが考えられそうですね。

以上、パーセンタイルランクの説明でした。

## おわりに
いかがでしたでしょうか？
ググれば十分出てくるし今更記事にするまでもない内容だとは思うのですが、
私の記事は「小学生でもわかるように」を目指していますので、
Tableau Prepやデータ加工の初心者の方に届けばいいなと思って書かせて頂きました。
この記事が誰かの助けになれば大変嬉しいです。
最後まで読んでいただき、ありがとうございました。

Rieko