UNPKG

@msom/common

Version:

@msom/common

251 lines (228 loc) 6.59 kB
import { tryCall } from "../global"; export function isArray<T>(o: unknown): o is Array<T> { return Array.isArray(o); } /** * 基础类型的键类型 */ type PrimitiveKey = string | number | boolean | null | undefined; /** * 用于存储元素计数的接口 */ interface CountMap { count: number; } /** * 用于存储对象元素的接口 */ interface ObjectCountMap extends CountMap { obj: object; } /** * 用于存储不同类型元素的映射集合 */ interface TypeMaps { primitiveMap: Map<PrimitiveKey, CountMap>; objectMap: Map<symbol, ObjectCountMap>; } /** * 比较新旧数组,找出所有新增和删除的元素 * @param newArr 新数组 * @param oldArr 旧数组 * @param insert 处理新增元素的回调函数 * @param del 处理删除元素的回调函数 */ export function compareArray<T>( newArr: T[], oldArr: T[], insert?: (items: T[]) => void, del?: (items: T[]) => void ) { // 用于存储对象到唯一标识符的映射 const objectKey = new WeakMap<object, symbol>(); // 初始化旧数组的映射集合 const oldMaps: TypeMaps = { primitiveMap: new Map(), objectMap: new Map(), }; // 初始化新数组的映射集合 const newMaps: TypeMaps = { primitiveMap: new Map(), objectMap: new Map(), }; /** * 将元素添加到映射集合中 * @param maps 目标映射集合 * @param key 要添加的元素 */ const addMap = (maps: TypeMaps, key: T) => { if (key === null || typeof key === "object" || typeof key === "function") { // 处理对象类型 const objKey = key as object; // 获取或创建对象的唯一标识符 const keySymbol = objectKey.get(objKey) || createAndSetSymbol(objKey); // 获取或创建计数对象 const value = maps.objectMap.get(keySymbol) || createObjectMapEntry(maps, keySymbol, objKey); // 增加计数 value.count++; } else { // 处理基本类型 const primitiveKey = key as PrimitiveKey; // 获取或创建计数对象 const value = maps.primitiveMap.get(primitiveKey) || createMapEntry(maps.primitiveMap, primitiveKey); // 增加计数 value.count++; } }; /** * 为对象创建并设置唯一标识符 * @param key 对象 * @returns 创建的唯一标识符 */ const createAndSetSymbol = (key: object): symbol => { const keySymbol = Symbol(); objectKey.set(key, keySymbol); return keySymbol; }; /** * 创建对象类型的计数条目 * @param maps 映射集合 * @param keySymbol 对象的唯一标识符 * @param obj 对象本身 * @returns 创建的计数对象 */ const createObjectMapEntry = ( maps: TypeMaps, keySymbol: symbol, obj: object ): ObjectCountMap => { const value = { count: 0, obj }; maps.objectMap.set(keySymbol, value); return value; }; /** * 创建基本类型的计数条目 * @param map 目标映射 * @param key 基本类型值 * @returns 创建的计数对象 */ const createMapEntry = ( map: Map<PrimitiveKey, CountMap>, key: PrimitiveKey ): CountMap => { const value = { count: 0 }; map.set(key, value); return value; }; // 构建新旧数组的映射 oldArr.forEach((item) => addMap(oldMaps, item)); newArr.forEach((item) => addMap(newMaps, item)); // 存储新增和删除的元素 const inserts: T[] = []; const dels: T[] = []; // 比较基本类型元素 compareMapEntries(oldMaps.primitiveMap, newMaps.primitiveMap, inserts, dels); // 比较对象类型元素 compareObjectMapEntries( oldMaps.objectMap, newMaps.objectMap, objectKey, inserts, dels ); // 调用回调函数处理变更 if (inserts.length > 0 && insert) { tryCall(insert, [inserts]); } if (dels.length > 0 && del) { tryCall(del, [dels]); } } /** * 比较基本类型元素的映射 * @param oldMap 旧映射 * @param newMap 新映射 * @param inserts 存储新增元素的数组 * @param dels 存储删除元素的数组 */ export function compareMapEntries<T>( oldMap: Map<PrimitiveKey, CountMap>, newMap: Map<PrimitiveKey, CountMap>, inserts: T[], dels: T[] ) { // 遍历旧映射,比较元素数量变化 oldMap.forEach((value, key) => { const newValue = newMap.get(key); updateArrays(key as T, value.count, newValue?.count || 0, inserts, dels); // 处理完后从新映射中删除,剩余的就是新增的 newMap.delete(key); }); // 处理纯新增的元素 newMap.forEach((value, key) => { inserts.push(...new Array(value.count).fill(key as T)); }); } /** * 比较对象类型元素的映射 * @param oldMap 旧映射 * @param newMap 新映射 * @param objectKey 对象到唯一标识符的映射 * @param inserts 存储新增元素的数组 * @param dels 存储删除元素的数组 */ export function compareObjectMapEntries<T>( oldMap: Map<symbol, ObjectCountMap>, newMap: Map<symbol, ObjectCountMap>, objectKey: WeakMap<object, symbol>, inserts: T[], dels: T[] ) { // 遍历旧映射,比较元素数量变化 oldMap.forEach((value) => { const keySymbol = objectKey.get(value.obj); if (keySymbol) { const newValue = newMap.get(keySymbol); updateArrays( value.obj as T, value.count, newValue?.count || 0, inserts, dels ); // 处理完后从新映射中删除,剩余的就是新增的 newMap.delete(keySymbol); } }); // 处理纯新增的元素 newMap.forEach((value) => { inserts.push(...new Array(value.count).fill(value.obj as T)); }); } /** * 根据元素数量变化更新新增和删除数组 * @param item 要处理的元素 * @param oldCount 旧数量 * @param newCount 新数量 * @param inserts 存储新增元素的数组 * @param dels 存储删除元素的数组 */ export function updateArrays<T>( item: T, oldCount: number, newCount: number, inserts: T[], dels: T[] ) { if (newCount < oldCount) { // 数量减少,添加到删除数组 dels.push(...new Array(oldCount - newCount).fill(item)); } else if (newCount > oldCount) { // 数量增加,添加到新增数组 inserts.push(...new Array(newCount - oldCount).fill(item)); } }