basantech89/awesome-ui

View on GitHub
src/utils/object.ts

Summary

Maintainability
A
35 mins
Test Coverage
const isObject = (obj: any) => {
  if (typeof obj === 'object' && obj !== null) {
    if (typeof Object.getPrototypeOf === 'function') {
      const prototype = Object.getPrototypeOf(obj)
      return prototype === Object.prototype || prototype === null
    }

    return Object.prototype.toString.call(obj) === '[object Object]'
  }

  return false
}

type TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void
  ? I
  : never

export const deepMerge = <T extends Record<string, any>[]>(
  ...objects: T
): TUnionToIntersection<T[number]> =>
  objects.reduce((result, current) => {
    Object.keys(current).forEach(key => {
      if (Array.isArray(result[key]) && Array.isArray(current[key])) {
        result[key] = Array.from(new Set(result[key].concat(current[key])))
      } else if (isObject(result[key]) && isObject(current[key])) {
        result[key] = deepMerge(result[key], current[key])
      } else {
        result[key] = current[key]
      }
    })

    return result
  }, {}) as any

// export const deepMerge: DeepMerge = <
//   T extends Record<string, unknown>,
//   S extends Record<string, unknown>
// >(
//   target: T,
//   ...sources: S[]
// ) => {
//   if (!sources.length) {
//     return target
//   }
//
//   if (isObject(target)) {
//     // making sure to not change target (immutable)
//     const output = { ...target }
//     sources.forEach(source => {
//       if (isObject(source)) {
//         Object.keys(source).forEach(key => {
//           if (isObject(source[key])) {
//             if (!output[key]) {
//               output[key] = { ...(source[key] as Object) }
//             } else {
//               output[key] = deepMerge(output[key], source[key])
//             }
//           } else if (!isFaulty(source[key])) {
//             output[key] = source[key]
//           }
//         })
//       }
//     })
//     return output
//   }
// }

type Get = (obj: Record<string, any>, path: string | number, fallback?: any) => any

export const get: Get = (obj, path, fallback) => {
  const keys: Array<string | number> = typeof path === 'string' ? path.split('.') : [path]

  if (!obj) {
    return fallback ?? null
  }

  return keys.reduce((value, key) => value[key] ?? fallback ?? null, obj)
}

export const memoize = (fn: Get): Get => {
  const cache = new WeakMap()

  return (obj, path, fallback) => {
    if (!obj) {
      return fn(obj, path, fallback)
    }

    if (!cache.has(obj)) {
      cache.set(obj, new Map())
    }

    const map = cache.get(obj)

    if (map.has(path)) {
      return map.get(path)
    }

    const value = fn(obj, path, fallback)

    map.set(path, value)

    return value
  }
}

export const memoizedGet = memoize(get)

export { default as omit } from 'lodash/omit'
export { default as pick } from 'lodash/pick'