Files
ProcessCodeConverter/src/utils/helper.array.ts

117 lines
3.4 KiB
TypeScript

import { AssignByPath, Resolve, type Paths, type PathValue } from "./helper.lang";
/**
* 将对象数组按指定字段分组,并返回分组结果的数组。
*
* @template T - 输入数组中对象的类型。
* @param data - 要分组的对象数组。
* @param fields - 用于分组对象的键(字段)数组。
* @param sort - 一个布尔值,指示是否按键对分组结果进行排序。默认为 `true`。
* @param separator - 用于连接字段值以形成分组键的字符串。默认为 `'-'`。
* @returns 分组结果的数组
*
* @example
* ```typescript
* const data = [
* { category: 'A', type: 'X', value: 10 },
* { category: 'A', type: 'Y', value: 20 },
* { category: 'B', type: 'X', value: 30 },
* ];
* const grouped = GroupBy(data, ['category', 'type']);
* console.log(grouped);
* // 输出:
* // [
* // {
* // key: 'A-X',
* // keyList: ['A', 'X'],
* // value: [{ category: 'A', type: 'X', value: 10 }],
* // keyObj: { category: 'A', type: 'X' }
* // },
* // {
* // key: 'A-Y',
* // keyList: ['A', 'Y'],
* // value: [{ category: 'A', type: 'Y', value: 20 }],
* // keyObj: { category: 'A', type: 'Y' }
* // },
* // {
* // key: 'B-X',
* // keyList: ['B', 'X'],
* // value: [{ category: 'B', type: 'X', value: 30 }],
* // keyObj: { category: 'B', type: 'X' }
* // }
* // ]
* ```
*/
export function GroupBy<T extends object, TKeys extends (Paths<T>)[]>(
data: T[],
fields: TKeys,
sort = true,
separator = '-',
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const groupList = {} as any;
const groupArray = [] as IGrouping<T, { [Key in TKeys[number]]: PathValue<T, Key> }>[];
if (fields.length === 0) return [];
for (const item of data) {
const keyList = fields.map((field) => Resolve(item, field));
const key = keyList.join(separator);
groupList[key] = groupList[key] || [];
groupList[key].push(item);
groupList[key].keyList = keyList;
const keyObj = {} as T;
fields.forEach((t) => {
AssignByPath(keyObj, t, Resolve(item, t) as PathValue<T, typeof t>);
});
groupList[key].keyObj = keyObj;
}
for (const key in groupList) {
groupArray.push({
key,
keyList: groupList[key].keyList,
value: groupList[key],
keyObj: groupList[key].keyObj,
});
}
if (sort)
return groupArray.sort(function (a, b) {
return a.key.localeCompare(b.key, 'zh-CN', { numeric: true });
});
else return groupArray;
}
/**
* 将对象数组按指定字段分组,并返回分组结果的数组。
*/
export interface IGrouping<T, TKeyRecord> {
/**
* 分组的键,若有多个键,则用连接符连接
*/
key: string;
/**
* 分组的键列表
*/
keyList: unknown[];
/**
* 分组的对象数组
*/
value: T[];
/**
* 分组键对象,其属性名为分组键名,属性值为分组键
*/
keyObj: TKeyRecord;
}
declare global {
interface Array<T> {
GroupBy: <TKeys extends (Paths<T>)[]>(
fields: TKeys,
sort?: boolean,
separator?: string,
) => Array<IGrouping<T, { [Key in TKeys[number]]: PathValue<T, Key> }>>;
}
}
Array.prototype.GroupBy = function (fields, sort, separator) {
return GroupBy(this, fields, sort, separator);
};