优化多层级下拉选框标签生成函数:替换.map/.filter为.reduce
优化三级嵌套下拉选框的标签生成函数
我们需要优化一个生成三级嵌套下拉(Epic/PBI/Task)标签的函数,它会根据用户选择的层级返回包含已选标签与未选层级占位符的Labels对象。初始实现用了多次map()和filter(),已经尝试用reduce()优化,但希望进一步提升性能、可读性和类型安全性。
需求示例
- 仅选择Epic时,返回
{label: 'Epic123', placeholder: ', PBI, Task'} - 选择Epic和PBI时,返回
{label: 'Epic123, PBI987', placeholder: ', Task'} - 选择全部三级时,返回
{label: 'Epic123, PBI987, TaskXX', placeholder: undefined}
定义的接口
interface Labels { label: string | undefined; placeholder: string | undefined; }
现有实现分析
初始版本
初始版本通过两次map()+filter()分别处理标签和占位符,存在多次遍历数组的问题,性能上不够高效,且类型使用any存在隐患。
private getItemLabels(item?: any, options?: any[]): Labels | undefined { if (!item) { return undefined; } const mappedLabels = options?.map(option => { const value = item[option.key]; return { label: value ? value.label : undefined, placeholder: value ? undefined : option.placeholder, }; }); const label = mappedLabels ?.map(l => l.label) .filter(l => l) .join(', '); const placeholder = mappedLabels ?.map(l => l.placeholder) .filter(l => l) .join(', '); return { label, placeholder: placeholder ? `, ${placeholder}` : undefined }; }
已尝试的reduce版本
这个版本用reduce()实现了一次遍历,但内部的join函数逻辑较复杂,且依然使用any类型,可读性和类型安全性有待提升。
private getItemLabels(item?: any, options?: any[]): Labels | undefined { if (!item) { return undefined; } const mappedLabels = options?.reduce<Labels>( (labels, currentOption) => { const value = item[currentOption.key]; const _label = value ? value.label : ''; const _placeholder = value ? '' : currentOption.placeholder; const join = (t1?: string, t2?: string) => (t1 ?? '') + (t1 && t2 ? ', ' : '') + (t2 ?? ''); return { label: join(labels.label, _label) || undefined, placeholder: join(labels.placeholder, _placeholder) || undefined, }; }, {} ); mappedLabels.placeholder = mappedLabels.placeholder ? `, ${mappedLabels.placeholder}` : undefined; return mappedLabels; }
优化后的实现
优化点
- 增强类型安全:定义
DropdownOption接口,替换any类型,避免类型隐患 - 一次遍历高效处理:通过
reduce()同时收集已选标签和未选占位符,仅遍历一次数组 - 逻辑清晰简洁:直接判断值是否存在,分别推入对应数组,后续用
join处理更直观 - 减少不必要的字符串拼接:先收集数组再统一拼接,比迭代中拼接更高效(小数据量差异不大,但更规范)
优化代码
// 先定义下拉选项的类型,替代any interface DropdownOption { key: string; placeholder: string; } private getItemLabels( item?: Record<string, { label?: string }>, options?: DropdownOption[] ): Labels | undefined { // 提前判断空值,避免后续处理 if (!item || !options?.length) { return undefined; } // 通过reduce一次遍历,同时收集标签和占位符数组 const { labels, placeholders } = options.reduce( (acc, option) => { const value = item[option.key]; // 存在有效标签则加入labels数组 if (value?.label) { acc.labels.push(value.label); } else { // 无有效值则加入占位符数组 acc.placeholders.push(option.placeholder); } return acc; }, { labels: [] as string[], placeholders: [] as string[] } ); return { // 标签数组为空则返回undefined,否则拼接 label: labels.length ? labels.join(', ') : undefined, // 占位符数组为空则返回undefined,否则前置逗号拼接 placeholder: placeholders.length ? `, ${placeholders.join(', ')}` : undefined }; }
效果验证
- 仅选Epic:
labels = ['Epic123'],placeholders = ['PBI', 'Task']→ 返回{label: 'Epic123', placeholder: ', PBI, Task'} - 选Epic+PBI:
labels = ['Epic123', 'PBI987'],placeholders = ['Task']→ 返回{label: 'Epic123, PBI987', placeholder: ', Task'} - 全选:
labels = ['Epic123', 'PBI987', 'TaskXX'],placeholders = []→ 返回{label: 'Epic123, PBI987, TaskXX', placeholder: undefined}
内容的提问来源于stack exchange,提问作者Ania
相关产品推荐
相关产品推荐

