# 機能設計書 81-diff_by_key

## 概要

本ドキュメントは、Hack Standard Library (HSL) の Dict モジュールにおける `diff_by_key` 関数の機能設計を記述する。この関数は、最初の KeyedTraversable にのみ存在するキーを持つエントリを抽出し、新しい dict として返す機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：アプリケーション開発において、2つ以上の連想配列（dict）を比較し、一方にのみ存在するデータを抽出する処理は頻繁に発生する。例えば、データベースから取得した最新データと既存キャッシュを比較して新規追加分を特定する場合や、ユーザー権限リストの差分を計算する場合などである。本関数は、このような集合演算をキーベースで効率的に行うためのユーティリティを提供する。

**機能の利用シーン**：
- マスターデータの差分更新処理において、新規追加されたキーのみを抽出する場合
- 複数のデータソースを統合する際に、重複しないエントリを特定する場合
- キャッシュ無効化の判定で、変更があったキーを特定する場合

**主要な処理内容**：
1. 第一引数の KeyedTraversable が空の場合、即座に空の dict を返す
2. 第二引数以降が全て空の場合、第一引数をそのまま dict として返す
3. 第二引数以降の全ての KeyedTraversable をマージして比較対象のキー集合を構築
4. 第一引数の各エントリについて、キーが比較対象に含まれないものだけをフィルタリング
5. フィルタリング結果を新しい dict として返却

**関連システム・外部連携**：本関数は純粋な計算処理であり、外部システムとの連携はない。内部的には `Dict\merge()` と `Dict\filter_keys()` を利用している。

**権限による制御**：本関数は汎用ユーティリティであり、権限による制御は行わない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本プロジェクトはUIを持たないライブラリのため画面なし |

## 機能種別

計算処理（集合演算）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $first | KeyedTraversable<Tk1, Tv> | Yes | 差分を取得する基準となる KeyedTraversable | なし |
| $second | KeyedTraversable<Tk2, mixed> | Yes | 比較対象の KeyedTraversable | なし |
| ...$rest | KeyedContainer<Tk2, mixed> | No | 追加の比較対象（可変長引数） | なし |

### 入力データソース

関数引数として直接渡される KeyedTraversable オブジェクト。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | dict<Tk1, Tv> | $first のうち、$second 以降のキーに含まれないエントリのみを含む dict |

### 出力先

関数の戻り値として呼び出し元に返却。

## 処理フロー

### 処理シーケンス

```
1. 入力検証
   └─ $first が空（falsy）の場合、空の dict を返却
2. 比較対象検証
   └─ $second と $rest が全て空の場合、$first を dict に変換して返却
3. 比較対象キー集合の構築
   └─ Dict\merge() で $second と $rest を統合
4. フィルタリング処理
   └─ Dict\filter_keys() で $first から比較対象に存在しないキーのみ抽出
5. 結果返却
   └─ フィルタリング結果を dict として返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{$first は空か?}
    B -->|Yes| C[空の dict を返却]
    B -->|No| D{$second と $rest は全て空か?}
    D -->|Yes| E[$first を dict に変換して返却]
    D -->|No| F[$second と $rest をマージ]
    F --> G[filter_keys で差分抽出]
    G --> H[結果の dict を返却]
    C --> I[終了]
    E --> I
    H --> I
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-81-01 | キーの一致判定 | キーは厳密等価（===）で比較される | 常時 |
| BR-81-02 | 値の無視 | 差分判定では値は考慮されず、キーのみで判定 | 常時 |
| BR-81-03 | 順序保持 | 結果の dict は $first の順序を保持 | 常時 |

### 計算ロジック

キーベースの集合差分演算:
- 結果 = { (k, v) | (k, v) in $first AND k not in keys($second union $rest) }

## データベース操作仕様

### 操作別データベース影響一覧

本関数はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 型エラー | 不正な型の引数が渡された場合 | Hack型システムによりコンパイル時に検出 |

### リトライ仕様

本関数は純粋関数であり、リトライの概念は適用されない。

## トランザクション仕様

本関数はステートレスな純粋関数であり、トランザクション管理は不要。

## パフォーマンス要件

- 時間計算量: O(n + m)、n は $first のサイズ、m は $second 以降の合計サイズ
- 空間計算量: O(n + m)

## セキュリティ考慮事項

- 純粋関数であり、副作用がないためセキュリティリスクは低い
- 入力データの検証は呼び出し元の責任

## 備考

- 空の入力に対する最適化が実装されている
- Hack の係数コンテキスト（ctx）を使用して純粋性を保証

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

Hack の組み込み型である dict, KeyedTraversable, KeyedContainer について理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | select.php | `src/dict/select.php` | dict 型の基本操作と関数シグネチャの理解 |

**読解のコツ**: Hack 言語の型パラメータ（ジェネリクス）に注目。`Tk1 as arraykey` は Tk1 が arraykey（int または string）のサブタイプであることを示す。

#### Step 2: エントリーポイントを理解する

diff_by_key 関数の定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | select.php | `src/dict/select.php` | diff_by_key 関数の実装（25-40行目） |

**主要処理フロー**:
1. **25-29行目**: 関数シグネチャの定義、型パラメータと引数の確認
2. **30-33行目**: $first が空の場合の早期リターン
3. **34-37行目**: $second と $rest が空の場合の早期リターン
4. **38行目**: Dict\merge() を使用して比較対象をマージ
5. **39行目**: Dict\filter_keys() で差分を抽出

#### Step 3: 依存関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | combine.php | `src/dict/combine.php` | merge 関数の実装（50-61行目） |
| 3-2 | select.php | `src/dict/select.php` | filter_keys 関数の実装（124-136行目） |
| 3-3 | introspect.php | `src/c/introspect.php` | contains_key 関数の実装（72-81行目） |

**主要処理フロー**:
- **combine.php 50-61行目**: merge は複数の KeyedTraversable を単一の dict に統合
- **select.php 124-136行目**: filter_keys は述語関数に基づいてキーをフィルタリング
- **introspect.php 72-81行目**: contains_key は O(1) でキーの存在を確認

### プログラム呼び出し階層図

```
Dict\diff_by_key($first, $second, ...$rest)
    |
    +-- Dict\cast_clear_legacy_array_mark($first)  [条件付き]
    |
    +-- Dict\merge($second, ...$rest)
    |       |
    |       +-- Dict\cast_clear_legacy_array_mark($first)
    |       |
    |       +-- foreach でエントリを追加
    |
    +-- Dict\filter_keys($first, $predicate)
            |
            +-- C\contains_key($union, $key)
                    |
                    +-- \array_key_exists($key, $container)
```

### データフロー図

```
[入力]                      [処理]                           [出力]

$first ─────────────────────────────────────────────────────┐
(KeyedTraversable)                                          |
                                                            v
$second ─────┬────> Dict\merge() ────> $union ────> filter_keys() ────> dict<Tk1, Tv>
             |      (比較対象の統合)     (キー集合)   (差分抽出)
...$rest ────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| select.php | `src/dict/select.php` | ソース | diff_by_key, filter_keys の実装 |
| combine.php | `src/dict/combine.php` | ソース | merge 関数の実装 |
| cast.php | `src/dict/cast.php` | ソース | cast_clear_legacy_array_mark の実装 |
| introspect.php | `src/c/introspect.php` | ソース | contains_key 関数の実装 |
| DictSelectTest.php | `tests/dict/DictSelectTest.php` | テスト | diff_by_key のユニットテスト（17-57行目） |
