
关于
使用函数式模式进行日常数据转换——数组、对象、分组、聚合和空安全访问。
name: fp-data-transforms description: 使用函数式模式的日常数据转换——数组、对象、分组、聚合和空安全访问 risk: unknown source: community version: 1.0.0 author: Claude tags:
- functional-programming
- typescript
- data-transformation
- fp-ts
- arrays
- objects
- grouping
- aggregation
- null-safety
实用数据转换
此技能涵盖你每天做的数据转换:处理数组、重塑对象、规范化 API 响应、分组数据和安全访问嵌套值。每个部分先展示命令式方法,然后是函数式等价物,并诚实评估每种方法何时更优。
何时使用
- 你需要在 TypeScript 中转换数组、对象、分组数据或嵌套值。
- 任务涉及重塑 API 响应、空安全访问、聚合或规范化。
- 你想要日常数据工作的实用函数式模式而非底层循环。
1. 数组操作
数组操作是数据转换的基础。用表达性强、可链式调用的操作替代冗长的循环。
Map:转换每个元素
任务:将价格数组从分转换为元。
const pricesInCents = [999, 1499, 2999, 4999];
// Imperative
function convertToDollars(prices: number[]): number[] {
const result: number[] = [];
for (let i = 0; i < prices.length; i++) {
result.push(prices[i] / 100);
}
return result;
}
// Functional
const toDollars = (cents: number): number => cents / 100;
const dollars = pricesInCents.map(toDollars);
// [9.99, 14.99, 29.99, 49.99]
为什么函数式更好:意图立即清晰。map 表示"转换每个元素"。转换逻辑(toDollars)有命名且可复用。无索引管理,无手动数组构建。
Filter:保留匹配的
interface User {
id: string;
name: string;
isActive: boolean;
}
// Functional
const isActive = (user: User): boolean => user.isActive;
const activeUsers = users.filter(isActive);
Reduce:累积为新事物
interface CartItem {
name: string;
price: number;
quantity: number;
}
// Functional
const calculateTotal = (items: CartItem[]): number =>
items.reduce((sum, item) => sum + item.price * item.quantity, 0);
2. 对象转换
Pick/Omit:选择或排除字段
// Pick specific fields
const pick = <T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> =>
keys.reduce((acc, key) => ({ ...acc, [key]: obj[key] }), {} as Pick<T, K>);
// Omit specific fields
const omit = <T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> =>
Object.fromEntries(
Object.entries(obj).filter(([k]) => !keys.includes(k as K))
) as Omit<T, K>;
Rename Keys:重命名键
const renameKeys = <T extends Record<string, unknown>>(
obj: T,
mapping: Record<string, string>
): Record<string, unknown> =>
Object.fromEntries(
Object.entries(obj).map(([k, v]) => [mapping[k] ?? k, v])
);
3. 数据规范化
将 API 响应转换为应用内部格式:
interface ApiUser {
user_id: string;
first_name: string;
last_name: string;
email_address: string;
}
interface AppUser {
id: string;
name: string;
email: string;
}
const normalizeUser = (api: ApiUser): AppUser => ({
id: api.user_id,
name: `${api.first_name} ${api.last_name}`,
email: api.email_address,
});
const users = apiResponse.map(normalizeUser);
4. 分组和聚合
// Group by key
const groupBy = <T>(items: T[], key: keyof T): Record<string, T[]> =>
items.reduce((groups, item) => {
const k = String(item[key]);
return { ...groups, [k]: [...(groups[k] ?? []), item] };
}, {} as Record<string, T[]>);
// Count by key
const countBy = <T>(items: T[], key: keyof T): Record<string, number> =>
items.reduce((counts, item) => {
const k = String(item[key]);
return { ...counts, [k]: (counts[k] ?? 0) + 1 };
}, {} as Record<string, number>);
5. 空安全访问
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/function'
// Safe nested access
const getNestedValue = (obj: any, path: string[]): O.Option<unknown> =>
path.reduce(
(acc, key) => pipe(acc, O.flatMap(v => O.fromNullable((v as any)[key]))),
O.some(obj) as O.Option<unknown>
);
7. 何时使用什么
| 场景 | 推荐方法 | |------|----------| | 简单映射/过滤 | 原生 .map/.filter | | 复杂管道 | pipe + fp-ts | | 可能为 null 的值 | Option | | 可能失败的操作 | Either | | 简单聚合 | .reduce |
限制
- 仅在任务明确匹配上述范围时使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少所需的输入、权限、安全边界或成功标准,请停下来寻求澄清。
兼容工具
Claude CodeCursor
标签
后端开发
