技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
fp-data-transforms【数据与研究】Everyday data transformations using functional patterns - arrays, objects, grouping, aggregation, and null-safe access
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: fp-data-transforms description: Everyday data transformations using functional patterns - arrays, objects, grouping, aggregation, and null-safe access risk: critical source: community date_added: "2026-09-04" version: 1.0.0 author: Claude tags:
This skill covers the data transformations you do every day: working with arrays, reshaping objects, normalizing API responses, grouping data, and safely accessing nested values. Each section shows the imperative approach first, then the functional equivalent, with honest assessments of when each approach shines.
Read the detailed guide before executing this skill. It retains the complete procedure and reference material. Treat its safety, prerequisites, and validation requirements as mandatory. For focused work, load the relevant sections; for end-to-end work, read the guide completely.
// API response
interface ApiOrder {
order_id: string;
customer: {
id: string;
full_name: string;
};
line_items: Array<{
product_id: string;
product_name: string;
qty: number;
unit_price: number;
}>;
order_date: string;
status: 'pending' | 'processing' | 'shipped' | 'delivered';
}
// What the UI needs
interface OrderSummary {
id: string;
customerName: string;
itemCount: number;
total: number;
formattedTotal: string;
date: string;
statusLabel: string;
statusColor: string;
}
// Transformation
const STATUS_CONFIG: Record<string, { label: string; color: string }> = {
pending: { label: 'Pending', color: 'yellow' },
processing: { label: 'Processing', color: 'blue' },
shipped: { label: 'Shipped', color: 'purple' },
delivered: { label: 'Delivered', color: 'green' },
};
const formatCurrency = (cents: number): string =>
`${(cents / 100).toFixed(2)}`;
const formatDate = (iso: string): string =>
new Date(iso).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
});
const toOrderSummary = (order: ApiOrder): OrderSummary => {
const total = order.line_items.reduce(
(sum, item) => sum + item.qty * item.unit_price,
0
);
const status = STATUS_CONFIG[order.status] ?? STATUS_CONFIG.pending;
return {
id: order.order_id,
customerName: order.customer.full_name,
itemCount: order.line_items.reduce((sum, item) => sum + item.qty, 0),
total,
formattedTotal: formatCurrency(total),
date: formatDate(order.order_date),
statusLabel: status.label,
statusColor: status.color,
};
};
// Transform all orders
const toOrderSummaries = (orders: ApiOrder[]): OrderSummary[] =>
orders.map(toOrderSummary);
interface AppSettings {
theme: {
mode: 'light' | 'dark' | 'system';
primaryColor: string;
fontSize: 'small' | 'medium' | 'large';
};
notifications: {
email: boolean;
push: boolean;
sms: boolean;
frequency: 'immediate' | 'daily' | 'weekly';
};
privacy: {
showProfile: boolean;
showActivity: boolean;
allowAnalytics: boolean;
};
}
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
const DEFAULT_SETTINGS: AppSettings = {
theme: {
mode: 'system',
primaryColor: '#007bff',
fontSize: 'medium',
},
notifications: {
email: true,
push: true,
sms: false,
frequency: 'immediate',
},
privacy: {
showProfile: true,
showActivity: true,
allowAnalytics: true,
},
};
const deepMergeSettings = (
defaults: AppSettings,
user: DeepPartial<AppSettings>
): AppSettings => ({
theme: { ...defaults.theme, ...user.theme },
notifications: { ...defaults.notifications, ...user.notifications },
privacy: { ...defaults.privacy, ...user.privacy },
});
// Usage
const userPreferences: DeepPartial<AppSettings> = {
theme: { mode: 'dark' },
notifications: { sms: true, frequency: 'daily' },
};
const finalSettings = deepMergeSettings(DEFAULT_SETTINGS, userPreferences);
interface Order {
id: string;
customerId: string;
customerName: string;
items: Array<{ name: string; price: number; quantity: number }>;
date: string;
}
interface CustomerOrderSummary {
customerId: string;
customerName: string;
orderCount: number;
totalSpent: number;
orders: Order[];
}
const calculateOrderTotal = (order: Order): number =>
order.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const groupOrdersByCustomer = (orders: Order[]): CustomerOrderSummary[] => {
const grouped = groupBy((order: Order) => order.customerId)(orders);
return Object.entries(grouped).map(([customerId, customerOrders]) => ({
customerId,
customerName: customerOrders[0].customerName,
orderCount: customerOrders.length,
totalSpent: customerOrders.reduce(
(sum, order) => sum + calculateOrderTotal(order),
0
),
orders: customerOrders,
}));
};
interface AppConfig {
services?: {
api?: {
endpoints?: {
users?: string;
orders?: string;
products?: string;
};
auth?: {
type?: 'bearer' | 'basic' | 'oauth';
token?: string;
};
};
database?: {
primary?: {
host?: string;
port?: number;
name?: string;
};
};
};
}
import * as O from 'fp-ts/Option';
import { pipe } from 'fp-ts/function';
// Create a type-safe config accessor
const getConfigValue = <T>(
config: AppConfig,
path: (config: AppConfig) => T | undefined,
defaultValue: T
): T => path(config) ?? defaultValue;
// Usage with optional chaining (simplest)
const apiUsersEndpoint = getConfigValue(
config,
c => c.services?.api?.endpoints?.users,
'/api/users'
);
// For more complex scenarios, use Option
const getEndpoint = (config: AppConfig, name: 'users' | 'orders' | 'products'): string =>
pipe(
O.fromNullable(config.services),
O.flatMap(s => O.fromNullable(s.api)),
O.flatMap(a => O.fromNullable(a.endpoints)),
O.flatMap(e => O.fromNullable(e[name])),
O.getOrElse(() => `/api/${name}`)
);
// Reusable pattern for multiple values
const getDbConfig = (config: AppConfig) => ({
host: config.services?.database?.primary?.host ?? 'localhost',
port: config.services?.database?.primary?.port ?? 5432,
name: config.services?.database?.primary?.name ?? 'app',
});
.map(), .filter(), .reduce() are perfectly goodobj?.prop?.value ?? default handles your null-safety needs// Native is fine here
const activeUserNames = users
.filter(u => u.isActive)
.map(u => u.name);
// fp-ts shines here
const result = pipe(
users,
A.findFirst(u => u.id === userId),
O.flatMap(u => O.fromNullable(u.profile)),
O.flatMap(p => O.fromNullable(p.settings)),
O.map(s => s.theme),
O.getOrElse(() => 'default')
);
groupBy, countBy, sumBy for your data// Custom utility pays off when used repeatedly
const revenueByRegion = sumBy(
(sale: Sale) => sale.region,
(sale: Sale) => sale.amount
)(sales);
arr.filter().map() creates one array, then anotherreduce: One pass through the data// If performance matters (and you've measured!)
const result = items.reduce((acc, item) => {
if (item.isActive) {
acc.push(item.name.toUpperCase());
}
return acc;
}, [] as string[]);
// vs the more readable (but 2-pass) version
const result = items
.filter(item => item.isActive)
.map(item => item.name.toUpperCase());