# TSL 结果集与过滤

文档类型：语法主线
是否可直接用于生成代码：是
是否含可直接照写示例：是
是否含不可照写反例：是
遇到不确定时：先按本页候选页继续判断；[12_matrix_and_collections.md](12_matrix_and_collections.md)、[14_ts_sql.md](14_ts_sql.md)；仍不命中时回到语法路由中心 [index.md](index.md)；如果问题已经超出语法层，回到 TSL 总入口 [../index.md](../index.md)

这一篇只讲结果集过滤语法：怎样按某一列过滤、怎样按整行过滤、怎样只返回行下标，以及怎样按字段列表过滤。它不负责 TS-SQL 查询语法，也不把去重型集合运算和业务选股逻辑混进来。

## 本篇职责

回答“已有数组或二维结果集时，想按某个过滤集保留命中的行、排除命中的行，或者只拿到符合条件的行下标时，应该怎么写”。

## 智能体结果集/过滤判断流程

1. 先判断任务是 `filterIn(...)` / `filterNotIn(...)` 过滤、结果集字段访问、去重型集合运算，还是 TS-SQL 查询。
2. 字段访问优先照本页明确的字符串键或结果集字段形态写。
3. 复杂查询需求优先跳转到 [14_ts_sql.md](14_ts_sql.md)，不要把查询语法硬塞进过滤函数。
4. `in` / `sqlin` / `union2` / `intersect` / `minus` / `outersect` 这类去重型集合关系跳转到 [12_matrix_and_collections.md](12_matrix_and_collections.md)。
5. 金融数据筛选要先确认数据来源；函数事实见 [../codegen/dotnet/datawarehouse/](../codegen/dotnet/datawarehouse/)，项目字段和业务上下文回项目实际接口。
6. 没有对应代码块时不要发明结果集/过滤写法。

## 核心规则

- 结果集过滤主函数是 `filterIn(...)` 和 `filterNotIn(...)`。
- `filterIn(rows, filter_array, field)` 返回命中的子结果集；`filterNotIn(...)` 返回未命中的子结果集。
- 第四个参数写成 `false` 时，返回值不再是子结果集，而是符合条件的行下标数组。
- `Field` 写成字符串时，表示按某一列过滤。
- `Field` 写成 `nil` 时，表示按整行过滤；一维数组也走这个入口。
- `Field` 写成 `array("A", "B")` 这类字段名数组时，表示只按这些列做匹配。
- 过滤运算不会自动去重；原结果集里有重复行时，命中的重复行会原样保留下来。
- 去重型的并、交、差运算不放在这一篇，见 [12_matrix_and_collections.md](12_matrix_and_collections.md)。

## 可直接照写示例

### 按单列过滤

按 `"Code"` 列做包含与排除：

代码块身份：可直接照写示例

```tsl
rows := array(
    ("Code": "0001", "V1": 8.9, "V2": 12),
    ("Code": "0002", "V1": 9, "V2": 10),
    ("Code": "0003", "V1": 8, "V2": 8),
    ("Code": "0004", "V1": 1, "V2": 2)
);
code_arr := array("0001", "0003");
keep_rows := filterIn(rows, code_arr, "Code");
drop_rows := filterNotIn(rows, code_arr, "Code");
writeLn(keep_rows[0]["Code"]);
writeLn(keep_rows[1]["Code"]);
writeLn(drop_rows[0]["Code"]);
writeLn(drop_rows[1]["Code"]);
```

结果说明：

- `filterIn(rows, code_arr, "Code")` 返回两行：`0001`、`0003`
- `filterNotIn(rows, code_arr, "Code")` 返回两行：`0002`、`0004`

代码块身份：输出片段

```text
0001
0003
0002
0004
```

### 返回行下标

第四个参数写成 `false` 时，返回行下标而不是子结果集：

代码块身份：可直接照写示例

```tsl
rows := array(
    ("Code": "0001", "V1": 8.9, "V2": 12),
    ("Code": "0002", "V1": 9, "V2": 10),
    ("Code": "0003", "V1": 8, "V2": 8),
    ("Code": "0004", "V1": 1, "V2": 2)
);
code_arr := array("0001", "0003");
keep_index := filterIn(rows, code_arr, "Code", false);
drop_index := filterNotIn(rows, code_arr, "Code", false);
```

结果说明：

- `filterIn(rows, code_arr, "Code", false)` 返回 `array(0, 2)`
- `filterNotIn(rows, code_arr, "Code", false)` 返回 `array(1, 3)`

