# 機能設計書 18-ユニーク・重複処理

## 概要

本ドキュメントは、Nushellにおけるユニーク・重複処理機能の設計について記述する。uniq、uniq-by、compactコマンドを通じて、重複の除去やnull値の削除機能を提供する。

### 本機能の処理概要

ユニーク・重複処理機能は、リストやテーブルから重複要素を除去したり、null/空値を削除する。uniqは値全体で重複判定、uniq-byは指定カラムで判定、compactはnull/空値を除去する。

**業務上の目的・背景**：データ処理において、重複データの除去やクリーンアップは品質向上の基本操作である。ログの重複排除、マスタデータの正規化、欠損値の除去など、データクレンジングの多様なパターンに対応する。

**機能の利用シーン**：
- `[1 2 2 3] | uniq` で重複を除去
- `$table | uniq-by id` で指定カラムで重複除去
- `[1 null 2 null 3] | compact` でnullを除去
- `[1 "" 2 {} 3] | compact --empty` で空値も除去

**主要な処理内容**：
1. 入力データをイテレート
2. 値のハッシュ/比較キーを生成
3. 重複判定/null判定
4. 結果を返却

**関連システム・外部連携**：nuon（ハッシュキー生成）、itertools（ソート処理）と連携。

**権限による制御**：特になし。純粋なデータ操作機能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインREPL | 主画面 | ユニークコマンドの入力と結果表示 |

## 機能種別

データ操作（重複処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| --count, -c | Switch | No (uniq/uniq-by) | 出現回数を表示 | - |
| --repeated, -d | Switch | No (uniq/uniq-by) | 重複のみ表示 | - |
| --unique, -u | Switch | No (uniq/uniq-by) | 1回のみの値を表示 | - |
| --ignore-case, -i | Switch | No (uniq/uniq-by) | 大文字小文字無視 | - |
| --keep-last, -l | Switch | No (uniq-by) | 重複で最後の値を保持 | - |
| columns | String | Yes (uniq-by) | 比較対象カラム | 有効なカラム名 |
| columns | String | No (compact) | 対象カラム | - |
| --empty, -e | Switch | No (compact) | 空値も除去 | - |

### 入力データソース

- パイプライン入力: List、Table、Record
- uniq/uniq-by: List
- compact: List、Record

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| unique_list | List | (uniq/uniq-by) 重複除去後のリスト |
| count_table | Table | (--count) value/countカラムを持つテーブル |
| compacted_list | List | (compact) null除去後のリスト |
| compacted_record | Record | (compact) null除去後のレコード |

### 出力先

- パイプライン出力: `PipelineData`として後続コマンドへ渡す

## 処理フロー

### 処理シーケンス

```
1. コマンド引数の解析
   └─ uniq: フラグを取得
   └─ uniq-by: カラムとフラグを取得
   └─ compact: カラムと--emptyを取得

2. 入力データの処理
   └─ uniq: ValueCounterで値をラップ
   └─ uniq-by: 指定カラムの値で比較キー生成
   └─ compact: is_nothing/is_empty判定

3. 重複判定/フィルタリング
   └─ HashMapで重複カウント
   └─ フラグに応じた結果フィルタリング

4. 結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{コマンド種別}
    B -->|uniq| C[フラグ解析]
    B -->|uniq-by| D[カラム+フラグ解析]
    B -->|compact| E[カラム+--empty解析]

    C --> F[ValueCounterでラップ]
    D --> G[指定カラム値で比較キー生成]
    E --> H{Record入力?}

    F --> I[HashMapで重複カウント]
    G --> I

    H -->|Yes| J[Record.retain処理]
    H -->|No| K[filter処理]

    I --> L{--repeated?}
    L -->|Yes| M[count > 1のみ保持]
    L -->|No| N{--unique?}
    N -->|Yes| O[count == 1のみ保持]
    N -->|No| P[全て保持]

    M --> Q[インデックスでソート]
    O --> Q
    P --> Q

    Q --> R{--count?}
    R -->|Yes| S[value/countテーブル生成]
    R -->|No| T[値のみリスト生成]

    J --> U[終了]
    K --> U
    S --> U
    T --> U
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 入力順序保持 | uniqは入力順序を保持 | 常に適用 |
| BR-02 | 最初の値保持 | 重複時はデフォルトで最初の値を保持 | --keep-lastなし |
| BR-03 | 大文字小文字正規化 | --ignore-caseで文字列をfolded_caseに変換 | フラグ指定時 |
| BR-04 | 空値判定 | compactの--emptyで""、{}、[]も除去 | フラグ指定時 |

### 計算ロジック

キー生成:
- 値をnuon形式に変換してハッシュキーとする
- レコードはキーをソートしてから変換（順序依存を排除）

重複カウント:
```
counter = HashMap<key, ValueCounter>
for item in input:
    key = generate_key(item)
    if counter[key] exists:
        counter[key].count += 1
        if keep_last: counter[key].val = item
    else:
        counter[key] = ValueCounter(item, 1)
