使用js实现一个方法,判断对象一是否是对象二的子集

98 阅读2分钟

数据结构和运行的结果如下:

  const obj = {
    a: 0,
    c: '',
    d: true,
    e: {
      f: 1,
      h: {
        e: 0,
        f: 2,
      },
    },
    g: {
      f: 2,
      s: {
        e: 1,
      }
    }
  };


  console.log(checkIsChildObject({ a: 0 }, obj)); // true
  console.log(checkIsChildObject({ e: 0 }, obj)); // true
  console.log(checkIsChildObject({ a: 0, c: '' }, obj)); // true
  console.log(checkIsChildObject({ a: 0, e: 0 }, obj)); // false
  console.log(checkIsChildObject({ e: { f: 1 } }, obj)); // true
  console.log(checkIsChildObject({ e: { f: 2 } }, obj)); // false
  console.log(checkIsChildObject({ h: { e: 0, f: 2 } }, obj)); // true
  console.log(checkIsChildObject({ h: { f: 2, e: 0 } }, obj)); // true
  console.log(checkIsChildObject({ h: { f: 2 }, f: 2 }, obj)); // false
  console.log(checkIsChildObject({ g: { f: 2 }, c: '' }, obj)); // true
  console.log(checkIsChildObject({ g: { f: 2 }, e: 1 }, obj)); // false

实现思路

  1. 生成一个map对象
  2. 遍历目标对象,取第一次遍历时的元素匹配到的所有的parentId存储到parentIds中,对对象中后续属性的判断直接使用parentIds去map结构中获取对应的值,判断该值和目标值是否一致即可 注意:使用parentId的原因是:parentId主要用于同级比较时判断两个是否来自同一个parent,避免将第一级数据和第三级数据放在一起时误判为真
  /**
   * map对象存储结构形如:
   * a -> [{ value: 0, parentId: -1}]
   * e -> [{ value: { f: 1, ... }, parentId: -1}, { value: 0, parentId: 'h'}]
   * h -> [{ value: { e: 0, ... }, parentId: 'e'}}]
   * ...
   * f -> [{ value: 1, parentId: 'e'}, { value: 2, parentId: 'h'}, { value: 2, parentId: 'g']
   */
function checkIsChildObject(target, obj) {
  const map = new Map()
  /** 用来生成上述的map结构 */
  const handleMap = (obj, parentId = -1, level = 0) => {
    Object.entries(obj).forEach(([key, value]) => {
      if (map.has(key)) {
        let data = map.get(key)
        data.push({ value, parentId})
      } else {
        map.set(key, [{ value, parentId}])
      }
      if (typeof value === 'object') {
        handleMap(value, key, level + 1)
      }
    })
  }

  /** 简单的判断o1是否是o2的子对象(只比较一层)*/
  const checkObjIsChild = (o1, o2) => {
    return Object.entries(o1).every(([key, value]) => JSON.stringify(o2[key]) === JSON.stringify((value)))
  }

  /** 比较两个值是否一致 */
  const checkHasTarget = (value, val) => {
    let res = false
    if ((typeof value !== 'object' && value === val) || (typeof value === 'object' && checkObjIsChild(value, val))) {
      res = true
    }

    return res
  }

  handleMap(obj)

  let parentIds = [], targetArr = Object.entries(target)
  /** 遍历目标对象 */
  for (let index in targetArr) {
    const [key, value] = targetArr[index]
    if (!map.has(key)) {
      return false
    } else {
      /** 存储parentIds */
      if (index === '0') {
        map.get(key).forEach(({ value: val, parentId }) => {
          const res = checkHasTarget(value, val)
          if (res) {
            parentIds.push(parentId)
          }
        })
        if (!parentIds.length) {
          return false
        }
      } else {
        /** 取出parentIds为后续的属性做判断 */
        let res = false
        for (let parentId of parentIds) {
          const objRessArr = map.get(key).filter(item => item.parentId === parentId)
          for (let { value: val } of objRessArr) {
            const ress = checkHasTarget(value, val)
            /** 只要存在一个目标值说明就是符合要求的,直接break */
            if (ress) {
              res = true
              break
            }
          }
        }
        /** 没有找到目标值 */
        if (!res) {
          return false
        }
      }
    }
  }
  return true
}