这些下标可以直接继续拿去切子结果集：

代码块身份：可直接照写示例

```tsl
rows := array(
    ("Code": "0001", "V1": 8.9, "V2": 12),
    ("Code": "0002", "V1": 9, "V2": 10),
    ("Code": "0003", "V1": 8, "V2": 8),
    ("Code": "0004", "V1": 1, "V2": 2)
);
code_arr := array("0001", "0003");
keep_index := filterIn(rows, code_arr, "Code", false);
picked := rows[keep_index, array("Code", "V1")];
```

### `Field = nil` 时按整行过滤

整行过滤与一维数组过滤都走 `Field = nil`：

代码块身份：可直接照写示例

```tsl
rows := array(
    ("A": 1, "B": 2, "C": 3),
    ("A": 2, "B": 3, "C": 4),
    ("A": 1, "B": 2, "C": 3),
    ("A": 4, "B": 5, "C": 6),
    ("A": 4, "B": 5, "C": 6)
);
filter_rows := array(
    ("A": 1, "B": 2, "C": 3),
    ("A": 2, "B": 3, "C": 4)
);
keep_rows := filterIn(rows, filter_rows, nil);
left_only := filterNotIn(array(1, 2, 3, 4, 5, 5, 6, 7), array(1, 2, 3, 4), nil);
```

结果说明：

- `filterIn(rows, filter_rows, nil)` 返回三行：`(1,2,3)`、`(2,3,4)`、`(1,2,3)`
- 这里第一行和第三行都被保留下来，说明过滤不会自动去重
- `filterNotIn(array(1, 2, 3, 4, 5, 5, 6, 7), array(1, 2, 3, 4), nil)` 返回 `array(5, 5, 6, 7)`

### 按字段列表过滤

如果只想按部分列判断是否命中，把 `Field` 写成字段名数组：

代码块身份：可直接照写示例

```tsl
rows := array(
    ("A": 1, "B": 2, "C": 3),
    ("A": 2, "B": 3, "C": 4),
    ("A": 1, "B": 2, "C": 5),
    ("A": 4, "B": 5, "C": 6)
);
filter_rows := array(
    ("A": 1, "B": 2),
    ("A": 2, "B": 3)
);
keep_rows := filterIn(rows, filter_rows, array("A", "B"));
keep_index := filterIn(rows, filter_rows, array("A", "B"), false);
sub_result := rows[keep_index, array("A", "C")];
```

结果说明：

- `filterIn(rows, filter_rows, array("A", "B"))` 返回三行：`(1,2,3)`、`(2,3,4)`、`(1,2,5)`
- `filterIn(rows, filter_rows, array("A", "B"), false)` 返回 `array(0, 1, 2)`
- `rows[keep_index, array("A", "C")]` 返回三行：`(1,3)`、`(2,4)`、`(1,5)`

## 和集合运算的区别

- 过滤运算按“原结果集中的每一行是否命中”来决定保留或排除。
- 集合运算按“行集合”来做并、交、差；结果默认会折叠重复行。
- 需要保留重复记录时，优先看这一篇。
- 需要去重后的并集、交集、差集时，优先看 [12_matrix_and_collections.md](12_matrix_and_collections.md)。

## 默认生成模板

结果集过滤的默认骨架如下：

代码块身份：可直接照写示例

```tsl
rows := array(
    ("Code": "0001"),
    ("Code": "0002")
);
code_arr := array("0001");
keep_rows := filterIn(rows, code_arr, "Code");
```

## 禁止项

- 把 `filterIn(...)` 当成去重工具。
- 过滤整行时忘了把 `Field` 写成 `nil`。
- 需要返回下标时漏掉第四个参数 `false`。
- 已经只按部分列过滤，却仍然把 `Field` 写成单个字符串。
- 把过滤运算和 `union2` / `intersect` 这类集合运算混为一谈。

代码块身份：反例 / 不可照写

```text
keep_rows := filterIn(rows, filter_rows, "A");
```

上面这种写法只会按单列 `"A"` 判断，不会同时比较 `A` 和 `B`；如果需求是“按多个字段联合过滤”，应改成 `array("A", "B")`。

代码块身份：反例 / 不可照写

```text
keep_rows := filterIn(rows, filter_rows);
```

不要把 `Field` 省略当成“默认按整行过滤”。本页只把显式写出 `nil` 作为可靠规则。