```

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

該当なし（インメモリ処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| MissingParameter | パラメータエラー | uniq-byでカラム未指定 | カラムを指定 |
| CantFindColumn | カラムエラー | 存在しないカラム指定 | 有効なカラム名を指定 |

### リトライ仕様

該当なし（同期処理のため）

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

該当なし（状態変更を伴わない）

## パフォーマンス要件

- HashMap使用: O(n)の重複検出
- nuonシリアライズ: 複雑なデータ構造でコスト増加の可能性
- compact: filterでストリーミング処理可能

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

- 特になし。読み取り専用操作のみ。

## 備考

- uniqはソート済みデータでなくても動作（内部でHashMap使用）
- uniq-byの--keep-lastは最後に出現した値を保持
- compactのRecord入力はin-placeでretain処理

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | uniq.rs | `crates/nu-command/src/filters/uniq.rs` | ValueCounter構造体(130-164行目)で値と出現情報を理解 |
| 1-2 | uniq.rs | `crates/nu-command/src/filters/uniq.rs` | ItemMapperState構造体(120-124行目)でマッピング状態を理解 |

**読解のコツ**: `ValueCounter`は元の値、比較用の値、出現回数、入力インデックスを保持。インデックスは順序復元に使用。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | uniq.rs | `crates/nu-command/src/filters/uniq.rs` | run関数(52-72行目)でマッパー設定を理解 |
| 2-2 | uniq_by.rs | `crates/nu-command/src/filters/uniq_by.rs` | run関数(59-88行目)でカラム指定処理を理解 |
| 2-3 | compact.rs | `crates/nu-command/src/filters/compact.rs` | run関数(42-61行目)でフィルタ処理を理解 |

**主要処理フロー (uniq)**:
1. **59-61行目**: item_mapperを定義
2. **68行目**: `input.into_iter().collect()`で入力収集
3. **64-71行目**: `uniq()`関数呼び出し

**主要処理フロー (uniq-by)**:
1. **66行目**: カラム名を取得
2. **78行目**: `validate()`でカラム存在確認
3. **85行目**: `item_mapper_by_col()`でカラム値マッパー作成
4. **87行目**: `uniq()`関数呼び出し

**主要処理フロー (compact)**:
1. **49-50行目**: --emptyフラグとカラムを取得
2. **52-55行目**: Record入力はretain処理
3. **57-60行目**: その他はfilter処理

#### Step 3: 重複判定ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | uniq.rs | `crates/nu-command/src/filters/uniq.rs` | uniq関数(233-308行目)で全体フローを理解 |
| 3-2 | uniq.rs | `crates/nu-command/src/filters/uniq.rs` | generate_key関数(209-216行目)でキー生成を理解 |

**主要処理フロー**:
- **251-287行目**: try_foldでHashMapを構築
- **267-281行目**: キー存在時はカウント増加、--keep-lastで値更新
- **291-297行目**: --repeated/--uniqueでフィルタ
- **299行目**: `sort()`でインデックス順にソート

#### Step 4: compact処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | compact.rs | `crates/nu-command/src/filters/compact.rs` | do_keep_value関数(110-116行目) |
| 4-2 | compact.rs | `crates/nu-command/src/filters/compact.rs` | do_keep_row関数(118-127行目) |

**主要処理フロー**:
- **111-114行目**: --emptyで`is_empty()`、それ以外は`is_nothing()`
- **121-126行目**: 行自体の判定とカラム値の判定を組み合わせ

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

```
Uniq::run() / UniqBy::run() / Compact::run()
    │
    ├─ Uniq:
    │      ├─ item_mapper定義
    │      │      └─ ValueCounter::new()
    │      │
    │      └─ uniq()
    │             ├─ try_fold(HashMap)
    │             │      └─ generate_key()
    │             │             └─ sort_attributes()
    │             │             └─ nuon::to_nuon()
    │             │
    │             ├─ retain(--repeated/--unique)
    │             │
    │             └─ sort(by index)
    │
    ├─ UniqBy:
    │      ├─ validate(columns)
    │      │
    │      ├─ item_mapper_by_col(columns)
    │      │      └─ get_data_by_columns()
    │      │
    │      └─ uniq() [same as Uniq]
    │
    └─ Compact:
           ├─ Record: val.retain(do_keep_value)
           │
           └─ Other: input.filter(do_keep_row)
                  └─ do_keep_value()
                         └─ is_empty() / is_nothing()
```

### データフロー図

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

                       ┌─────────────────────────────────┐
PipelineData ─────────▶│          uniq処理                │
     │                 │  for (idx, item) in input:      │
     │                 │    vc = ValueCounter(item, idx) │
     │                 │    key = generate_key(vc)       │
     │                 │    counter[key].count++         │
     │                 │  filter by --repeated/--unique  │
     │                 │  sort by index                  │
     │                 └─────────────────────────────────┘
     │                                  │
     │                                  ▼
     │                          List (unique values)
     │                              or
     │                          Table (with count)
     │
     │                 ┌─────────────────────────────────┐
     └────────────────▶│        compact処理               │
                       │  (Record) retain(!is_null)      │
                       │  (List) filter(!is_null)        │
                       │  (--empty) include is_empty     │
                       └─────────────────────────────────┘
                                        │
                                        ▼
                                 List / Record
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| uniq.rs | `crates/nu-command/src/filters/uniq.rs` | ソース | uniqコマンド実装 |
| uniq_by.rs | `crates/nu-command/src/filters/uniq_by.rs` | ソース | uniq-byコマンド実装 |
| compact.rs | `crates/nu-command/src/filters/compact.rs` | ソース | compactコマンド実装 |
| mod.rs | `crates/nu-command/src/filters/mod.rs` | ソース | フィルターモジュールエクスポート |
