Skip to content

对象

将对象数组转换为矩阵

js
function isObject(value) {
  const type = typeof value;
  return (type === 'object' && value !== null) || type === 'function';
}

function jsonToMatrix(arr) {
  const m = arr.length;
  const map = new Map();
  const dfs = (value, row, name) => {
    if (!isObject(value)) {
      let data = map.get(name);
      if (!data) {
        map.set(name, (data = new Array(m).fill('')));
      }
      data[row] = value;
      return;
    }
    if (Array.isArray(value)) {
      for (let i = 0; i < value.length; ++i) {
        dfs(value[i], row, name ? `${name}.${i}` : `${i}`);
      }
      return;
    }
    for (const key of Object.keys(value)) {
      dfs(value[key], row, name ? `${name}.${key}` : `${key}`);
    }
  };
  for (let i = 0; i < m; ++i) {
    dfs(arr[i], i);
  }
  const pairs = Array.from(map).sort((a, b) => a[0].localeCompare(b[0]));
  const n = pairs.length;
  const res = new Array(m + 1).fill(0).map(() => new Array(n));
  for (let j = 0; j < n; ++j) {
    res[0][j] = pairs[j][0];
  }
  for (let i = 1; i < m + 1; ++i) {
    for (let j = 0; j < n; ++j) {
      res[i][j] = pairs[j][1][i - 1];
    }
  }
  return res;
}

将 JSON 字符串转换为对象

js
const primitiveMap = {
  null: null,
  true: true,
  false: false,
};

function jsonPrimitive(str) {
  if (primitiveMap[str] !== undefined) {
    return primitiveMap[str];
  }
  if (!isNaN(Number(str))) {
    return Number(str);
  }
  return str.slice(1, -1);
}

function splitByComma(str) {
  const result = [];
  const { length } = str;
  let count = 0;
  let lastComma = 0;
  let inStr = false;
  for (let i = 0; i < length; ++i) {
    const c = str[i];
    if (
      (c === ',' || c === '}' || c === ']') &&
      count === 1 &&
      !inStr &&
      i - lastComma > 1
    ) {
      result.push(str.slice(lastComma + 1, i));
      lastComma = i;
    }
    if (c === '{' || c === '[') {
      count += 1;
    }
    if (c === '}' || c === ']') {
      count -= 1;
    }
    if (c === '"') {
      inStr = !inStr;
    }
  }
  return result;
}

function jsonArray(str) {
  return splitByComma(str).map(jsonParse);
}

function jsonObject(str) {
  const result = {};
  const props = splitByComma(str);
  const regexp = /^"(?<key>.+?)":(?<value>.+)$/;
  props.forEach(prop => {
    const match = prop.match(regexp);
    const key = match[1];
    const value = match[2];
    result[key] = jsonParse(value);
  });
  return result;
}

function jsonParse(str) {
  if (str[0] === '{') {
    return jsonObject(str);
  }
  if (str[0] === '[') {
    return jsonArray(str);
  }
  return jsonPrimitive(str);
}

检查是否是类的对象实例

js
function isObject(value) {
  const type = typeof value;
  return (type === 'object' && type !== null) || type === 'function';
}

const primitiveClassFunction = {
  number: Number,
  string: String,
  boolean: Boolean,
  bigint: BigInt,
  symbol: Symbol,
};

function checkPrimitiveInstanceOf(value, classFunction) {
  const Ctor = primitiveClassFunction[typeof value];
  return (
    Ctor !== undefined &&
    (Ctor === classFunction || classFunction === Object)
  );
}

function checkIfInstanceOf(obj, classFunction) {
  if (typeof classFunction !== 'function') {
    return false;
  }
  if (!isObject(obj)) {
    return checkPrimitiveInstanceOf(obj, classFunction);
  }
  return obj instanceof classFunction;
}

完全相等的 JSON 字符串

js
const arrayTag = '[object Array]';
const objectTag = '[object Object]';

function getTag(value) {
  return Object.prototype.toString.call(value);
}

function areDeeplyEqual(o1, o2) {
  const tag1 = getTag(o1);
  const tag2 = getTag(o2);
  if (tag1 !== tag2) {
    return false;
  }
  if (![arrayTag, objectTag].includes(tag1)) {
    return o1 === o2;
  }
  if (tag1 === arrayTag) {
    return (
      o1.length === o2.length &&
      o1.every((_, i) => areDeeplyEqual(o1[i], o2[i]))
    );
  }
  const keys1 = Object.keys(o1);
  const keys2 = Object.keys(o2);
  return (
    keys1.length === keys2.length &&
    keys1.every(key => areDeeplyEqual(o1[key], o2[key]))
  );
}

将对象转换为 JSON 字符串

js
function isJSONObject(value) {
  return typeof value === 'object' && value !== null;
}

function jsonStringify(object) {
  if (typeof object === 'number' || typeof object === 'boolean') {
    return String(object);
  }
  if (typeof object === 'string') {
    return `"${object}"`;
  }
  if (!object) {
    return 'null';
  }
  if (Array.isArray(object)) {
    return `[${object.map(el => jsonStringify(el)).join(',')}]`;
  }
  const props = Object.keys(object).map(
    key => `"${key}":${jsonStringify(object[key])}`
  );
  return `{${props.join(',')}}`;
}

两个对象之间的差异

js
const arrayTag = '[object Array]';
const objectTag = '[object Object]';

function getTag(value) {
  return Object.prototype.toString.call(value);
}

