使⽤diff算法对⽐两个数据,获取两个数据的属性差异和属性差异状态因为使⽤的是ts,所以⾸先定义⼀下需要⽤到的⼀些类型
touch type.ts
// type.ts
export const isArray =<T extends Array<any>>(type:unknown):type is T=>typeof type==='object'&&Array.isArray(type)
export const isUndefined =(type:unknown):type is undefined=>type===undefined&&typeof type==='undefined'
export type UndefinedAble<T>=undefined|T
// 这是属性的状态
export enum CompareStatusEnum {
Create ='create',
Delete ='delete',
Update ='update',
None ='none'
}
export type CompareDataAttr<T=any>={[key in KeyType]:T}
// 属性对⽐的状态结果
export type AttrCompareStatus<T extends Record<string,unknown>= CompareDataAttr>={
type: CompareStatusEnum,
// 属性的属性的状态
attrStatus?:{[key in keyof T]?: AttrCompareStatus }
}
export type KeyType =string|number|symbol
进⾏diff对⽐
touch diffAttr.ts
// 导⼊类型⽂件
import{ isUndefined, isArray, UndefinedAble, AttrCompareStatus, CompareStatusEnum, KeyType }from'./type'
/**
* 根据属性状态修改pe
* @param status
*/
const updateStatusTypeByAttr =(status: AttrCompareStatus): AttrCompareStatus =>{
let type= pe
if(status.attrStatus){
// 如果全⼦节点状态⼀致,则是同种状态,如果状态不s⼀致,那就是 update
const keys = Object.keys(status.attrStatus)as Array<keyof typeof status.attrStatus>
if(!keys.some(it => status.attrStatus![it]=== status.attrStatus![0])){
type= CompareStatusEnum.Update
}
}
return status
}
/**
* 建⽴ key 与索引的缓存
* @param start
* @param end
* @param source
*/
const createIdxCache =(start:number, end:number, source: KeyType[]): Map<KeyType,number>=>{
const map: Map<KeyType,number>=new Map()
for(; start < end;++start){
Reflect.has(source, start)&& map.set(source[start], start)
Reflect.has(source, start)&& map.set(source[start], start)
}
return map
}typeof array
/**
* 获取两个值对⽐之后的状态
* @param originKey
* @param targetKey
* @param origin
* @param target
*/
export const sameValue =<T extends Record<KeyType,unknown>>(originKey: KeyType, targetKey: KeyType, origin:T, target:T): UndefinedAble<AttrCo mpareStatus>=>{
const originValue = (origin, originKey)
const targetValue = (target, targetKey)
if(originValue !== targetValue){
const status: AttrCompareStatus ={
type: CompareStatusEnum.None
}
if(typeof originValue ==='object'&&typeof targetValue ==='object'){
// 递归去⽐较⼦节点属性的状态
const{ attrStatus }=diffAttr(originValue, targetValue)
if(attrStatus){
if(!Reflect.has(status,'attrStatus')){
Reflect.set(status,'attrStatus',{})
}
Object.assign(status.attrStatus, attrStatus)
}else{
return undefined
}
}else{
}
return updateStatusTypeByAttr(status)
}
}
/
**
* 对象的属性对⽐
* @param origin
* @param target
*/
const diffObjAttr =(origin: Record<KeyType,unknown>, target: Record<KeyType,unknown>): AttrCompareStatus =>{
// Reflect.ownKeys 会拿到 get 属性的值
const originKeys: KeyType[]= Object.keys(origin)
const targetKeys: KeyType[]= Object.keys(target)
const filterIdx =new Set()
const status: AttrCompareStatus ={
type: CompareStatusEnum.None
}
let startOriginKeyIdx =0,
startOriginKey = originKeys[0],
endOriginKeyIdx = originKeys.length -1,
endOriginKey = originKeys[originKeys.length -1]
let startTargetKeyIdx =0,
startTargetKey = targetKeys[0],
endTargetKeyIdx = targetKeys.length -1,
endTargetKey = targetKeys[targetKeys.length -1]
let cacheIdMap: UndefinedAble<Map<KeyType,number>>
/
**
* ⽐较两个key是否相等
* @param originKey
* @param originKey
* @param targetKey
*/
const sameKey =(originKey: KeyType, targetKey: KeyType):boolean=> originKey === targetKey /**
* 统⼀修改状态
* @param key
* @param value
*/
const updateStatus =(key: KeyType, value: AttrCompareStatus):void=>{
if(!Reflect.has(status,'attrStatus')){
Reflect.set(status,'attrStatus',{})
}
// 添加状态
status.attrStatus![key]= value
}
/**
* 对⽐key,设置状态
* @param originKey
* @param targetKey
*/
const compare =(originKey: KeyType, targetKey: KeyType):void=>{
// 获取两个状态
const _status =sameValue(originKey, targetKey, origin, target)
_status &&updateStatus(originKey, _status)
}
while(startOriginKeyIdx <= endOriginKeyIdx && startTargetKeyIdx <= endTargetKeyIdx){
// 说明当前是查过后的。直接跳过
if(filterIdx.has(startTargetKey)){
startTargetKey = targetKeys[++startTargetKeyIdx]
}else if(filterIdx.has(endTargetKey)){
endTargetKey = targetKeys[--endTargetKeyIdx]
}else if(sameKey(startOriginKey, startTargetKey)){
compare(startOriginKey, startTargetKey)
startOriginKey = originKeys[++startOriginKeyIdx]
startTargetKey = targetKeys[++startTargetKeyIdx]
}else if(sameKey(endOriginKey, endTargetKey)){
compare(endOriginKey, endTargetKey)
endOriginKey = originKeys[--endOriginKeyIdx]
endTargetKey = targetKeys[--endTargetKeyIdx]
}else if(sameKey(endOriginKey, startTargetKey)){
compare(endOriginKey, startTargetKey)
endOriginKey = originKeys[--endOriginKeyIdx]
startTargetKey = targetKeys[++startTargetKeyIdx]
}else if(sameKey(startOriginKey, endTargetKey)){
compare(startOriginKey, endTargetKey)
startOriginKey = originKeys[++startOriginKeyIdx]
endTargetKey = targetKeys[--endTargetKeyIdx]
}else{
if(!cacheIdMap){
cacheIdMap =createIdxCache(startTargetKeyIdx, endTargetKeyIdx, targetKeys) }
const findIdx = (startOriginKey)
// 表⽰到了
if(!isUndefined(findIdx)){
// 进⾏对⽐。如果对⽐结果没有,则代表只是换了位置并没有更新值
compare(startOriginKey, targetKeys[findIdx])
// 添加过滤
filterIdx.add(startOriginKey)
}else{
// 不到表⽰为新增
updateStatus(startOriginKey,{type: CompareStatusEnum.Create })
}
startOriginKey = originKeys[++startOriginKeyIdx]
}
}
// target 遍历完成了。剩下的都是新增的
if(startOriginKeyIdx <= endOriginKeyIdx){
for(; startOriginKeyIdx <= endOriginKeyIdx;++startOriginKeyIdx)updateStatus(originKeys[startOriginKeyIdx],{type: CompareStatusEnum.Create }) }
// origin 遍历完成了。剩下的都是删除的
if(startTargetKeyIdx <= endTargetKeyIdx){
for(; startTargetKeyIdx <= endTargetKeyIdx;++startTargetKeyIdx)updateStatus(targetKeys[startTargetKeyIdx],{type: CompareStatusEnum.Delete } )
}
return updateStatusTypeByAttr(status)
}
/**
* 数组的属性对⽐。数组的每⼀项使⽤ diffAttr 对⽐
* @param origin
* @param target
*/
const diffArrayAttr =(origin:Array<any>, target:Array<any>): AttrCompareStatus =>{
let startOriginIdx =0,
startOrigin = origin[0]
const endOriginIdx = origin.length -1
let startTargetIdx =0,
startTarget = target[0]
const endTargetIdx = target.length -1
const status: AttrCompareStatus ={
type: CompareStatusEnum.None
}
const updateStatus =(key:number, value: AttrCompareStatus):void=>{
if(!Reflect.has(status,'attrStatus')){
Reflect.set(status,'attrStatus',{})
}
// 添加状态
status.attrStatus![key]= value
}
while(startOriginIdx <= endOriginIdx && startTargetIdx <= endTargetIdx){
const currentStatus =diffAttr(startOrigin, startTarget)
// 如果两个相同的话
pe=== CompareStatusEnum.Update || currentStatus.attrStatus){
updateStatus(startTargetIdx, currentStatus)
}
startOrigin = origin[++startOriginIdx]
startTarget = target[++startTargetIdx]
}
// target遍历完成,代表有新增的
if(startOriginIdx <= endOriginIdx){
for(; startOriginIdx <= endOriginIdx;++startOriginIdx)updateStatus(startOriginIdx,{type: CompareStatusEnum.Create })
}
// origin遍历完成,代表有删除的
if(startTargetIdx <= endTargetIdx){
// 这⾥代表着是修改的
for(; startTargetIdx <= endTargetIdx;++startTargetIdx)updateStatus(startTargetIdx,{type: CompareStatusEnum.Delete })
}
return updateStatusTypeByAttr(status)
}
}
/**
* 属性对⽐,区分数组/对象的对⽐。。
* @param origin new
* @param target old
*/
export const diffAttr =(origin:unknown, target:unknown): AttrCompareStatus =>{
if(typeof origin ==='object'&&typeof target ==='object'){
if(isArray(origin)&&isArray(target)){
return diffArrayAttr(origin, target)
}
return diffObjAttr(origin as Record<KeyType,unknown>, target as Record<KeyType,unknown>)
}else{
// 删除
if(!isUndefined(origin)&&isUndefined(target)){
return{
type: CompareStatusEnum.Delete
}
}else if(isUndefined(origin)&&!isUndefined(target)){// 新增
return{
type: CompareStatusEnum.Create
}
}
return{
type: origin === target ? CompareStatusEnum.None : CompareStatusEnum.Update
}
}
}
注意:当前进⾏的对⽐和状态获取都是基于左侧(origin) 为基准的,也就是说所有的状态都是基于左侧v 来做对⽐的,如:左侧(origin) 有,右侧(target) 没有,结果:该属性为删除。
使⽤
对象
const status =diffAttr({ a:1, c:3, e:4},{ b:2, c:3, e:5})
console.log(status)
数组
const status =diffAttr([{ a:1},{ b:2}],[{ b:2, a:1},{ a:11, b:3}])
console.log(status)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论