Home Reference Source

src/base.js

export function typeOf(obj) {
  let objName = Object.prototype.toString.call(obj)
  objName = objName.slice(8, -1)
  if (objName === 'Number' && isNaN(obj)) {
    return 'NaN'
  }
  return objName
}

export function isObject(obj) {
  return typeOf(obj) === 'Object'
}

export function isArray(obj) {
  return typeOf(obj) === 'Array'
}

export function isString(obj) {
  return typeOf(obj) === 'String'
}

export function isNumber(obj) {
  return typeOf(obj * 1) === 'Number'
}

export function isFunction(obj) {
  return typeOf(obj) === 'Function'
}

export function isEmptyArray(arr) {
  assert(isArray(arr), `isEmptyArray(arr): expect arr to be type of Array, but got ${typeOf(arr)}`)

  return arr.length === 0
}

export function isEmptyObject(obj) {
  assert(isObject(obj), `isEmptyObject(obj): expect obj to be type of Array, but got ${typeOf(obj)}`)

  return Object.keys(obj).length === 0
}

export function isEmptyString(str) {
  assert(isString(str), `isEmptyString(str): expect str to be type of Array, but got ${typeOf(str)}`)

  return str === ''
}

export function isEmpty(any) {
  if (isUnset(any)) return true

  if (isArray(any) && isEmptyArray(any)) return true

  if (isObject(any) && isEmptyObject(any)) return true

  if (isString(any) && isEmptyString(any)) return true

  return false
}

/**
 * we assume Undefined or Null as unsetted value
 * @params {*} value - value to be check
 * @returns {Boolean} if typeOf value is Undefined or Null return false, else return true 
 */
export function isSet(value) {
  const type = typeOf(value)
  return type !== 'Undefined' && type !== 'Null'
}

/**
 * we assume Undefined or Null as unsetted value
 * @params {*} value - value to be check
 * @returns {Boolean} if typeOf value is Undefined or Null return true, else return false
 */
export function isUnset(value) {
  return !isSet(value)
}

/**
 * @desc check the format for the input email 
 * 
 * @params {String} emailAddress - a email address
 * @returns {Boolean} - true | false 
 */
export const isEmail = (emailAddress) => {
  let emailRE = /^(?:[-\w])+(?:\.[-\w]+)*@(?:[-\w])+(?:(?:\.[-\w]+)+)$/
  if (emailAddress && emailRE.test(emailAddress)) {
    return true
  }

  return false
}

/**
 * 
 * @param {Number|String} uint - unsigned int
 * @return (true | false)
 */
export function isUInt(uint) {
  return /^\d+$/.test(uint)
}

/**
 * 
 * @param {*} obj - any type in javascript
 * @return the copy of obj
 * 
 */
export function deepCopy(obj) {
  let type = typeOf(obj)
  let r
  switch (type) {
    case 'Object':
      r = {}
      Object.keys(obj)
        .forEach(key => {
          r[key] = deepCopy(obj[key])
        })
      break
    case 'Array':
      r = []
      obj.forEach((v, k) => {
        r[k] = deepCopy(v)
      })
      break
    default:
      r = obj
  }
  return r
}

/**
 * @desc a way to tell if two object hold the same value recursively.
 */
export function deepEqual(a, b) {
  if (a == b) return true

  let typeA = typeOf(a)
  let typeB = typeOf(b)

  if (typeA !== typeB) return false

  switch (typeA) {
    case 'Object':
      return deepEqualObject(a, b)
    case 'Array':
      return deepEqualArray(a, b)
    default:
      return false
  }
}

function deepEqualObject(a, b) {
  let aKeys = Object.keys(a)
  if (aKeys.length !== Object.keys(b).length) {
    return false
  }

  let r = true
  aKeys.forEach(key => {
    if (a[key] != b[key]) {
      let ta = typeOf(a[key])
      let tb = typeOf(b[key])
      if (ta !== tb) {
        r = false
      } else {
        switch (ta) {
          case 'Object':
            r = deepEqualObject(a[key], b[key])
            break
          case 'Array':
            r = deepEqualArray(a[key], b[key])
            break
          default:
            r = false
            break
        }
      }
    }
  })
  return r
}

function deepEqualArray(a, b) {
  if (a.length !== b.length) {
    return false
  }

  let r = true
  a.forEach((v, i) => {
    if (v != b[i]) {
      let ta = typeOf(v)
      let tb = typeOf(b[i])
      if (ta !== tb) {
        r = false
      } else {
        switch (ta) {
          case 'Object':
            r = deepEqualObject(v, b[i])
            break
          case 'Array':
            r = deepEqualArray(v, b[i])
            break
          default:
            r = false
            break
        }
      }
    }
  })

  return r
}

export function shallowEqual(a, b) {
  if (a == b) return true

  let typeA = typeOf(a)
  let typeB = typeOf(b)

  if (typeA !== typeB) return false

  switch (typeA) {
    case 'Object':
      return shallowEqualObject(a, b)
    case 'Array':
      return shallowEqualArray(a, b)
    default:
      return false
  }
}

function shallowEqualObject(a, b) {
  let aKeys = Object.keys(a)
  if (aKeys.length !== Object.keys(b).length) {
    return false
  }

  let r = true
  aKeys.forEach(key => {
    if (a[key] != b[key]) {
      r = false
    }
  })
  return r
}

function shallowEqualArray(a, b) {
  if (a.length !== b.length) {
    return false
  }

  let r = true
  a.forEach((v, i) => {
    if (v != b[i]) {
      r = false
    }
  })

  return r
}

export function assert(condition, message) {
  const assertion = isFunction(condition) ? !!condition() : !!condition
  if (!assertion) {
    throw new Error(message)
  }
}