function jsonValueDiff(val1, val2) {
  if (val1 === undefined || val2 === undefined) {
    return undefined;
  }
  const tag1 = getTag(val1);
  const tag2 = getTag(val2);
  if (tag1 !== tag2) {
    return [val1, val2];
  }
  if (![arrayTag, objectTag].includes(tag1)) {
    return val1 === val2 ? undefined : [val1, val2];
  }
  const result = {};
  if (tag1 === arrayTag) {
    val1.forEach((el, i) => {
      const diff = jsonValueDiff(val1[i], val2[i]);
      if (diff !== undefined) {
        result[i] = diff;
      }
    });
  }
  if (tag1 === objectTag) {
    Object.keys(val1).forEach(key => {
      const diff = jsonValueDiff(val1[key], val2[key]);
      if (diff !== undefined) {
        result[key] = diff;
      }
    });
  }
  return Object.keys(result).length ? result : undefined;
}

function objDiff(obj1, obj2) {
  return jsonValueDiff(obj1, obj2) ?? {};
}

精简对象

js
function isObject(value) {
  return (
    (typeof value === 'object' && value !== null) ||
    typeof value === 'function'
  );
}

function compactObject(obj) {
  if (!isObject(obj)) {
    return obj;
  }
  if (Array.isArray(obj)) {
    return obj.filter(Boolean).map(el => compactObject(el));
  }
  const result = {};
  Object.keys(obj).forEach(key => {
    const value = obj[key];
    if (value) {
      result[key] = compactObject(value);
    }
  });
  return result;
}

深度合并两个对象

js
const objectTag = '[object Object]';

function getTag(value) {
  return Object.prototype.toString.call(value);
}

function deepMerge(obj1, obj2) {
  if (getTag(obj1) === objectTag && getTag(obj2) === objectTag) {
    const result = {};
    Object.keys(obj1)
      .concat(Object.keys(obj2))
      .forEach(key => {
        if (obj1[key] !== undefined && obj2[key] !== undefined) {
          result[key] = deepMerge(obj1[key], obj2[key]);
        } else {
          result[key] = obj1[key] ?? obj2[key];
        }
      });
    return result;
  }
  if (Array.isArray(obj1) && Array.isArray(obj2)) {
    const len = Math.max(obj1.length, obj2.length);
    const result = new Array(len);
    for (let i = 0; i < len; ++i) {
      if (obj1[i] !== undefined && obj2[i] !== undefined) {
        result[i] = deepMerge(obj1[i], obj2[i]);
      } else {
        result[i] = obj1[i] ?? obj2[i];
      }
    }
    return result;
  }
  return obj2;
}

将 undefined 转为 null

js
function isJSONObject(value) {
  return typeof value === 'object' && value !== null;
}

function undefinedValueToNull(value) {
  if (value === undefined) {
    return null;
  }
  if (!isJSONObject(value)) {
    return value;
  }
  if (Array.isArray(value)) {
    return value.map(undefinedValueToNull);
  }
  const result = {};
  Object.keys(value).forEach(key => {
    result[key] = undefinedValueToNull(value[key]);
  });
  return result;
}

function undefinedToNull(obj) {
  return undefinedValueToNull(obj);
}

深度对象筛选

js
function isJSONObject(value) {
  return typeof value === 'object' && value !== null;
}

function deepFilterValue(value, fn) {
  if (!isJSONObject(value)) {
    return fn(value) ? value : undefined;
  }
  if (Array.isArray(value)) {
    const result = value
      .map(el => deepFilterValue(el, fn))
      .filter(el => el !== undefined);
    return result.length ? result : undefined;
  }
  const result = {};
  Object.keys(value).forEach(key => {
    const val = deepFilterValue(value[key], fn);
    if (val !== undefined) {
      result[key] = val;
    }
  });
  return Object.keys(result).length ? result : undefined;
}

function deepFilter(obj, fn) {
  return deepFilterValue(obj, fn);
}

使用方法链的计算器

js
class Calculator {
  result = undefined;

  constructor(value) {
    this.result = value;
  }

  add(value) {
    this.result += value;
    return this;
  }

  subtract(value) {
    this.result -= value;
    return this;
  }

  multiply(value) {
    this.result *= value;
    return this;
  }

  divide(value) {
    if (value === 0) {
      throw new Error('Division by zero is not allowed');
    }
    this.result /= value;
    return this;
  }

  power(value) {
    if (value === 0) {
      this.result = 1;
    } else {
      const base = this.result;
      for (let i = 1; i < Math.abs(value); ++i) {
        this.result *= base;
      }
      if (value < 0) {
        this.result = 1 / this.result;
      }
    }
    return this;
  }

  getResult() {
    return this.result;
  }
}

判断对象是否为空

js
function isEmpty(obj) {
  if (Array.isArray(obj)) {
    return obj.length === 0;
  }
  return Object.keys(obj).length === 0;
}

从两个数组中创建对象

js
function createObject(keysArr, valuesArr) {
  const result = {};
  const { length } = keysArr;
  for (let i = 0; i < length; ++i) {
    const key = String(keysArr[i]);
    const value = valuesArr[i];
    if (!(key in result)) {
      result[key] = value;
    }
  }
  return result;
}

对象反转

js
function invertObject(obj) {
  const result = {};
  Object.keys(obj).forEach(val => {
    const key = String(obj[val]);
    if (!(key in result)) {
      result[key] = val;
    } else if (!Array.isArray(result[key])) {
      result[key] = [result[key], val];
    } else {
      result[key].push(val);
    }
  });
  return result;
}