From 17c46c2f572123d37eedb2f236a4cb69dbd53b15 Mon Sep 17 00:00:00 2001 From: linwens Date: Mon, 30 Dec 2019 15:02:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(chunk):=20=E6=8A=84=E4=BA=86chunk=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=8F=8A=E4=BE=9D=E8=B5=96=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chunk,toFinite,toInteger,toNumber,isObject方法;没有写完整的测试用例,只是保证覆盖率在93%左右 --- lib/index.d.ts | 6 ++++- lib/index.js | 8 ++++++ src/__test__/chunk.test.ts | 35 ++++++++++++++++++++++++ src/__test__/isObject.test.ts | 18 +++++++++++++ src/__test__/slice.test.ts | 15 +++++++++++ src/__test__/toNumber.test.ts | 18 +++++++++++++ src/__test__/utils.ts | 51 +++++++++++++++++++++++++++++++++++ src/chunk.ts | 21 +++++++++++++++ src/index.ts | 6 ++++- src/isObject.ts | 6 +++++ src/slice.ts | 28 +++++++++++++++++++ src/toFinite.ts | 18 +++++++++++++ src/toInteger.ts | 11 ++++++++ src/toNumber.ts | 42 +++++++++++++++++++++++++++++ tslint.json | 1 + 15 files changed, 282 insertions(+), 2 deletions(-) create mode 100644 src/__test__/chunk.test.ts create mode 100644 src/__test__/isObject.test.ts create mode 100644 src/__test__/slice.test.ts create mode 100644 src/__test__/toNumber.test.ts create mode 100644 src/__test__/utils.ts create mode 100644 src/chunk.ts create mode 100644 src/isObject.ts create mode 100644 src/slice.ts create mode 100644 src/toFinite.ts create mode 100644 src/toInteger.ts create mode 100644 src/toNumber.ts diff --git a/lib/index.d.ts b/lib/index.d.ts index 9fbdbe4..d4afb55 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -8,4 +8,8 @@ import divide from './divide'; import mean from './mean'; import meanBy from './meanBy'; import map from './map'; -export { Greeter, ArrayEach, DeBounce, eq, add, ceil, divide, mean, meanBy, map }; +import chunk from './chunk'; +import slice from './slice'; +import toNumber from './toNumber'; +import isObject from './isObject'; +export { Greeter, ArrayEach, DeBounce, eq, add, ceil, divide, mean, meanBy, map, chunk, slice, toNumber, isObject }; diff --git a/lib/index.js b/lib/index.js index 9fac687..cd97566 100644 --- a/lib/index.js +++ b/lib/index.js @@ -23,3 +23,11 @@ var meanBy_1 = __importDefault(require("./meanBy")); exports.meanBy = meanBy_1.default; var map_1 = __importDefault(require("./map")); exports.map = map_1.default; +var chunk_1 = __importDefault(require("./chunk")); +exports.chunk = chunk_1.default; +var slice_1 = __importDefault(require("./slice")); +exports.slice = slice_1.default; +var toNumber_1 = __importDefault(require("./toNumber")); +exports.toNumber = toNumber_1.default; +var isObject_1 = __importDefault(require("./isObject")); +exports.isObject = isObject_1.default; diff --git a/src/__test__/chunk.test.ts b/src/__test__/chunk.test.ts new file mode 100644 index 0000000..99e2b0a --- /dev/null +++ b/src/__test__/chunk.test.ts @@ -0,0 +1,35 @@ +import { chunk, map } from '../index' +import { falsey, stubArray } from './utils' + +describe('chunk', function() { + var array = [0, 1, 2, 3, 4, 5] + + it('should return chunked arrays', function() { + var actual = chunk(array, 3) + expect(actual).toEqual([[0, 1, 2], [3, 4, 5]]) + }) + + it('should return the last chunk as remaining elements', function() { + var actual = chunk(array, 4) + expect(actual).toEqual([[0, 1, 2, 3], [4, 5]]) + }) + + it('should treat falsey `size` values, except `undefined`, as `0`', function() { + var expected = map(falsey, function(value) { + return value === undefined ? [[0], [1], [2], [3], [4], [5]] : [] + }) + + var actual = map(falsey, function(size, index) { + return index ? chunk(array, size) : chunk(array) + }) + expect(actual).toEqual(expected) + }) + + // it('should ensure the minimum `size` is `0`', function() { + + // }); + + it('should coerce `size` to an integer', function() { + expect(chunk(array, array.length / 4)).toEqual([[0], [1], [2], [3], [4], [5]]) + }) +}) diff --git a/src/__test__/isObject.test.ts b/src/__test__/isObject.test.ts new file mode 100644 index 0000000..dea202c --- /dev/null +++ b/src/__test__/isObject.test.ts @@ -0,0 +1,18 @@ +import { isObject, map } from '../index' +import { args, slice } from './utils' + +describe('isObject', function() { + it('should return `true` for objects', function() { + expect(isObject(args)).toStrictEqual(true) + expect(isObject([1, 2, 3])).toStrictEqual(true) + expect(isObject(Object(false))).toStrictEqual(true) + expect(isObject(new Date())).toStrictEqual(true) + expect(isObject(new Error())).toStrictEqual(true) + + expect(isObject(slice)).toStrictEqual(true) + expect(isObject({ a: 1 })).toStrictEqual(true) + expect(isObject(Object(0))).toStrictEqual(true) + expect(isObject(/x/)).toStrictEqual(true) + expect(isObject(Object('a'))).toStrictEqual(true) + }) +}) diff --git a/src/__test__/slice.test.ts b/src/__test__/slice.test.ts new file mode 100644 index 0000000..0b270a8 --- /dev/null +++ b/src/__test__/slice.test.ts @@ -0,0 +1,15 @@ +import { slice } from '../index' + +describe('slice', function() { + var array = [1, 2, 3] + + // it('should return [] no array', function() { + // expect(slice(null)).toEqual([]); + // }); + it('should work with a negative `start`', function() { + expect(slice(array, -1)).toEqual([3]) + }) + it('should work with a negative `end`', function() { + expect(slice(array, 0, -1)).toEqual([1, 2]) + }) +}) diff --git a/src/__test__/toNumber.test.ts b/src/__test__/toNumber.test.ts new file mode 100644 index 0000000..2613c8c --- /dev/null +++ b/src/__test__/toNumber.test.ts @@ -0,0 +1,18 @@ +import { toNumber } from '../index' + +describe('toNumber', function() { + var array = [1, 2, 3] + + // it('should return [] no array', function() { + // expect(slice(null)).toEqual([]); + // }); + it('should work with a boolean', function() { + expect(toNumber(false)).toEqual(0) + }) + it('should work with a string', function() { + expect(toNumber(' 1 ')).toEqual(1) + expect(toNumber('0b11')).toEqual(3) + expect(toNumber('0o11')).toEqual(9) + expect(toNumber('0x11')).toEqual(17) + }) +}) diff --git a/src/__test__/utils.ts b/src/__test__/utils.ts new file mode 100644 index 0000000..588f49a --- /dev/null +++ b/src/__test__/utils.ts @@ -0,0 +1,51 @@ +/** Used as a reference to the global object. */ +var root = (typeof global == 'object' && global) || this || globalThis + +var ArrayBuffer = root.ArrayBuffer, + Map = root.Map, + Promise = root.Promise, + Set = root.Set, + Symbol = root.Symbol, + Uint8Array = root.Uint8Array, + WeakMap = root.WeakMap, + WeakSet = root.WeakSet + +var arrayBuffer = ArrayBuffer ? new ArrayBuffer(2) : undefined, + map = Map ? new Map() : undefined, + set = Set ? new Set() : undefined, + symbol = Symbol ? Symbol('a') : undefined, + weakMap = WeakMap ? new WeakMap() : undefined, + weakSet = WeakSet ? new WeakSet() : undefined + +/** Used for native method references. */ +var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype, + numberProto = Number.prototype, + stringProto = String.prototype + +/** 将数组转为 arguments */ +function toArgs(array: any) { + return function() { + return arguments + }.apply(undefined, array) +} + +var falsey = [, null, undefined, false, 0, NaN, ''] + +var stubArray = function() { + return [] +} + +var stubTrue = function() { + return true + }, + stubFalse = function() { + return false + } + +var args = toArgs([1, 2, 3]), + realm = {}, + slice = arrayProto.slice + +export { falsey, stubArray, args, realm, slice, symbol, stubFalse, toArgs } diff --git a/src/chunk.ts b/src/chunk.ts new file mode 100644 index 0000000..e8b7cdc --- /dev/null +++ b/src/chunk.ts @@ -0,0 +1,21 @@ +import slice from './slice' +import toInteger from './toInteger' + +function chunk(array: any[], size = 1) { + size = Math.max(toInteger(size), 0) + const length = array == null ? 0 : array.length + if (!length || size < 1) { + return [] + } + let index = 0 + let resIndex = 0 + // Math.ceil向上取整,因为会有余数的情况 + const result = new Array(Math.ceil(length / size)) + + while (index < length) { + result[resIndex++] = slice(array, index, (index += size)) + } + return result +} + +export default chunk diff --git a/src/index.ts b/src/index.ts index e339d27..dd9d2b6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,5 +8,9 @@ import divide from './divide' import mean from './mean' import meanBy from './meanBy' import map from './map' +import chunk from './chunk' +import slice from './slice' +import toNumber from './toNumber' +import isObject from './isObject' -export { Greeter, ArrayEach, DeBounce, eq, add, ceil, divide, mean, meanBy, map } +export { Greeter, ArrayEach, DeBounce, eq, add, ceil, divide, mean, meanBy, map, chunk, slice, toNumber, isObject } diff --git a/src/isObject.ts b/src/isObject.ts new file mode 100644 index 0000000..476e6ce --- /dev/null +++ b/src/isObject.ts @@ -0,0 +1,6 @@ +function isObject(value: any): boolean { + const type = typeof value + return value != null && (type === 'object' || type === 'function') +} + +export default isObject diff --git a/src/slice.ts b/src/slice.ts new file mode 100644 index 0000000..848dd53 --- /dev/null +++ b/src/slice.ts @@ -0,0 +1,28 @@ +function slice(array: T[], start = 0, end?: number): T[] { + let length = array == null ? 0 : array.length + if (!length) { + return [] + } + start = start == null ? 0 : start + end = end === undefined ? length : end + + if (start < 0) { + start = -start > length ? 0 : length + start + } + end = end > length ? length : end + if (end < 0) { + end += length + } + // >>> 无符号移位 >>>0 用于确保前面的表达式有意义,保证其值为非负整数 + length = start > end ? 0 : (end - start) >>> 0 + start >>>= 0 // => start = start >>> 0 + + let index = -1 + const rslt = new Array(length) + while (++index < length) { + rslt[index] = array[index + start] + } + return rslt +} + +export default slice diff --git a/src/toFinite.ts b/src/toFinite.ts new file mode 100644 index 0000000..427538d --- /dev/null +++ b/src/toFinite.ts @@ -0,0 +1,18 @@ +import toNumber from './toNumber' + +const INFINITY = 1 / 0 // => Infinity +const MAX_INTEGER = 1.7976931348623157e308 // Number.MAX_VALUE + +function toFinite(value: any) { + if (!value) { + return value === 0 ? value : 0 + } + value = toNumber(value) + if (value === INFINITY || value === -INFINITY) { + const sign = value < 0 ? -1 : 1 + return sign * MAX_INTEGER + } + return value === value ? value : 0 +} + +export default toFinite diff --git a/src/toInteger.ts b/src/toInteger.ts new file mode 100644 index 0000000..aa951f9 --- /dev/null +++ b/src/toInteger.ts @@ -0,0 +1,11 @@ +import toFinite from './toFinite' + +function toInteger(value: any) { + const result = toFinite(value) + // 3.2 % 1 => 0.20000000000000018 ; 3 % 1 => 0 + const remainder = result % 1 // 获取到result的小数位,如果有的话 + // result - remainder 不受浮点数插值影响 + return remainder ? result - remainder : result +} + +export default toInteger diff --git a/src/toNumber.ts b/src/toNumber.ts new file mode 100644 index 0000000..6f0ae22 --- /dev/null +++ b/src/toNumber.ts @@ -0,0 +1,42 @@ +import isObject from './isObject' +import isSymbol from './isSymbol' + +const NAN = 0 / 0 +/** 匹配头尾空白 */ +const reTrim = /^\s+|\s+$/g +/** 匹配十六进制 0x前缀 */ +const reIsBadHex = /^[-+]0x[0-9a-f]+$/i +/** 匹配二进制 0b前缀 */ +const reIsBinary = /^0b[01]+$/i +/** 匹配八进制 0o前缀 */ +const reIsOctal = /^0o[0-7]+$/i + +const freeParseInt = parseInt + +function toNumber(value: any): number { + if (typeof value === 'number') { + return value + } + if (isSymbol(value)) { + return NAN + } + if (isObject(value)) { + // valueOf() => + const other = typeof value.valueOf === 'function' ? value.valueOf() : value + value = isObject(other) ? `${other}` : other + } + if (typeof value !== 'string') { + return value === 0 ? value : +value + } + value = value.replace(reTrim, '') + const isBinary = reIsBinary.test(value) + return isBinary || reIsOctal.test(value) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : reIsBadHex.test(value) + ? NAN + : +value +} + +export default toNumber + +// https://segmentfault.com/a/1190000014454306 diff --git a/tslint.json b/tslint.json index 9fa40c8..abfba9f 100644 --- a/tslint.json +++ b/tslint.json @@ -2,6 +2,7 @@ "extends": ["tslint:recommended", "tslint-config-prettier"], "rules": { "no-console": false, + "no-bitwise": false, "object-literal-sort-keys": false, "member-access": false, "ordered-imports": false