From f4bb928626a78740a3c265af0ad84528ec6b24cd Mon Sep 17 00:00:00 2001 From: xjq Date: Sat, 19 Nov 2022 11:18:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20deepMerge=20?= =?UTF-8?q?=E9=A2=98=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- question/FrontEnd/deepMerge/answer.mjs | 37 +++++++++++++++++++ question/FrontEnd/deepMerge/index.md | 50 ++++++++++++++++++++++++++ question/FrontEnd/deepMerge/index.mjs | 11 ++++++ question/FrontEnd/deepMerge/test.mjs | 35 ++++++++++++++++++ server/src/qsdata/questions.json | 2 +- 5 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 question/FrontEnd/deepMerge/answer.mjs create mode 100644 question/FrontEnd/deepMerge/index.md create mode 100644 question/FrontEnd/deepMerge/index.mjs create mode 100644 question/FrontEnd/deepMerge/test.mjs diff --git a/question/FrontEnd/deepMerge/answer.mjs b/question/FrontEnd/deepMerge/answer.mjs new file mode 100644 index 0000000..6d276e0 --- /dev/null +++ b/question/FrontEnd/deepMerge/answer.mjs @@ -0,0 +1,37 @@ +function getType(o) { + return Object.prototype.toString.call(o); +} + +function deepClone(obj, weakMap = new WeakMap()) { + const type = Object.prototype.toString.call(obj); + if (!(type === '[object Object]' || type === '[object Array]')) return obj; + const o = type === '[object Object]' ? {} : []; + if (weakMap.get(obj)) return o; + weakMap.set(obj, true); + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + o[key] = deepClone(obj[key], weakMap); + } + } + return o; +} + +export default function deepMerge(a, b) { + if (getType(a) !== '[object Object]' && getType(a) !== '[object Array]') { + return deepClone(b); + } + + const isArray = getType(a) === '[object Array]'; + const res = isArray ? [] : {}; + for (const key in b) { + if (b.hasOwnProperty(key)) { + res[key] = deepMerge(a[key], b[key]); + } + } + for (const key in a) { + if (res[key] === undefined && a.hasOwnProperty(key)) { + res[key] = deepClone(a[key]); + } + } + return res; +} diff --git a/question/FrontEnd/deepMerge/index.md b/question/FrontEnd/deepMerge/index.md new file mode 100644 index 0000000..623a033 --- /dev/null +++ b/question/FrontEnd/deepMerge/index.md @@ -0,0 +1,50 @@ +对象合并 + +深度合并两个对象, 并且修改新对象不会影响原对象, 相同的属性则以 b 对象为主 + +用例 1: + +```js +const a = { a: 1 }; +const b = { b: 2 }; + +deepMerge(a, b); // { a: 1, b: 2 } +``` + +用例 2: + +```js +const a = { foo: { bar: 0 }, arr: [1, 3, { a: { b: 1 } }] }; +const b = { foo: { bar: 1 }, arr: [1, 2, { b: { a: 1 } }] }; + +deepMerge(a, b); // { foo: { bar: 1 }, arr: [1, 2, { a: { b: 1 }, b: { a: 1 } }] } +``` + +用例 3: + +b 属性值为 undefined 不会并入 a + +```js +const a = { a: 1 }; +const b = { a: undefined }; + +deepMerge(a, b); // { a: 1 } +``` + +用例 4: + +```js +const a = { a: 1 }; +const b = undefined; + +deepMerge(a, b); // { a: 1 } +``` + +用例 5: + +```js +const a = undefined; +const b = { a: 1 }; + +deepMerge(a, b); // { a: 1 } +``` diff --git a/question/FrontEnd/deepMerge/index.mjs b/question/FrontEnd/deepMerge/index.mjs new file mode 100644 index 0000000..2230ec0 --- /dev/null +++ b/question/FrontEnd/deepMerge/index.mjs @@ -0,0 +1,11 @@ +/** + * 对象合并 + * + * @export + * @param {Object} a + * @param {Object} b + * @return {Object} + */ +export default function deepMerge(a, b) { + return {}; +} diff --git a/question/FrontEnd/deepMerge/test.mjs b/question/FrontEnd/deepMerge/test.mjs new file mode 100644 index 0000000..e149e55 --- /dev/null +++ b/question/FrontEnd/deepMerge/test.mjs @@ -0,0 +1,35 @@ +import f from './index.mjs'; +import { it } from 'mocha'; +import { assert } from 'chai'; + +it('一层对象: 输入 { a: 1 }, { b: 2 }', () => { + assert.deepEqual(f({ a: 1 }, { b: 2 }), { a: 1, b: 2 }); +}); + +it('两层对象: 输入 { a: 1, b: { c: 2 } }, { b: 2 }', () => { + assert.deepEqual(f({ a: 1, b: { c: 2 } }, { b: { c: 3, d: 4 } }), { + a: 1, + b: { c: 3, d: 4 }, + }); +}); + +it("两层对象存在数组合并: 输入 { b: { c: [1, 3, { a: 1 }] } }, { b: { c: ['1', 2, { a: 2 }] } }", () => { + assert.deepEqual( + f({ b: { c: [1, 3, { a: 1 }] } }, { b: { c: ['1', 2, { a: 2 }] } }), + { b: { c: ['1', 2, { a: 2 }] } } + ); +}); + +it('b 中属性值为 undefined 时不会合并: 输入 { a: 1 }, { a: undefined }', () => { + assert.deepEqual(f({ a: 1 }, { a: undefined }), { a: 1 }); +}); + +it('边界输入一 undefined | null, { a: 1 }', () => { + assert.deepEqual(f(undefined, { a: 1 }), { a: 1 }); + assert.deepEqual(f(null, { a: 1 }), { a: 1 }); +}); + +it('边界输入二 { a: 1 }, undefined | null', () => { + assert.deepEqual(f({ a: 1 }, undefined), { a: 1 }); + assert.deepEqual(f({ a: 1 }, null), { a: 1 }); +}); diff --git a/server/src/qsdata/questions.json b/server/src/qsdata/questions.json index 97bb129..52ad870 100644 --- a/server/src/qsdata/questions.json +++ b/server/src/qsdata/questions.json @@ -1 +1 @@ -{"amountThousandthsFormat":{"answermd":"### 方法一\n\n正则表达式\n\n```js\nexport default function amountThousandthsFormat(amount) {\n return amount.replace(/(? {\n assert.deepEqual(f('314'), '314');\n});\n\nit('输入 31415926', () => {\n assert.deepEqual(f('31415926'), '31,415,926');\n});\n\nit('带小数位: 输入 31415926.62', () => {\n assert.deepEqual(f('31,415,926.62'), '31,415,926.62');\n});\n\nit('带长小数位: 输入 31415926.629514', () => {\n assert.deepEqual(f('31,415,926.629514'), '31,415,926.629514');\n});\n"},"curry":{"answermd":"### 方法一\n\n```js\nexport default function currying(fn) {\n let _args = [];\n return (...args) => {\n let result = null;\n if (args.length) {\n _args = _args.concat(args);\n } else {\n result = fn(..._args);\n }\n return result;\n };\n}\n```\n","answer":"export default function f(fn) {\n let _args = [];\n return (...args) => {\n let result = null;\n if (args.length) {\n _args = _args.concat(args);\n } else {\n result = fn(..._args);\n }\n return result;\n };\n}\n","introduce":"实现一个柯里化函数\n\n未传递参数时真正调用\n\n用例 1:\n\n```js\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\n\n// 输入 [add, [1], [2], [3], null]\n\n// 执行\nconst curryAdd = currying(add); // true\ncurryAdd(1); // null\ncurryAdd(2); // null\ncurryAdd(3); // null\ncurryAdd(); // 6\n\n// [true, null, null, null, 6]\n```\n\n用例 2:\n\n```js\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\n\n// 输入 [add, [1, 2, 1], null, null, null]\nconst curryAdd = currying(add); // true\ncurryAdd(1, 2, 1); // null\ncurryAdd(); // 4\ncurryAdd(); // 4\ncurryAdd(); // 4\n\n// [true, null, 4, 4, 4]\n```\n\n用例 3:\n\n```js\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\n\n// 输入 [add, [1, 2, 1], null, [1, 2], null]\nconst curryAdd = currying(add); // true\ncurryAdd(1, 2, 1); // null\ncurryAdd(); // 4\ncurryAdd(1, 2); // null\ncurryAdd(); // 7\n\n// [true, null, 4, null, 7]\n```\n\n用例 4:\n\n```js\nconst multi = (...args) => args.reduce((a, b) => a * b, 1);\n\n// 输入 [multi, [100], null, [1, 2], null]\nconst curryMulti = currying(multi);\ncurryMulti(100); // null\ncurryMulti(); // 100\ncurryMulti(1, 2); // null\ncurryMulti(); // 200\n// [true, null, 100, null, 200]\n```\n","desc":"实现一个柯里化函数","index":"/**\n * 柯里化函数\n *\n * @export\n * @param {Function} fn\n * @return {Function}\n */\nexport default function currying(fn) {\n return fn;\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\nconst multi = (...args) => args.reduce((a, b) => a * b, 1);\n\nfunction Received(Input) {\n let curry;\n const output = [];\n Input.forEach((arg) => {\n let result;\n if (!curry) {\n curry = f(arg);\n result = !!curry;\n } else if (arg) {\n result = curry(...arg);\n } else {\n result = curry();\n }\n output.push(result);\n });\n return output;\n}\n\nit('加法函数柯里化一: 输入 [add, [1], [2], [3], null]', () => {\n assert.deepEqual(Received([add, [1], [2], [3], null]), [\n true,\n null,\n null,\n null,\n 6,\n ]);\n});\n\nit('加法函数柯里化二: 输入 [add, [1, 2, 1], null, null, null]', () => {\n assert.deepEqual(Received([add, [1, 2, 1], null, null, null]), [\n true,\n null,\n 4,\n 4,\n 4,\n ]);\n});\n\nit('加法函数柯里化三: 输入 [add, [1, 2, 1], null, [1, 2], null]', () => {\n assert.deepEqual(Received([add, [1, 2, 1], null, [1, 2], null]), [\n true,\n null,\n 4,\n null,\n 7,\n ]);\n});\n\nit('乘法函数柯里化一: 输入 [multi, [100], null, [1, 2], null]', () => {\n assert.deepEqual(Received([multi, [100], null, [1, 2], null]), [\n true,\n null,\n 100,\n null,\n 200,\n ]);\n});\n"},"deepClone":{"answermd":"### 方法一\n\n递归拷贝, 使用 weakMap 过滤循环引用\n\n```js\nexport default function deepClone(obj, weakMap = new WeakMap()) {\n const type = Object.prototype.toString.call(obj);\n if (!(type === '[object Object]' || type === '[object Array]')) return obj;\n const o = type === '[object Object]' ? {} : [];\n if (weakMap.get(obj)) return o;\n weakMap.set(obj, true);\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n o[key] = deepClone(obj[key], weakMap);\n }\n }\n return o;\n}\n```\n","answer":"export default function deepClone(obj, weakMap = new WeakMap()) {\n const type = Object.prototype.toString.call(obj);\n if (!(type === '[object Object]' || type === '[object Array]')) return obj;\n const o = type === '[object Object]' ? {} : [];\n if (weakMap.get(obj)) return o;\n weakMap.set(obj, true);\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n o[key] = deepClone(obj[key], weakMap);\n }\n }\n return o;\n}\n","introduce":"实现对象深拷贝, 修改深拷贝对象不影响原对象\n\n用例 1:\n\n```js\nconst userList = { user: [{ name: 'xjq' }] };\nconst cloneUserList = deepClone(userList);\n\nconsole.log(cloneUserList); // { user: [{ name: 'xjq' }] }\ncloneUserList.user.push({ name: 'xjq1' });\nconsole.log(cloneUserList.user); // [{ name:'xjq' }]\n```\n\n用例 2:\n\n```js\n\nconst user = { name: 'xjq', height: 180 };\nconst cloneUser = deepClone(user);\n\nconsole.log(user); // { name: 'xjq', height: 180 }\n\ndelete cloneUser.height;\nconsole.log(cloneUser); // { name: 'xjq' }\nconsole.log(user; // { name: 'xjq', height: 180 }\n```\n","desc":"实现对象深拷贝, 修改深拷贝对象不影响原对象","index":"/**\n * 深拷贝\n *\n * @param {(Array|Object)} obj\n * @return {(Array|Object)}\n */\nexport default function deepClone(obj) {\n return {};\n}\n","test":"import _deepClone from './answer.mjs';\nimport f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('一层对象: 输入 { f: 1 }', () => {\n assert.deepEqual(f({ f: 2 }), { f: 2 });\n});\n\nit('两层对象: 输入 { a: 1, b: { c: 3 } }', () => {\n assert.deepEqual(f({ a: 1, b: { c: 3 } }), { a: 1, b: { c: 3 } });\n});\n\nit('数组: 输入 [1, { a: 1 }]', () => {\n assert.deepEqual(f([1, { a: 1 }]), [1, { a: 1 }]);\n});\n\nit('修改拷贝对象属性, 原对象应不受影响', () => {\n const Input = { b: { c: 1, d: 2 } };\n const cloneObj = f(Input);\n cloneObj.b.c = 2;\n assert.deepEqual(Input, {\n b: { c: 1, d: 2 },\n });\n});\n\nit('循环引用', () => {\n const circle1 = { foo: 1 };\n const circle2 = { bar: 2, c1: circle1 };\n circle1.c2 = circle2;\n assert.deepEqual(f(circle1), { foo: 1, c2: { bar: 2, c1: {} } });\n});\n"},"flatten":{"answermd":"### 方法一\n\n```js\nexport default function flatten(arr) {\n return arr.reduce((acc, cur) => {\n if (Array.isArray(cur)) {\n acc = acc.concat(flatten(cur));\n } else {\n acc.push(cur);\n }\n return acc;\n }, []);\n}\n```\n","answer":"export default function flatten(arr) {\n return arr.reduce((acc, cur) => {\n if (Array.isArray(cur)) {\n acc = acc.concat(flatten(cur));\n } else {\n acc.push(cur);\n }\n return acc;\n }, []);\n}\n","introduce":"实现数组扁平化方法\n\n用例 1:\n\n```js\nflatten([1, 2, 3]); // [1, 2, 3]\n```\n\n用例 2:\n\n```js\nflatten([1, [2, 3]]); // [1, 2, 3]\n```\n\n用例 3:\n\n```js\nflatten([1, [[2, 2], [3, [4]], 5]]); // [1, 2, 2, 3, 4, 5];\n```\n","desc":"实现数组扁平化方法","index":"/**\n * 数组扁平化\n *\n * @param {number[]} arr\n * @return {number[]}\n */\nexport default function flatten(arr) {\n return [];\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('一层数组: 输入 [1, 2, 3]', () => {\n assert.deepEqual(f([1, 2, 3]), [1, 2, 3]);\n});\n\nit('两层数组: 输入 [1, [2, 3]]', () => {\n assert.deepEqual(f([1, [2, 3]]), [1, 2, 3]);\n});\n\nit('四层数组: 输入 [1, [[2, 2], [3, [4]], 5]]', () => {\n assert.deepEqual(f([1, [[2, 2], [3, [4]], 5]]), [1, 2, 2, 3, 4, 5]);\n});\n"},"sort":{"answermd":"### 方法一\n\n原生 api sort 方法\n\n```js\nexport default function sort(arr) {\n // js api\n return arr.sort();\n}\n```\n","answer":"export default function f(arr) {\n // js api\n return arr.sort();\n}\n","introduce":"将数组按从小到大的顺序排序\n\n输入为 数字数组\n\n- 冒泡排序\n- 选择排序\n- 插入排序\n- 归并排序\n- 堆排序\n- 快速排序\n\n...\n\n用例 1:\n\n```js\nconst arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];\nconst sortArr = sort(arr);\n\nconsole.log(sortArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n用例 2:\n\n```js\nconst arr = [1, 9, 2, 4, 3, 7, 6, 8, 6];\nconst sortArr = sort(arr);\n\nconsole.log(sortArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n用例 3:\n\n```js\nconst arr = [1, 1, 1, 3, 4, 5, 7, 6, 6, 6, 0, 0, -1];\nconst sortArr = sort(arr);\n\nconsole.log(sortArr); // [-1, 0, 0, 1, 1, 1, 3, 4, 5, 6, 6, 6, 7]\n```\n","desc":"将数组按从小到大的顺序排序","index":"/**\n * 排序函数\n *\n * @export\n * @param {number[]} arr\n * @return {number[]}\n */\nexport default function sort(arr) {\n return [];\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('空数组: 输入 []', () => {\n assert.deepEqual(f([]), []);\n});\n\nit('已排序好的数组: 输入 [1, 2, 3, 4, 5, 6, 7, 8, 9]', () => {\n assert.deepEqual(f([1, 2, 3, 4, 5, 6, 7, 8, 9]), [1, 2, 3, 4, 5, 6, 7, 8, 9]);\n});\n\nit('乱序数组一: 输入 [1, 9, 2, 4, 3, 7, 6, 8, 5]', () => {\n assert.deepEqual(f([1, 9, 2, 4, 3, 7, 6, 8, 5]), [1, 2, 3, 4, 5, 6, 7, 8, 9]);\n});\n\nit('乱序数组二-包含负数: 输入 [1, 1, 1, 3, 4, 5, 7, 6, 6, 6, 0, 0, -1]', () => {\n assert.deepEqual(\n f([1, 1, 1, 3, 4, 5, 7, 6, 6, 6, 0, 0, -1]),\n [-1, 0, 0, 1, 1, 1, 3, 4, 5, 6, 6, 6, 7]\n );\n});\n"},"unique":{"answermd":"### 方法一\n\n利用 set 去除\n\n```js\nexport default function unique(arr) {\n return [...new Set(arr)];\n}\n```\n","answer":"export default function unique(arr) {\n return [...new Set(arr)];\n}\n","introduce":"数组去重\n\n用例 1:\n\n```js\nconst arr = [1, 2, 3];\nconst uniqueArr = unique(arr1);\n\nconsole.log(uniqueArr); // [1, 2, 3]\n```\n\n用例 2:\n\n```js\nconst arr = [1, 2, 3, 3, 3];\nconst uniqueArr = unique(arr);\n\nconsole.log(uniqueArr); // [1, 2, 3]\n```\n","desc":"数组去重","index":"/**\n * 数组去重\n *\n * @export\n * @param {number[]} arr\n * @return {number[]}\n */\nexport default function unique(arr) {\n return [];\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('无重复数字: 输入 [1, 2, 3]', () => {\n assert.deepEqual(f([1, 2, 3]), [1, 2, 3]);\n});\n\nit('单个重复数字: 输入 [1, 2, 3, 3]', () => {\n assert.deepEqual(f([1, 2, 3, 3]), [1, 2, 3]);\n});\n\nit('多个重复数字: 输入 [1, 1, 2, 2, 2, 3, 3]', () => {\n assert.deepEqual(f([1, 1, 2, 2, 2, 3, 3]), [1, 2, 3]);\n});\n"}} \ No newline at end of file +{"amountThousandthsFormat":{"answermd":"### 方法一\n\n正则表达式\n\n```js\nexport default function amountThousandthsFormat(amount) {\n return amount.replace(/(? {\n assert.deepEqual(f('314'), '314');\n});\n\nit('输入 31415926', () => {\n assert.deepEqual(f('31415926'), '31,415,926');\n});\n\nit('带小数位: 输入 31415926.62', () => {\n assert.deepEqual(f('31,415,926.62'), '31,415,926.62');\n});\n\nit('带长小数位: 输入 31415926.629514', () => {\n assert.deepEqual(f('31,415,926.629514'), '31,415,926.629514');\n});\n"},"curry":{"answermd":"### 方法一\n\n```js\nexport default function currying(fn) {\n let _args = [];\n return (...args) => {\n let result = null;\n if (args.length) {\n _args = _args.concat(args);\n } else {\n result = fn(..._args);\n }\n return result;\n };\n}\n```\n","answer":"export default function f(fn) {\n let _args = [];\n return (...args) => {\n let result = null;\n if (args.length) {\n _args = _args.concat(args);\n } else {\n result = fn(..._args);\n }\n return result;\n };\n}\n","introduce":"实现一个柯里化函数\n\n未传递参数时真正调用\n\n用例 1:\n\n```js\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\n\n// 输入 [add, [1], [2], [3], null]\n\n// 执行\nconst curryAdd = currying(add); // true\ncurryAdd(1); // null\ncurryAdd(2); // null\ncurryAdd(3); // null\ncurryAdd(); // 6\n\n// [true, null, null, null, 6]\n```\n\n用例 2:\n\n```js\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\n\n// 输入 [add, [1, 2, 1], null, null, null]\nconst curryAdd = currying(add); // true\ncurryAdd(1, 2, 1); // null\ncurryAdd(); // 4\ncurryAdd(); // 4\ncurryAdd(); // 4\n\n// [true, null, 4, 4, 4]\n```\n\n用例 3:\n\n```js\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\n\n// 输入 [add, [1, 2, 1], null, [1, 2], null]\nconst curryAdd = currying(add); // true\ncurryAdd(1, 2, 1); // null\ncurryAdd(); // 4\ncurryAdd(1, 2); // null\ncurryAdd(); // 7\n\n// [true, null, 4, null, 7]\n```\n\n用例 4:\n\n```js\nconst multi = (...args) => args.reduce((a, b) => a * b, 1);\n\n// 输入 [multi, [100], null, [1, 2], null]\nconst curryMulti = currying(multi);\ncurryMulti(100); // null\ncurryMulti(); // 100\ncurryMulti(1, 2); // null\ncurryMulti(); // 200\n// [true, null, 100, null, 200]\n```\n","desc":"实现一个柯里化函数","index":"/**\n * 柯里化函数\n *\n * @export\n * @param {Function} fn\n * @return {Function}\n */\nexport default function currying(fn) {\n return fn;\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nconst add = (...args) => args.reduce((a, b) => a + b, 0);\nconst multi = (...args) => args.reduce((a, b) => a * b, 1);\n\nfunction Received(Input) {\n let curry;\n const output = [];\n Input.forEach((arg) => {\n let result;\n if (!curry) {\n curry = f(arg);\n result = !!curry;\n } else if (arg) {\n result = curry(...arg);\n } else {\n result = curry();\n }\n output.push(result);\n });\n return output;\n}\n\nit('加法函数柯里化一: 输入 [add, [1], [2], [3], null]', () => {\n assert.deepEqual(Received([add, [1], [2], [3], null]), [\n true,\n null,\n null,\n null,\n 6,\n ]);\n});\n\nit('加法函数柯里化二: 输入 [add, [1, 2, 1], null, null, null]', () => {\n assert.deepEqual(Received([add, [1, 2, 1], null, null, null]), [\n true,\n null,\n 4,\n 4,\n 4,\n ]);\n});\n\nit('加法函数柯里化三: 输入 [add, [1, 2, 1], null, [1, 2], null]', () => {\n assert.deepEqual(Received([add, [1, 2, 1], null, [1, 2], null]), [\n true,\n null,\n 4,\n null,\n 7,\n ]);\n});\n\nit('乘法函数柯里化一: 输入 [multi, [100], null, [1, 2], null]', () => {\n assert.deepEqual(Received([multi, [100], null, [1, 2], null]), [\n true,\n null,\n 100,\n null,\n 200,\n ]);\n});\n"},"deepClone":{"answermd":"### 方法一\n\n递归拷贝, 使用 weakMap 过滤循环引用\n\n```js\nexport default function deepClone(obj, weakMap = new WeakMap()) {\n const type = Object.prototype.toString.call(obj);\n if (!(type === '[object Object]' || type === '[object Array]')) return obj;\n const o = type === '[object Object]' ? {} : [];\n if (weakMap.get(obj)) return o;\n weakMap.set(obj, true);\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n o[key] = deepClone(obj[key], weakMap);\n }\n }\n return o;\n}\n```\n","answer":"export default function deepClone(obj, weakMap = new WeakMap()) {\n const type = Object.prototype.toString.call(obj);\n if (!(type === '[object Object]' || type === '[object Array]')) return obj;\n const o = type === '[object Object]' ? {} : [];\n if (weakMap.get(obj)) return o;\n weakMap.set(obj, true);\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n o[key] = deepClone(obj[key], weakMap);\n }\n }\n return o;\n}\n","introduce":"实现对象深拷贝, 修改深拷贝对象不影响原对象\n\n用例 1:\n\n```js\nconst userList = { user: [{ name: 'xjq' }] };\nconst cloneUserList = deepClone(userList);\n\nconsole.log(cloneUserList); // { user: [{ name: 'xjq' }] }\ncloneUserList.user.push({ name: 'xjq1' });\nconsole.log(cloneUserList.user); // [{ name:'xjq' }]\n```\n\n用例 2:\n\n```js\n\nconst user = { name: 'xjq', height: 180 };\nconst cloneUser = deepClone(user);\n\nconsole.log(user); // { name: 'xjq', height: 180 }\n\ndelete cloneUser.height;\nconsole.log(cloneUser); // { name: 'xjq' }\nconsole.log(user; // { name: 'xjq', height: 180 }\n```\n","desc":"实现对象深拷贝, 修改深拷贝对象不影响原对象","index":"/**\n * 深拷贝\n *\n * @param {(Array|Object)} obj\n * @return {(Array|Object)}\n */\nexport default function deepClone(obj) {\n return {};\n}\n","test":"import _deepClone from './answer.mjs';\nimport f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('一层对象: 输入 { f: 1 }', () => {\n assert.deepEqual(f({ f: 2 }), { f: 2 });\n});\n\nit('两层对象: 输入 { a: 1, b: { c: 3 } }', () => {\n assert.deepEqual(f({ a: 1, b: { c: 3 } }), { a: 1, b: { c: 3 } });\n});\n\nit('数组: 输入 [1, { a: 1 }]', () => {\n assert.deepEqual(f([1, { a: 1 }]), [1, { a: 1 }]);\n});\n\nit('修改拷贝对象属性, 原对象应不受影响', () => {\n const Input = { b: { c: 1, d: 2 } };\n const cloneObj = f(Input);\n cloneObj.b.c = 2;\n assert.deepEqual(Input, {\n b: { c: 1, d: 2 },\n });\n});\n\nit('循环引用', () => {\n const circle1 = { foo: 1 };\n const circle2 = { bar: 2, c1: circle1 };\n circle1.c2 = circle2;\n assert.deepEqual(f(circle1), { foo: 1, c2: { bar: 2, c1: {} } });\n});\n"},"deepMerge":{"answer":"function getType(o) {\n return Object.prototype.toString.call(o);\n}\n\nfunction deepClone(obj, weakMap = new WeakMap()) {\n const type = Object.prototype.toString.call(obj);\n if (!(type === '[object Object]' || type === '[object Array]')) return obj;\n const o = type === '[object Object]' ? {} : [];\n if (weakMap.get(obj)) return o;\n weakMap.set(obj, true);\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n o[key] = deepClone(obj[key], weakMap);\n }\n }\n return o;\n}\n\nexport default function deepMerge(a, b) {\n if (getType(a) !== '[object Object]' && getType(a) !== '[object Array]') {\n return deepClone(b);\n }\n\n const isArray = getType(a) === '[object Array]';\n const res = isArray ? [] : {};\n for (const key in b) {\n if (b.hasOwnProperty(key)) {\n res[key] = deepMerge(a[key], b[key]);\n }\n }\n for (const key in a) {\n if (res[key] === undefined && a.hasOwnProperty(key)) {\n res[key] = deepClone(a[key]);\n }\n }\n return res;\n}\n","introduce":"对象合并\n\n深度合并两个对象, 并且修改新对象不会影响原对象, 相同的属性则以 b 对象为主\n\n用例 1:\n\n```js\nconst a = { a: 1 };\nconst b = { b: 2 };\n\ndeepMerge(a, b); // { a: 1, b: 2 }\n```\n\n用例 2:\n\n```js\nconst a = { foo: { bar: 0 }, arr: [1, 3, { a: { b: 1 } }] };\nconst b = { foo: { bar: 1 }, arr: [1, 2, { b: { a: 1 } }] };\n\ndeepMerge(a, b); // { foo: { bar: 1 }, arr: [1, 2, { a: { b: 1 }, b: { a: 1 } }] }\n```\n\n用例 3:\n\nb 属性值为 undefined 不会并入 a\n\n```js\nconst a = { a: 1 };\nconst b = { a: undefined };\n\ndeepMerge(a, b); // { a: 1 }\n```\n\n用例 4:\n\n```js\nconst a = { a: 1 };\nconst b = undefined;\n\ndeepMerge(a, b); // { a: 1 }\n```\n\n用例 5:\n\n```js\nconst a = undefined;\nconst b = { a: 1 };\n\ndeepMerge(a, b); // { a: 1 }\n```\n","desc":"对象合并","index":"function getType(o) {\n return Object.prototype.toString.call(o);\n}\n\nfunction deepClone(obj, weakMap = new WeakMap()) {\n const type = Object.prototype.toString.call(obj);\n if (!(type === '[object Object]' || type === '[object Array]')) return obj;\n const o = type === '[object Object]' ? {} : [];\n if (weakMap.get(obj)) return o;\n weakMap.set(obj, true);\n for (const key in obj) {\n if (obj.hasOwnProperty(key)) {\n o[key] = deepClone(obj[key], weakMap);\n }\n }\n return o;\n}\n\n/**\n * 对象合并\n *\n * @export\n * @param {Object} a\n * @param {Object} b\n * @return {Object}\n */\nexport default function deepMerge(a, b) {\n if (getType(a) !== '[object Object]' && getType(a) !== '[object Array]') {\n return deepClone(b);\n }\n\n const isArray = getType(a) === '[object Array]';\n const res = isArray ? [] : {};\n for (const key in b) {\n if (b.hasOwnProperty(key)) {\n res[key] = deepMerge(a[key], b[key]);\n }\n }\n for (const key in a) {\n if (res[key] === undefined && a.hasOwnProperty(key)) {\n res[key] = deepClone(a[key]);\n }\n }\n return res;\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('一层对象: 输入 { a: 1 }, { b: 2 }', () => {\n assert.deepEqual(f({ a: 1 }, { b: 2 }), { a: 1, b: 2 });\n});\n\nit('两层对象: 输入 { a: 1, b: { c: 2 } }, { b: 2 }', () => {\n assert.deepEqual(f({ a: 1, b: { c: 2 } }, { b: { c: 3, d: 4 } }), {\n a: 1,\n b: { c: 3, d: 4 },\n });\n});\n\nit(\"两层对象存在数组合并: 输入 { b: { c: [1, 3, { a: 1 }] } }, { b: { c: ['1', 2, { a: 2 }] } }\", () => {\n assert.deepEqual(\n f({ b: { c: [1, 3, { a: 1 }] } }, { b: { c: ['1', 2, { a: 2 }] } }),\n { b: { c: ['1', 2, { a: 2 }] } }\n );\n});\n\nit('b 中属性值为 undefined 时不会合并: 输入 { a: 1 }, { a: undefined }', () => {\n assert.deepEqual(f({ a: 1 }, { a: undefined }), { a: 1 });\n});\n\nit('边界输入一 undefined | null, { a: 1 }', () => {\n assert.deepEqual(f(undefined, { a: 1 }), { a: 1 });\n assert.deepEqual(f(null, { a: 1 }), { a: 1 });\n});\n\nit('边界输入二 { a: 1 }, undefined | null', () => {\n assert.deepEqual(f({ a: 1 }, undefined), { a: 1 });\n assert.deepEqual(f({ a: 1 }, null), { a: 1 });\n});\n"},"flatten":{"answermd":"### 方法一\n\n```js\nexport default function flatten(arr) {\n return arr.reduce((acc, cur) => {\n if (Array.isArray(cur)) {\n acc = acc.concat(flatten(cur));\n } else {\n acc.push(cur);\n }\n return acc;\n }, []);\n}\n```\n","answer":"export default function flatten(arr) {\n return arr.reduce((acc, cur) => {\n if (Array.isArray(cur)) {\n acc = acc.concat(flatten(cur));\n } else {\n acc.push(cur);\n }\n return acc;\n }, []);\n}\n","introduce":"实现数组扁平化方法\n\n用例 1:\n\n```js\nflatten([1, 2, 3]); // [1, 2, 3]\n```\n\n用例 2:\n\n```js\nflatten([1, [2, 3]]); // [1, 2, 3]\n```\n\n用例 3:\n\n```js\nflatten([1, [[2, 2], [3, [4]], 5]]); // [1, 2, 2, 3, 4, 5];\n```\n","desc":"实现数组扁平化方法","index":"/**\n * 数组扁平化\n *\n * @param {number[]} arr\n * @return {number[]}\n */\nexport default function flatten(arr) {\n return [];\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('一层数组: 输入 [1, 2, 3]', () => {\n assert.deepEqual(f([1, 2, 3]), [1, 2, 3]);\n});\n\nit('两层数组: 输入 [1, [2, 3]]', () => {\n assert.deepEqual(f([1, [2, 3]]), [1, 2, 3]);\n});\n\nit('四层数组: 输入 [1, [[2, 2], [3, [4]], 5]]', () => {\n assert.deepEqual(f([1, [[2, 2], [3, [4]], 5]]), [1, 2, 2, 3, 4, 5]);\n});\n"},"sort":{"answermd":"### 方法一\n\n原生 api sort 方法\n\n```js\nexport default function sort(arr) {\n // js api\n return arr.sort();\n}\n```\n","answer":"export default function f(arr) {\n // js api\n return arr.sort();\n}\n","introduce":"将数组按从小到大的顺序排序\n\n输入为 数字数组\n\n- 冒泡排序\n- 选择排序\n- 插入排序\n- 归并排序\n- 堆排序\n- 快速排序\n\n...\n\n用例 1:\n\n```js\nconst arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];\nconst sortArr = sort(arr);\n\nconsole.log(sortArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n用例 2:\n\n```js\nconst arr = [1, 9, 2, 4, 3, 7, 6, 8, 6];\nconst sortArr = sort(arr);\n\nconsole.log(sortArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n用例 3:\n\n```js\nconst arr = [1, 1, 1, 3, 4, 5, 7, 6, 6, 6, 0, 0, -1];\nconst sortArr = sort(arr);\n\nconsole.log(sortArr); // [-1, 0, 0, 1, 1, 1, 3, 4, 5, 6, 6, 6, 7]\n```\n","desc":"将数组按从小到大的顺序排序","index":"/**\n * 排序函数\n *\n * @export\n * @param {number[]} arr\n * @return {number[]}\n */\nexport default function sort(arr) {\n return [];\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('空数组: 输入 []', () => {\n assert.deepEqual(f([]), []);\n});\n\nit('已排序好的数组: 输入 [1, 2, 3, 4, 5, 6, 7, 8, 9]', () => {\n assert.deepEqual(f([1, 2, 3, 4, 5, 6, 7, 8, 9]), [1, 2, 3, 4, 5, 6, 7, 8, 9]);\n});\n\nit('乱序数组一: 输入 [1, 9, 2, 4, 3, 7, 6, 8, 5]', () => {\n assert.deepEqual(f([1, 9, 2, 4, 3, 7, 6, 8, 5]), [1, 2, 3, 4, 5, 6, 7, 8, 9]);\n});\n\nit('乱序数组二-包含负数: 输入 [1, 1, 1, 3, 4, 5, 7, 6, 6, 6, 0, 0, -1]', () => {\n assert.deepEqual(\n f([1, 1, 1, 3, 4, 5, 7, 6, 6, 6, 0, 0, -1]),\n [-1, 0, 0, 1, 1, 1, 3, 4, 5, 6, 6, 6, 7]\n );\n});\n"},"unique":{"answermd":"### 方法一\n\n利用 set 去除\n\n```js\nexport default function unique(arr) {\n return [...new Set(arr)];\n}\n```\n","answer":"export default function unique(arr) {\n return [...new Set(arr)];\n}\n","introduce":"数组去重\n\n用例 1:\n\n```js\nconst arr = [1, 2, 3];\nconst uniqueArr = unique(arr1);\n\nconsole.log(uniqueArr); // [1, 2, 3]\n```\n\n用例 2:\n\n```js\nconst arr = [1, 2, 3, 3, 3];\nconst uniqueArr = unique(arr);\n\nconsole.log(uniqueArr); // [1, 2, 3]\n```\n","desc":"数组去重","index":"/**\n * 数组去重\n *\n * @export\n * @param {number[]} arr\n * @return {number[]}\n */\nexport default function unique(arr) {\n return [];\n}\n","test":"import f from './index.mjs';\nimport { it } from 'mocha';\nimport { assert } from 'chai';\n\nit('无重复数字: 输入 [1, 2, 3]', () => {\n assert.deepEqual(f([1, 2, 3]), [1, 2, 3]);\n});\n\nit('单个重复数字: 输入 [1, 2, 3, 3]', () => {\n assert.deepEqual(f([1, 2, 3, 3]), [1, 2, 3]);\n});\n\nit('多个重复数字: 输入 [1, 1, 2, 2, 2, 3, 3]', () => {\n assert.deepEqual(f([1, 1, 2, 2, 2, 3, 3]), [1, 2, 3]);\n});\n"}} \ No newline at end of file