From 58e2347abde32f1b5b9e3c7a50c3d2eb875e7da7 Mon Sep 17 00:00:00 2001 From: Brian Neisler Date: Tue, 18 Apr 2017 19:08:49 -0700 Subject: [PATCH] First pass of introducing protocols for associative and indexed values --- docs/API.md | 93 +++++++++++++++++-- docs/README.md | 14 +-- src/core/__tests__/delete.test.js | 85 +++++++++++++++++ src/core/__tests__/push.test.js | 90 ++++++++++++------ src/core/__tests__/slice.test.js | 50 +++++++++- src/core/__tests__/types/index.js | 2 + .../__tests__/types/indexedWithoutStack.js | 10 ++ src/core/__tests__/types/stack.js | 7 ++ src/core/__tests__/update.test.js | 55 +++++++++++ src/core/__tests__/util/getType.js | 4 + src/core/array/arraySlice.js | 20 ++++ src/core/array/index.js | 1 + src/core/base/baseDelete.js | 4 +- src/core/base/baseDifference.js | 6 +- src/core/base/baseEach.js | 10 +- src/core/base/baseEachRight.js | 9 +- src/core/base/baseEqualArrays.js | 2 +- src/core/base/baseEqualObjects.js | 12 +-- src/core/base/baseFindIndex.js | 6 +- src/core/base/baseFlatten.js | 6 +- src/core/base/baseFor.js | 6 +- src/core/base/baseForRight.js | 6 +- src/core/base/baseGet.js | 2 +- src/core/base/baseHas.js | 8 +- src/core/base/baseIncludes.js | 4 +- src/core/base/baseIncludesWith.js | 4 +- src/core/base/baseIsMatch.js | 8 +- src/core/base/baseKeys.js | 4 +- src/core/base/baseMatches.js | 10 +- src/core/base/baseMatchesProperty.js | 2 +- src/core/base/baseOmitBy.js | 18 ++-- src/core/base/basePickBy.js | 15 +-- src/core/base/baseProperty.js | 2 +- src/core/base/basePropertyOf.js | 2 +- src/core/base/basePullAll.js | 8 +- src/core/base/baseSet.js | 4 +- src/core/base/baseUniq.js | 8 +- src/core/base/baseWhile.js | 2 +- src/core/chunk.js | 18 ++-- src/core/concat.js | 11 ++- src/core/constants/constants.js | 4 + src/core/count.js | 24 ++++- src/core/deleteKey.js | 32 ++++++- src/core/drop.js | 6 +- src/core/dropRight.js | 10 +- src/core/findIndex.js | 9 +- src/core/findLastIndex.js | 9 +- src/core/flattenDepth.js | 4 +- src/core/get.js | 3 +- src/core/getKey.js | 10 +- src/core/hasKey.js | 13 ++- src/core/hasKeyIn.js | 11 +++ src/core/includes.js | 4 +- src/core/includesWith.js | 4 +- src/core/index.js | 9 ++ src/core/indexOf.js | 8 +- src/core/isArrayLike.js | 5 + src/core/isKey.js | 4 +- src/core/isLength.js | 6 ++ src/core/isNil.js | 3 + src/core/isSymbol.js | 7 ++ src/core/lastIndexOf.js | 8 +- src/core/mutations/mutationPushAll.js | 6 +- src/core/pop.js | 4 +- src/core/property.js | 2 +- src/core/protocols/Concatable.js | 8 ++ src/core/protocols/Counted.js | 8 ++ src/core/protocols/Sliceable.js | 8 ++ src/core/protocols/Stacked.js | 9 ++ src/core/protocols/index.js | 5 + src/core/push.js | 17 ++-- src/core/regex/index.js | 6 ++ src/core/regex/reHasUnicode.js | 15 +++ src/core/regex/reIsBadHex.js | 2 + src/core/regex/reIsBinary.js | 2 + src/core/regex/reIsOctal.js | 2 + src/core/regex/reTrim.js | 2 + src/core/regex/reUnicode.js | 29 ++++++ src/core/setKey.js | 35 ++++++- src/core/shift.js | 4 +- src/core/size.js | 5 +- src/core/slice.js | 31 ++++--- src/core/splice.js | 6 +- src/core/strict/strictIndexOf.js | 6 +- src/core/strict/strictLastIndexOf.js | 2 +- src/core/toFinite.js | 14 +++ src/core/toInteger.js | 8 ++ src/core/{util => }/toKey.js | 7 +- src/core/toNumber.js | 27 ++++++ src/core/util/asciiSize.js | 3 + src/core/util/assocIndexOf.js | 6 +- src/core/util/assocKey.js | 33 ------- src/core/util/dissocKey.js | 15 --- src/core/util/getKey.js | 9 -- src/core/util/getMatchData.js | 8 +- src/core/util/getSize.js | 8 -- src/core/util/hasKey.js | 12 --- src/core/util/hasKeyIn.js | 9 -- src/core/util/hasUnicode.js | 5 + src/core/util/index.js | 13 +-- src/core/util/matchesStrictComparable.js | 4 +- src/core/util/setKey.js | 17 ---- src/core/util/stringSize.js | 7 ++ src/core/util/unicodeSize.js | 9 ++ src/core/util/unsetKey.js | 11 --- src/core/with/withFor.js | 6 +- 106 files changed, 906 insertions(+), 360 deletions(-) create mode 100644 src/core/__tests__/delete.test.js create mode 100644 src/core/__tests__/types/indexedWithoutStack.js create mode 100644 src/core/__tests__/types/stack.js create mode 100644 src/core/array/arraySlice.js create mode 100644 src/core/hasKeyIn.js create mode 100644 src/core/isArrayLike.js create mode 100644 src/core/isLength.js create mode 100644 src/core/isNil.js create mode 100644 src/core/isSymbol.js create mode 100644 src/core/protocols/Concatable.js create mode 100644 src/core/protocols/Counted.js create mode 100644 src/core/protocols/Sliceable.js create mode 100644 src/core/protocols/Stacked.js create mode 100644 src/core/regex/reHasUnicode.js create mode 100644 src/core/regex/reIsBadHex.js create mode 100644 src/core/regex/reIsBinary.js create mode 100644 src/core/regex/reIsOctal.js create mode 100644 src/core/regex/reTrim.js create mode 100644 src/core/regex/reUnicode.js create mode 100644 src/core/toFinite.js create mode 100644 src/core/toInteger.js rename src/core/{util => }/toKey.js (56%) create mode 100644 src/core/toNumber.js create mode 100644 src/core/util/asciiSize.js delete mode 100644 src/core/util/assocKey.js delete mode 100644 src/core/util/dissocKey.js delete mode 100644 src/core/util/getKey.js delete mode 100644 src/core/util/getSize.js delete mode 100644 src/core/util/hasKey.js delete mode 100644 src/core/util/hasKeyIn.js create mode 100644 src/core/util/hasUnicode.js delete mode 100644 src/core/util/setKey.js create mode 100644 src/core/util/stringSize.js create mode 100644 src/core/util/unicodeSize.js delete mode 100644 src/core/util/unsetKey.js diff --git a/docs/API.md b/docs/API.md index a82faae..3583094 100644 --- a/docs/API.md +++ b/docs/API.md @@ -167,7 +167,7 @@ + [`isArguments()`](#isarguments) **Lo* + [`isArray()`](#isarray) + [`isArrayBuffer()`](#isarraybuffer) **Lo* - + [`isArrayLike()`](#isarraylike) **Lo* + + [`isArrayLike()`](#isarraylike) + [`isArrayLikeObject()`](#isarraylikeobject) **Lo* + [`isAssociative()`](#isassociative) + [`isBatchable()`](#isbatchable) @@ -215,14 +215,14 @@ + [`isKey()`](#iskey) + [`isKeyable()`](#iskeyable) + [`isKeyed()`](#iskeyed) - + [`isLength()`](#islength) **Lo* + + [`isLength()`](#islength) + [`isMap()`](#ismap) **Lo* + [`isMatch()`](#ismatch) + [`isMatchWith()`](#ismatchwith) **TODO* + [`isMutable()`](#ismutable) + [`isNaN()`](#isnan) **Lo* + [`isNative()`](#isnative) **Lo* - + [`isNil()`](#isnil) **Lo* + + [`isNil()`](#isnil) + [`isNull()`](#isnull) **Lo* + [`isNumber()`](#isnumber) + [`isObject()`](#isobject) @@ -234,7 +234,7 @@ + [`isSet()`](#isset) **Lo* + [`isShallowEqual()`](#isshallowequal) + [`isString()`](#isstring) - + [`isSymbol()`](#issymbol) **Lo* + + [`isSymbol()`](#issymbol) + [`isTypedArray()`](#istypedarray) **Lo* + [`isUndefined()`](#isundefined) **Lo* + [`isWeakMap()`](#isweakmap) **Lo* @@ -245,7 +245,7 @@ + [`lt()`](#lt) **Lo* + [`lte()`](#lte) **Lo* + [`toArray()`](#toarray) - + [`toFinite()`](#tofinite) **Lo* + + [`toFinite()`](#tofinite) + [`toIm()`](#toim) + [`toImIndexedSeq()`](#toimindexedseq) + [`toImIterable()`](#toimiterable) @@ -271,11 +271,11 @@ + [`toImSetSeq()`](#toimsetseq) + [`toImStack()`](#toimstack) + [`toIndexed()`](#toindexed) - + [`toInteger()`](#tointeger) **Lo* + + [`toInteger()`](#tointeger) + [`toIterable()`](#toiterable) + [`toLength()`](#tolength) **Lo* + [`toMutable()`](#tomutable) - + [`toNumber()`](#tonumber) **Lo* + + [`toNumber()`](#tonumber) + [`toObject()`](#toobject) + [`toPlainObject()`](#toplainobject) **Lo* + [`toSageInteger()`](#tosafeinteger) **Lo* @@ -674,6 +674,19 @@ isArray( Returns `true` if value is an `Array`. Otherwise `false`. +### `isArrayLike()` + +```js +isArrayLike( + data: any +): boolean +``` + +Returns `true` if value is array-like. Otherwise `false`. + +A value is considered array-like if it's not a function and has a `value.length` that's an integer greater than or equal to 0 and less than or equal to `Number.MAX_SAFE_INTEGER`. + + ### `isBoolean()` ```js @@ -852,6 +865,28 @@ isImmutableStack( Returns `true` if data is of type [`Immutable.Stack`][Immutable.Stack]. Otherwise `false`. +### `isLength()` + +```js +isLength( + data: any +): boolean +``` + +Returns `true` if value is a valid array-like length. Otherwise `false`. + + +### `isNil()` + +```js +isNil( + data: any +): boolean +``` + +Returns `true` if value is `null` or `undefined`. Otherwise `false`. + + ### `isNumber()` ```js @@ -885,6 +920,50 @@ isString( Returns `true` if value is classified as a `String` primitive or object. Otherwise `false`. +### `isSymbol()` + +```js +isSymbol( + data: any +): boolean +``` + +Returns `true` if value is classified as a `Symbol` primitive or object. Otherwise `false`. + + +### `toFinite()` + +```js +toFinite( + data: any +): number +``` + +Converts value to a finite number. + + +### `toInteger()` + +```js +toInteger( + data: any +): number +``` + +Converts value to an integer. + + +### `toNumber()` + +```js +toNumber( + data: any +): number +``` + +Converts value to a number. + + ## Object and Immutable.Map ### `set()` diff --git a/docs/README.md b/docs/README.md index 9a9e27b..ec74e05 100644 --- a/docs/README.md +++ b/docs/README.md @@ -161,7 +161,7 @@ + [`isArguments()`](./API.md#isarguments) **Lo* + [`isArray()`](./API.md#isarray) + [`isArrayBuffer()`](./API.md#isarraybuffer) **Lo* - + [`isArrayLike()`](./API.md#isarraylike) **Lo* + + [`isArrayLike()`](./API.md#isarraylike) + [`isArrayLikeObject()`](./API.md#isarraylikeobject) **Lo* + [`isAssociative()`](./API.md#isassociative) + [`isBatchable()`](./API.md#isbatchable) @@ -209,14 +209,14 @@ + [`isKey()`](./API.md#iskey) + [`isKeyable()`](./API.md#iskeyable) + [`isKeyed()`](./API.md#iskeyed) - + [`isLength()`](./API.md#islength) **Lo* + + [`isLength()`](./API.md#islength) + [`isMap()`](./API.md#ismap) **Lo* + [`isMatch()`](./API.md#ismatch) + [`isMatchWith()`](./API.md#ismatchwith) **TODO* + [`isMutable()`](./API.md#ismutable) + [`isNaN()`](./API.md#isnan) **Lo* + [`isNative()`](./API.md#isnative) **Lo* - + [`isNil()`](./API.md#isnil) **Lo* + + [`isNil()`](./API.md#isnil) + [`isNull()`](./API.md#isnull) **Lo* + [`isNumber()`](./API.md#isnumber) + [`isObject()`](./API.md#isobject) @@ -228,7 +228,7 @@ + [`isSet()`](./API.md#isset) **Lo* + [`isShallowEqual()`](./API.md#isshallowequal) + [`isString()`](./API.md#isstring) - + [`isSymbol()`](./API.md#issymbol) **Lo* + + [`isSymbol()`](./API.md#issymbol) + [`isTypedArray()`](./API.md#istypedarray) **Lo* + [`isUndefined()`](./API.md#isundefined) **Lo* + [`isWeakMap()`](./API.md#isweakmap) **Lo* @@ -239,7 +239,7 @@ + [`lt()`](./API.md#lt) **Lo* + [`lte()`](./API.md#lte) **Lo* + [`toArray()`](./API.md#toarray) - + [`toFinite()`](./API.md#tofinite) **Lo* + + [`toFinite()`](./API.md#tofinite) + [`toIm()`](./API.md#toim) + [`toImIndexedSeq()`](./API.md#toimindexedseq) + [`toImIterable()`](./API.md#toimiterable) @@ -265,11 +265,11 @@ + [`toImSetSeq()`](./API.md#toimsetseq) + [`toImStack()`](./API.md#toimstack) + [`toIndexed()`](./API.md#toindexed) - + [`toInteger()`](./API.md#tointeger) **Lo* + + [`toInteger()`](./API.md#tointeger) + [`toIterable()`](./API.md#toiterable) + [`toLength()`](./API.md#tolength) **Lo* + [`toMutable()`](./API.md#tomutable) - + [`toNumber()`](./API.md#tonumber) **Lo* + + [`toNumber()`](./API.md#tonumber) + [`toObject()`](./API.md#toobject) + [`toPlainObject()`](./API.md#toplainobject) **Lo* + [`toSageInteger()`](./API.md#tosafeinteger) **Lo* diff --git a/src/core/__tests__/delete.test.js b/src/core/__tests__/delete.test.js new file mode 100644 index 0000000..b2d444b --- /dev/null +++ b/src/core/__tests__/delete.test.js @@ -0,0 +1,85 @@ +import 'babel-polyfill' +import { delete as _delete } from '../' +import { withHintSameType, withMultipleTypeInputs } from './recompose' +import { immutableTest } from './tests' +import { indexed, keyed } from './types' +import { compose, runTests, setupTest } from './util' + +const enhance = compose( + withMultipleTypeInputs, + withHintSameType +) + +const testDelete = enhance(immutableTest(({ data, path }) => _delete(data, path))) + +describe('delete', function() { + const context = setupTest() + const { Symbol } = context + + const symbolA = Symbol('a') + + const tests = { + 'should delete indexed `data` on `path`': { + inputs: { + data: { + value: ['a', 'b', 'c'], + types: indexed() + }, + path: { + value: 1 + } + }, + expected: ['a', 'c'] + }, + 'should delete keyed `data` on `path`': { + inputs: { + data: { + value: { 'a':1, 'b':2, 'c':3 }, + types: keyed() + }, + path: { + value: 'c' + } + }, + expected: { 'a':1, 'b':2 } + }, + 'should delete deep `data` on `path` with nested object': { + inputs: { + data: { + value: { 'a':1, 'b':2, 'c': { 'd': 3, 'e': 4 } }, + types: keyed() + }, + path: { + value: 'c.d' + } + }, + expected: { 'a':1, 'b':2, 'c': { 'e': 4 } } + }, + 'should delete deep `data` on `path` with nested array': { + inputs: { + data: { + value: { 'a':1, 'b':2, 'c': [3, 4, 5] }, + types: keyed() + }, + path: { + value: 'c.1' + } + }, + expected: { 'a':1, 'b':2, 'c': [3, 5] } + }, + 'should delete keyed `data` with symbol `path`': { + inputs: { + data: { + value: { [symbolA]:1, 'b':2, 'c':3 } + //types: keyed() //TODO BRN: immutable types do not seem to be working with symbols + }, + path: { + value: symbolA + } + }, + expected: { 'b':2, 'c':3 } + } + } + + runTests(tests, context, testDelete) +}) diff --git a/src/core/__tests__/push.test.js b/src/core/__tests__/push.test.js index 963843a..ff67778 100644 --- a/src/core/__tests__/push.test.js +++ b/src/core/__tests__/push.test.js @@ -1,35 +1,69 @@ import 'babel-polyfill' -import { expect } from 'chai' import { push } from '../' -import Immutable from 'immutable' +import { withHintSameType, withMultipleTypeInputs } from './recompose' +import { immutableTest } from './tests' +import { indexed, indexedWithoutStack, stack } from './types' +import { compose, runTests, setupTest } from './util' -describe('push', function() { +const enhance = compose( + withMultipleTypeInputs, + withHintSameType +) + +const testPush = enhance(immutableTest(({ data, values }) => push(data, ...values))) - it('pushes to mutable array', function() { - const array = [] - const result = push(array, 'test1') - expect(result).not.to.equal(array) //expect new array - expect(array).to.deep.equal([]) //do no modify array - expect(result).to.deep.equal([ - 'test1' - ]) - }) +describe('push', function() { - it('returns array if data is empty', function() { - const data = null - const result = push(data, 'test1') - expect(result).to.deep.equal([ - 'test1' - ]) - }) + const context = setupTest() + const tests = { + 'pushes `values` to empty indexed `data`': { + inputs: { + data: { + value: [], + types: indexed() + }, + values: { + value: ['a'] + } + }, + expected: ['a'] + }, + 'pushes `value` to null data': { + inputs: { + data: { + value: null + }, + values: { + value: ['b'] + } + }, + expected: ['b'] + }, + 'pushes multiple `values` to indexed `data`': { + inputs: { + data: { + value: ['a'], + types: indexedWithoutStack() + }, + values: { + value: [ 'b', 'c' ] + } + }, + expected: [ 'a', 'b', 'c' ] + }, + 'pushes multiple `values` to front of `Stack`': { + inputs: { + data: { + value: ['a'], + types: stack() + }, + values: { + value: [ 'b', 'c' ] + } + }, + expected: [ 'b', 'c', 'a' ] + } + } - it('pushes to immutable List', function() { - const data = Immutable.fromJS([]) - const result = push(data, 'test1') - expect(result).not.to.equal(data) //expect new array - expect(Immutable.List.isList(result)).to.be.true - expect(result.toJS()).to.deep.equal([ - 'test1' - ]) - }) + runTests(tests, context, testPush) }) diff --git a/src/core/__tests__/slice.test.js b/src/core/__tests__/slice.test.js index c2ac197..426271d 100644 --- a/src/core/__tests__/slice.test.js +++ b/src/core/__tests__/slice.test.js @@ -1,8 +1,18 @@ import 'babel-polyfill' import _ from 'lodash' -import { slice } from '../' import Immutable from 'immutable' -import { clone, expectAllExactEqual, getType } from './util' +import { slice } from '../' +import { withHintSameType, withMultipleTypeInputs } from './recompose' +import { immutableTest } from './tests' +import { indexed } from './types' +import { clone, compose, expectAllExactEqual, getType, runTests, setupTest } from './util' + +const enhance = compose( + withMultipleTypeInputs, + withHintSameType +) + +const testSlice = enhance(immutableTest(({ data, start, end }) => slice(data, start, end))) describe('slice', function() { const inputs = [ @@ -31,6 +41,42 @@ describe('slice', function() { _.each(inputs, (input) => testSliceDefaultParamsReturnsExisting(input, clone(input))) _.each(inputs, (input) => testSliceStartingAt0ReturnsExisting(input, clone(input))) + + const context = setupTest() + const tests = { + 'slices start of 1 from indexed `data`': { + inputs: { + data: { + value: ['a', 'b', 'c'], + types: indexed() + }, + start: { + value: 1 + }, + end: { + value: undefined + } + }, + expected: ['b', 'c'] + }, + 'slices start of 1 to end of 2 from indexed `data`': { + inputs: { + data: { + value: ['a', 'b', 'c'], + types: indexed() + }, + start: { + value: 1 + }, + end: { + value: 2 + } + }, + expected: ['b'] + } + } + + runTests(tests, context, testSlice) }) function testSliceDefaultParamsReturnsExisting(data, expected) { diff --git a/src/core/__tests__/types/index.js b/src/core/__tests__/types/index.js index ccb2599..60c935e 100644 --- a/src/core/__tests__/types/index.js +++ b/src/core/__tests__/types/index.js @@ -1,2 +1,4 @@ export { default as indexed } from './indexed' +export { default as indexedWithoutStack } from './indexedWithoutStack' export { default as keyed } from './keyed' +export { default as stack } from './stack' diff --git a/src/core/__tests__/types/indexedWithoutStack.js b/src/core/__tests__/types/indexedWithoutStack.js new file mode 100644 index 0000000..c2b56e3 --- /dev/null +++ b/src/core/__tests__/types/indexedWithoutStack.js @@ -0,0 +1,10 @@ +import Immutable from 'immutable' +import _ from 'lodash' + +export default function indexed() { + return { + 'an array': _.toArray, + 'an Immutable.List': Immutable.List, + 'an Immutable.Seq': Immutable.Seq + } +} diff --git a/src/core/__tests__/types/stack.js b/src/core/__tests__/types/stack.js new file mode 100644 index 0000000..8defbfc --- /dev/null +++ b/src/core/__tests__/types/stack.js @@ -0,0 +1,7 @@ +import Immutable from 'immutable' + +export default function indexed() { + return { + 'an Immutable.Stack': Immutable.Stack + } +} diff --git a/src/core/__tests__/update.test.js b/src/core/__tests__/update.test.js index 12cba4f..5e7e8fc 100644 --- a/src/core/__tests__/update.test.js +++ b/src/core/__tests__/update.test.js @@ -1,4 +1,5 @@ import 'babel-polyfill' +//import Immutable from 'immutable' import { expect } from 'chai' import { update } from '../' import { withHintSameType, withMultipleTypeInputs } from './recompose' @@ -56,6 +57,60 @@ describe('update', function() { }, expected: { 'a':1, 'b':2, 'c':4 } }, + 'should update deep `data` on `path` with nested object': { + inputs: { + data: { + value: { 'a':1, 'b':2, 'c': { 'd': 3 } }, + types: keyed() + }, + path: { + value: 'c.d' + }, + updater: { + value: (value) => { + expect(value).to.equal(3) + return 4 + } + } + }, + expected: { 'a':1, 'b':2, 'c': { 'd': 4 } } + }, + // 'should update deep `data` on `path` with nested Map': { + // inputs: { + // data: { + // value: { 'a':1, 'b':2, 'c': Immutable.Map({ 'd': 3 }) }, + // types: keyed() + // }, + // path: { + // value: 'c.d' + // }, + // updater: { + // value: (value) => { + // expect(value).to.equal(3) + // return 4 + // } + // } + // }, + // expected: { 'a':1, 'b':2, 'c': Immutable.Map({ 'd': 4 }) } + // }, + 'should update deep `data` on `path` with nested array': { + inputs: { + data: { + value: { 'a':1, 'b':2, 'c': [3, 4, 5] }, + types: keyed() + }, + path: { + value: 'c.1' + }, + updater: { + value: (value) => { + expect(value).to.equal(4) + return 6 + } + } + }, + expected: { 'a':1, 'b':2, 'c': [3, 6, 5] } + }, 'should update keyed `data` with symbol `path`': { inputs: { data: { diff --git a/src/core/__tests__/util/getType.js b/src/core/__tests__/util/getType.js index 2259b92..5cbdf45 100644 --- a/src/core/__tests__/util/getType.js +++ b/src/core/__tests__/util/getType.js @@ -1,3 +1,4 @@ +import _ from 'lodash' import { List, Map, OrderedMap, OrderedSet, Seq, Set, Stack } from 'immutable' import isImmutable from './isImmutable' @@ -19,5 +20,8 @@ export default function getType(data) { return 'Immutable.Seq' } } + if (_.isArray(data)) { + return 'array' + } return typeof data } diff --git a/src/core/array/arraySlice.js b/src/core/array/arraySlice.js new file mode 100644 index 0000000..5e3fbe4 --- /dev/null +++ b/src/core/array/arraySlice.js @@ -0,0 +1,20 @@ +export default function arraySlice(array, start, end) { + let index = -1 + let length = array.length + + if (start < 0) { + start = -start > length ? 0 : (length + start) + } + end = end > length ? length : end + if (end < 0) { + end += length + } + length = start > end ? 0 : ((end - start) >>> 0) + start >>>= 0 + + const result = Array(length) + while (++index < length) { + result[index] = array[index + start] + } + return result +} diff --git a/src/core/array/index.js b/src/core/array/index.js index f4fe246..23ad062 100644 --- a/src/core/array/index.js +++ b/src/core/array/index.js @@ -1,4 +1,5 @@ export { default as arrayFilter } from './arrayFilter' export { default as arrayLikeKeys } from './arrayLikeKeys' export { default as arrayPush } from './arrayPush' +export { default as arraySlice } from './arraySlice' export { default as arraySome } from './arraySome' diff --git a/src/core/base/baseDelete.js b/src/core/base/baseDelete.js index af8482a..66e371e 100644 --- a/src/core/base/baseDelete.js +++ b/src/core/base/baseDelete.js @@ -1,7 +1,7 @@ import castPath from '../util/castPath' -import getKey from '../util/getKey' -import toKey from '../util/toKey' +import getKey from '../getKey' import isObject from '../isObject' +import toKey from '../toKey' export default function baseDelete(data, path, deleteFunc, setFunc) { path = castPath(path) diff --git a/src/core/base/baseDifference.js b/src/core/base/baseDifference.js index e12c263..53d4b1b 100644 --- a/src/core/base/baseDifference.js +++ b/src/core/base/baseDifference.js @@ -1,9 +1,9 @@ import Immutable from 'immutable' import cacheHas from '../cache/cacheHas' import mutationPush from '../mutations/mutationPush' -import getKey from '../util/getKey' -import getSize from '../util/getSize' import withMutations from '../with/withMutations' +import count from '../count' +import getKey from '../getKey' import hintConvert from '../hintConvert' import map from '../map' import baseIncludes from './baseIncludes' @@ -12,7 +12,7 @@ import baseUnary from './baseUnary' const findDifference = withMutations((result, indexed, values, iteratee, comparator, includes) => { let index = -1 - const length = getSize(indexed) + const length = count(indexed) if (!length) { return result } diff --git a/src/core/base/baseEach.js b/src/core/base/baseEach.js index d7a08bb..e0d99f0 100644 --- a/src/core/base/baseEach.js +++ b/src/core/base/baseEach.js @@ -1,19 +1,21 @@ -import _ from 'lodash' import baseForOwn from './baseForOwn' -import { getKey, getSize, isForEachable } from '../util' +import count from '../count' +import getKey from '../getKey' +import isArrayLike from '../isArrayLike' +import { isForEachable } from '../util' export default function baseEach(data, iteratee) { if (data == null) { return data } - if (!_.isArrayLike(data)) { + if (!isArrayLike(data)) { if (isForEachable(data)) { data.forEach(iteratee) return data } return baseForOwn(data, iteratee) } - const size = getSize(data) + const size = count(data) let index = -1 while (++index < size) { diff --git a/src/core/base/baseEachRight.js b/src/core/base/baseEachRight.js index 3552412..e1012f3 100644 --- a/src/core/base/baseEachRight.js +++ b/src/core/base/baseEachRight.js @@ -1,15 +1,16 @@ -import _ from 'lodash' +import count from '../count' +import getKey from '../getKey' +import isArrayLike from '../isArrayLike' import baseForOwnRight from './baseForOwnRight' -import { getKey, getSize } from '../util' export default function baseEachRight(data, iteratee) { if (data == null) { return data } - if (!_.isArrayLike(data)) { + if (!isArrayLike(data)) { return baseForOwnRight(data, iteratee) } - let size = getSize(data) + let size = count(data) while (size--) { if (iteratee(getKey(data, size), size, data) === false) { diff --git a/src/core/base/baseEqualArrays.js b/src/core/base/baseEqualArrays.js index 60f400a..69b9543 100644 --- a/src/core/base/baseEqualArrays.js +++ b/src/core/base/baseEqualArrays.js @@ -2,7 +2,7 @@ import { COMPARE_PARTIAL_FLAG, COMPARE_UNORDERED_FLAG } from '../constants' import arraySome from '../array/arraySome' import cacheHas from '../cache/cacheHas' import SetCache from '../cache/SetCache' -import getKey from '../util/getKey' +import getKey from '../getKey' export default function baseEqualArrays(array, other, bitmask, customizer, equalFunc, stack) { const isPartial = bitmask & COMPARE_PARTIAL_FLAG diff --git a/src/core/base/baseEqualObjects.js b/src/core/base/baseEqualObjects.js index 5a8a869..9a0f829 100644 --- a/src/core/base/baseEqualObjects.js +++ b/src/core/base/baseEqualObjects.js @@ -1,16 +1,16 @@ import { COMPARE_PARTIAL_FLAG } from '../constants' import getAllKeys from '../util/getAllKeys' -import getKey from '../util/getKey' -import getSize from '../util/getSize' -import hasKey from '../util/hasKey' -import hasKeyIn from '../util/hasKeyIn' +import count from '../count' +import getKey from '../getKey' +import hasKey from '../hasKey' +import hasKeyIn from '../hasKeyIn' export default function baseEqualObjects(object, other, bitmask, customizer, equalFunc, stack) { const isPartial = bitmask & COMPARE_PARTIAL_FLAG const objProps = getAllKeys(object) - const objLength = getSize(objProps) + const objLength = count(objProps) const othProps = getAllKeys(other) - const othLength = getSize(othProps) + const othLength = count(othProps) if (objLength != othLength && !isPartial) { return false diff --git a/src/core/base/baseFindIndex.js b/src/core/base/baseFindIndex.js index 3a1a779..5c1a5fd 100644 --- a/src/core/base/baseFindIndex.js +++ b/src/core/base/baseFindIndex.js @@ -1,8 +1,8 @@ -import getKey from '../util/getKey' -import getSize from '../util/getSize' +import count from '../count' +import getKey from '../getKey' export default function baseFindIndex(indexed, predicate, fromIndex, fromRight = false) { - const length = getSize(indexed) + const length = count(indexed) if (!length) { return -1 } diff --git a/src/core/base/baseFlatten.js b/src/core/base/baseFlatten.js index 8067ee5..e73eaf4 100644 --- a/src/core/base/baseFlatten.js +++ b/src/core/base/baseFlatten.js @@ -1,14 +1,14 @@ import mutationPush from '../mutations/mutationPush' import mutationPushAll from '../mutations/mutationPushAll' -import getKey from '../util/getKey' -import getSize from '../util/getSize' import withMutations from '../with/withMutations' +import count from '../count' +import getKey from '../getKey' import hintConvert from '../hintConvert' import isFlattenable from '../isFlattenable' const recurFlatten = (result, indexed, depth, predicate, isStrict) => { let index = -1 - const length = getSize(indexed) + const length = count(indexed) while (++index < length) { const value = getKey(indexed, index) if (depth > 0 && predicate(value)) { diff --git a/src/core/base/baseFor.js b/src/core/base/baseFor.js index cbfe682..2a57e27 100644 --- a/src/core/base/baseFor.js +++ b/src/core/base/baseFor.js @@ -1,10 +1,10 @@ -import getKey from '../util/getKey' -import getSize from '../util/getSize' +import count from '../count' +import getKey from '../getKey' export default function baseFor(iterable, iteratee, keysFunc) { let index = -1 const props = keysFunc(iterable) - let size = getSize(props) + let size = count(props) while (size--) { const key = getKey(props, ++index) diff --git a/src/core/base/baseForRight.js b/src/core/base/baseForRight.js index 061874b..75850ca 100644 --- a/src/core/base/baseForRight.js +++ b/src/core/base/baseForRight.js @@ -1,9 +1,9 @@ -import getKey from '../util/getKey' -import getSize from '../util/getSize' +import count from '../count' +import getKey from '../getKey' export default function baseForRight(iterable, iteratee, keysFunc) { const props = keysFunc(iterable) - let size = getSize(props) + let size = count(props) while (size--) { const key = getKey(props, size) diff --git a/src/core/base/baseGet.js b/src/core/base/baseGet.js index 2d222ba..40b1013 100644 --- a/src/core/base/baseGet.js +++ b/src/core/base/baseGet.js @@ -1,4 +1,4 @@ -import toKey from '../util/toKey' +import toKey from '../toKey' export default function baseGet(associative, path, getKeyFunc) { let data = associative diff --git a/src/core/base/baseHas.js b/src/core/base/baseHas.js index 0f0a4d0..c6c9f25 100644 --- a/src/core/base/baseHas.js +++ b/src/core/base/baseHas.js @@ -1,5 +1,5 @@ -import getKey from '../util/getKey' -import toKey from '../util/toKey' +import getKey from '../getKey' +import toKey from '../toKey' export default function baseHas(data, path, hasKeyFunc) { let result = false @@ -14,8 +14,4 @@ export default function baseHas(data, path, hasKeyFunc) { data = getKey(data, key) } return result - - // const dataLength = data ? getSize(data) : 0 - // return !!length && _.isLength(length) && isIndex(key, length) && - // (_.isArray(object) || _.isString(object) || _.isArguments(object)) } diff --git a/src/core/base/baseIncludes.js b/src/core/base/baseIncludes.js index 3498562..be916cf 100644 --- a/src/core/base/baseIncludes.js +++ b/src/core/base/baseIncludes.js @@ -1,11 +1,11 @@ import nativeMax from '../native/nativeMax' -import getSize from '../util/getSize' +import count from '../count' import isString from '../isString' import baseIndexOf from './baseIndexOf' export default function baseIncludes(indexed, value, fromIndex) { - const length = getSize(indexed) + const length = count(indexed) if (fromIndex < 0) { fromIndex = nativeMax(length + fromIndex, 0) } diff --git a/src/core/base/baseIncludesWith.js b/src/core/base/baseIncludesWith.js index 70193ed..c1dafe2 100644 --- a/src/core/base/baseIncludesWith.js +++ b/src/core/base/baseIncludesWith.js @@ -1,9 +1,9 @@ import nativeMax from '../native/nativeMax' -import getSize from '../util/getSize' +import count from '../count' import baseIndexOfWith from './baseIndexOf' export default function baseIncludesWith(indexed, value, comparator, fromIndex) { - const length = getSize(indexed) + const length = count(indexed) if (fromIndex < 0) { fromIndex = nativeMax(length + fromIndex, 0) } diff --git a/src/core/base/baseIsMatch.js b/src/core/base/baseIsMatch.js index cce32c6..32bbaa3 100644 --- a/src/core/base/baseIsMatch.js +++ b/src/core/base/baseIsMatch.js @@ -1,13 +1,13 @@ import { Stack } from '../cache' import { COMPARE_PARTIAL_FLAG, COMPARE_UNORDERED_FLAG } from '../constants' import { _Object } from '../context' -import getKey from '../util/getKey' -import getSize from '../util/getSize' -import hasKeyIn from '../util/hasKeyIn' +import count from '../count' +import getKey from '../getKey' +import hasKeyIn from '../hasKeyIn' import baseIsEqual from './baseIsEqual' export default function baseIsMatch(object, source, matchData, customizer) { - let index = getSize(matchData) + let index = count(matchData) const length = index const noCustomizer = !customizer let data diff --git a/src/core/base/baseKeys.js b/src/core/base/baseKeys.js index 606cd0b..2648b1b 100644 --- a/src/core/base/baseKeys.js +++ b/src/core/base/baseKeys.js @@ -1,12 +1,12 @@ -import _ from 'lodash' import arrayLikeKeys from '../array/arrayLikeKeys' import objectKeys from '../object/objectKeys' +import isArrayLike from '../isArrayLike' import isImmutable from '../isImmutable' export default function baseKeys(data) { if (isImmutable(data)) { return data.keySeq() - } else if (_.isArrayLike(data)) { + } else if (isArrayLike(data)) { return arrayLikeKeys(data) } return objectKeys(data) diff --git a/src/core/base/baseMatches.js b/src/core/base/baseMatches.js index 94c3b59..be2647d 100644 --- a/src/core/base/baseMatches.js +++ b/src/core/base/baseMatches.js @@ -1,13 +1,13 @@ -import baseGet from './baseGet' -import baseIsMatch from './baseIsMatch' -import getKey from '../util/getKey' import getMatchData from '../util/getMatchData' -import getSize from '../util/getSize' import matchesStrictComparable from '../util/matchesStrictComparable' +import count from '../count' +import getKey from '../getKey' +import baseGet from './baseGet' +import baseIsMatch from './baseIsMatch' export default function baseMatches(source) { const matchData = getMatchData(source) - if (getSize(matchData) == 1 && baseGet(matchData, [0, 2], getKey)) { + if (count(matchData) == 1 && baseGet(matchData, [0, 2], getKey)) { return matchesStrictComparable(baseGet(matchData, [0, 0], getKey), baseGet(matchData, [0, 1], getKey)) } return function(object) { diff --git a/src/core/base/baseMatchesProperty.js b/src/core/base/baseMatchesProperty.js index 7e2704f..004ff1c 100644 --- a/src/core/base/baseMatchesProperty.js +++ b/src/core/base/baseMatchesProperty.js @@ -1,9 +1,9 @@ import { COMPARE_PARTIAL_FLAG, COMPARE_UNORDERED_FLAG } from '../constants' import isStrictComparable from '../util/isStrictComparable' import matchesStrictComparable from '../util/matchesStrictComparable' -import toKey from '../util/toKey' import has from '../has' import isKey from '../isKey' +import toKey from '../toKey' import baseGet from './baseGet' import baseIsEqual from './baseIsEqual' diff --git a/src/core/base/baseOmitBy.js b/src/core/base/baseOmitBy.js index 9d0f315..73d2ad1 100644 --- a/src/core/base/baseOmitBy.js +++ b/src/core/base/baseOmitBy.js @@ -1,22 +1,22 @@ -import baseDelete from './baseDelete' -import baseGet from './baseGet' -import getSize from '../util/getSize' -import getKey from '../util/getKey' -import setKey from '../util/setKey' -import unsetKey from '../util/unsetKey' import castPath from '../util/castPath' import withMutations from '../with/withMutations' -import { get } from '../' +import count from '../count' +import deleteKey from '../deleteKey' +import get from '../get' +import getKey from '../getKey' +import setKey from '../setKey' +import baseDelete from './baseDelete' +import baseGet from './baseGet' const omitPaths = withMutations((result, data, paths, predicate) => { - const length = getSize(paths) + const length = count(paths) let index = -1 while (++index < length) { const path = get(paths, index) const value = baseGet(data, path, getKey) if (predicate(value, path)) { - result = baseDelete(result, castPath(path, data), unsetKey, setKey) + result = baseDelete(result, castPath(path, data), deleteKey, setKey) } } diff --git a/src/core/base/basePickBy.js b/src/core/base/basePickBy.js index 7e928fc..79926a3 100644 --- a/src/core/base/basePickBy.js +++ b/src/core/base/basePickBy.js @@ -1,14 +1,15 @@ -import baseGet from './baseGet' -import baseSet from './baseSet' -import getSize from '../util/getSize' -import getKey from '../util/getKey' -import setKey from '../util/setKey' import castPath from '../util/castPath' import withMutations from '../with/withMutations' -import { hintConvert, get } from '../' +import count from '../count' +import get from '../get' +import getKey from '../getKey' +import hintConvert from '../hintConvert' +import setKey from '../setKey' +import baseGet from './baseGet' +import baseSet from './baseSet' const pickPaths = withMutations((result, data, paths, predicate) => { - const length = getSize(paths) + const length = count(paths) let index = -1 while (++index < length) { diff --git a/src/core/base/baseProperty.js b/src/core/base/baseProperty.js index e3d09df..ad14138 100644 --- a/src/core/base/baseProperty.js +++ b/src/core/base/baseProperty.js @@ -1,4 +1,4 @@ -import getKey from '../util/getKey' +import getKey from '../getKey' export default function baseProperty(key) { return function(data) { diff --git a/src/core/base/basePropertyOf.js b/src/core/base/basePropertyOf.js index 79d1e0f..281d851 100644 --- a/src/core/base/basePropertyOf.js +++ b/src/core/base/basePropertyOf.js @@ -1,4 +1,4 @@ -import getKey from '../util/getKey' +import getKey from '../getKey' export default function basePropertyOf(data) { return function(key) { diff --git a/src/core/base/basePullAll.js b/src/core/base/basePullAll.js index 9a5a676..b9015a6 100644 --- a/src/core/base/basePullAll.js +++ b/src/core/base/basePullAll.js @@ -1,7 +1,7 @@ -import getKey from '../util/getKey' -import getSize from '../util/getSize' import withComparatorValue from '../with/withComparatorValue' import withEqValue from '../with/withEqValue' +import count from '../count' +import getKey from '../getKey' import map from '../map' import splice from '../splice' import baseFindIndex from './baseFindIndex' @@ -10,7 +10,7 @@ import baseUnary from './baseUnary' export default function basePullAll(data, values, iteratee, comparator, fromRight = false) { let index = -1 let seen = data - const length = getSize(values) + const length = count(values) if (iteratee) { seen = map(data, baseUnary(iteratee)) @@ -19,7 +19,7 @@ export default function basePullAll(data, values, iteratee, comparator, fromRigh const value = getKey(values, index) const computed = iteratee ? iteratee(value) : value const predicate = comparator ? withComparatorValue(comparator, computed) : withEqValue(computed) - const fromIndex = fromRight ? getSize(seen) : 0 + const fromIndex = fromRight ? count(seen) : 0 const startIndex = baseFindIndex(seen, predicate, fromIndex, fromRight) if (startIndex > -1) { diff --git a/src/core/base/baseSet.js b/src/core/base/baseSet.js index c1e65b1..2668b67 100644 --- a/src/core/base/baseSet.js +++ b/src/core/base/baseSet.js @@ -1,10 +1,10 @@ import castPath from '../util/castPath' -import getKey from '../util/getKey' -import toKey from '../util/toKey' +import getKey from '../getKey' import isImmutable from '../isImmutable' import isIndex from '../isIndex' import isObject from '../isObject' import toImmutable from '../toImmutable' +import toKey from '../toKey' export default function baseSet(data, path, value, setKeyFunc, options = {}) { path = castPath(path) diff --git a/src/core/base/baseUniq.js b/src/core/base/baseUniq.js index 3b17d14..756881c 100644 --- a/src/core/base/baseUniq.js +++ b/src/core/base/baseUniq.js @@ -1,15 +1,15 @@ import Immutable from 'immutable' import cacheHas from '../cache/cacheHas' import mutationPush from '../mutations/mutationPush' -import getKey from '../util/getKey' -import getSize from '../util/getSize' import withMutations from '../with/withMutations' +import count from '../count' +import getKey from '../getKey' +import hintConvert from '../hintConvert' import baseIncludes from './baseIncludes' import baseIncludesWith from './baseIncludesWith' -import hintConvert from '../hintConvert' const findUniques = withMutations((result, indexed, seen, iteratee, comparator, includes) => { - const length = getSize(indexed) + const length = count(indexed) let index = -1 while (++index < length) { let value = getKey(indexed, index) diff --git a/src/core/base/baseWhile.js b/src/core/base/baseWhile.js index dc06ac7..177ff1a 100644 --- a/src/core/base/baseWhile.js +++ b/src/core/base/baseWhile.js @@ -1,4 +1,4 @@ -import getKey from '../util/getKey' +import getKey from '../getKey' import size from '../size' import slice from '../slice' diff --git a/src/core/chunk.js b/src/core/chunk.js index 1560e8c..63bdfff 100644 --- a/src/core/chunk.js +++ b/src/core/chunk.js @@ -1,25 +1,25 @@ -import _ from 'lodash' +import count from './count' import hintConvert from './hintConvert' import isIterateeCall from './isIterateeCall' import push from './push' import slice from './slice' -import size from './size' +import toInteger from './toInteger' -export default function chunk(data, _size, guard) { - if ((guard ? isIterateeCall(data, _size, guard) : size === undefined)) { - _size = 1 +export default function chunk(data, size, guard) { + if ((guard ? isIterateeCall(data, size, guard) : size === undefined)) { + size = 1 } else { - _size = Math.max(_.toInteger(_size), 0) + size = Math.max(toInteger(size), 0) } - const length = data ? size(data) : 0 - if (!length || _size < 1) { + const length = data ? count(data) : 0 + if (!length || size < 1) { return hintConvert(data, []) } let index = 0 let result = hintConvert(data, []) while (index < length) { - result = push(result, slice(data, index, (index += _size))) + result = push(result, slice(data, index, (index += size))) } return result } diff --git a/src/core/concat.js b/src/core/concat.js index e5e65ac..97cb8c5 100644 --- a/src/core/concat.js +++ b/src/core/concat.js @@ -1,8 +1,9 @@ -import _ from 'lodash' -import isImmutable from './isImmutable' +import Concatable from './protocols/Concatable' +import hintConvert from './hintConvert' export default function concat(data, ...args) { - return isImmutable(data) - ? data.concat(...args) - : _.concat(data, ...args) + if (!Concatable.is(data)) { + data = hintConvert(data, [data]) + } + return data.concat(...args) } diff --git a/src/core/constants/constants.js b/src/core/constants/constants.js index f1ea6ef..178d90c 100644 --- a/src/core/constants/constants.js +++ b/src/core/constants/constants.js @@ -6,8 +6,10 @@ const COMPARE_UNORDERED_FLAG = 2 const HASH_UNDEFINED = '__lodash_hash_undefined__' const INFINITY = 1 / 0 const LARGE_ARRAY_SIZE = 200 +const MAX_INTEGER = 1.7976931348623157e+308 const MAX_MEMOIZE_SIZE = 500 const MAX_SAFE_INTEGER = 9007199254740991 +const NAN = 0 / 0 const CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.' const FUNC_ERROR_TEXT = 'Expected a function' @@ -56,8 +58,10 @@ export { HASH_UNDEFINED, INFINITY, LARGE_ARRAY_SIZE, + MAX_INTEGER, MAX_MEMOIZE_SIZE, MAX_SAFE_INTEGER, + NAN, CORE_ERROR_TEXT, FUNC_ERROR_TEXT, diff --git a/src/core/count.js b/src/core/count.js index 83813b9..6b9e742 100644 --- a/src/core/count.js +++ b/src/core/count.js @@ -1 +1,23 @@ -export default from './size' +import { MAP_TAG, SET_TAG } from './constants' +import { Counted } from './protocols' +import { getTag, stringSize } from './util' +import isArrayLike from './isArrayLike' +import isString from './isString' +import keys from './keys' + +export default function count(data) { + if (data == null) { + return 0 + } + if (Counted.is(data)) { + return data.count() + } + if (isArrayLike(data)) { + return isString(data) ? stringSize(data) : data.length + } + const tag = getTag(data) + if (tag == MAP_TAG || tag == SET_TAG) { + return data.size + } + return keys(data).length +} diff --git a/src/core/deleteKey.js b/src/core/deleteKey.js index c670471..a2fd714 100644 --- a/src/core/deleteKey.js +++ b/src/core/deleteKey.js @@ -1,5 +1,33 @@ -import { dissocKey } from './util' +import baseClone from './base/baseClone' +import Associative from './protocols/Associative' +import Concatable from './protocols/Concatable' +import Sliceable from './protocols/Sliceable' +import eq from './eq' +import hasKey from './hasKey' +import isArrayLike from './isArrayLike' +import isImmutableSeq from './isImmutableSeq' +import isKeyed from './isKeyed' +import splice from './splice' export default function deleteKey(data, key) { - return data == null ? data : dissocKey(data, key) + if (data == null) { + return data + } + if (hasKey(data, key)) { + if (Associative.is(data)) { + //TODO BRN: Check for ES6 Map + return data.delete(key) + } + if (isImmutableSeq(data) && isKeyed(data)) { + //TODO BRN: This is SLOW... figure out a better way + return data.filter((value, othKey) => !eq(othKey, key)) + } + if (isArrayLike(data) || (Concatable.is(data) && Sliceable.is(data))) { + return splice(data, key, 1) + } + //TODO BRN: Move clone to withMutations + data = baseClone(data) + delete data[key] + } + return data } diff --git a/src/core/drop.js b/src/core/drop.js index ed85d13..bb4e7f1 100644 --- a/src/core/drop.js +++ b/src/core/drop.js @@ -1,13 +1,13 @@ -import _ from 'lodash' +import hintConvert from './hintConvert' import slice from './slice' import size from './size' -import hintConvert from './hintConvert' +import toInteger from './toInteger' export default function drop(data, n = 1) { const length = size(data) if (!length) { return hintConvert(data, []) } - n = n === undefined ? 1 : _.toInteger(n) + n = n === undefined ? 1 : toInteger(n) return slice(data, n < 0 ? 0 : n, length) } diff --git a/src/core/dropRight.js b/src/core/dropRight.js index c801cbb..b55a98f 100644 --- a/src/core/dropRight.js +++ b/src/core/dropRight.js @@ -1,14 +1,14 @@ -import _ from 'lodash' -import slice from './slice' -import size from './size' +import count from './count' import hintConvert from './hintConvert' +import slice from './slice' +import toInteger from './toInteger' export default function dropRight(data, n = 1) { - const length = size(data) + const length = count(data) if (!length) { return hintConvert(data, []) } - n = n === undefined ? 1 : _.toInteger(n) + n = n === undefined ? 1 : toInteger(n) n = length - n return slice(data, 0, n < 0 ? 0 : n) } diff --git a/src/core/findIndex.js b/src/core/findIndex.js index 3379830..a174b95 100644 --- a/src/core/findIndex.js +++ b/src/core/findIndex.js @@ -1,10 +1,11 @@ -import _ from 'lodash' import { baseFindIndex } from './base' import { nativeMax } from './native' -import { getIteratee, getSize } from './util' +import { getIteratee } from './util' +import count from './count' import isIndexed from './isIndexed' import isOrdered from './isOrdered' import toIndexed from './toIndexed' +import toInteger from './toInteger' export default function findIndex(data, predicate, fromIndex) { let indexed = data @@ -14,8 +15,8 @@ export default function findIndex(data, predicate, fromIndex) { } indexed = toIndexed(data) } - const length = getSize(data) - let index = fromIndex == null ? 0 : _.toInteger(fromIndex) + const length = count(data) + let index = fromIndex == null ? 0 : toInteger(fromIndex) if (index < 0) { index = nativeMax(length + index, 0) } diff --git a/src/core/findLastIndex.js b/src/core/findLastIndex.js index 9b5a47f..c6a1ae0 100644 --- a/src/core/findLastIndex.js +++ b/src/core/findLastIndex.js @@ -1,10 +1,11 @@ -import _ from 'lodash' import { baseFindIndex } from './base' import { nativeMax, nativeMin } from './native' -import { getIteratee, getSize } from './util' +import { getIteratee } from './util' +import count from './count' import isIndexed from './isIndexed' import isOrdered from './isOrdered' import toIndexed from './toIndexed' +import toInteger from './toInteger' export default function findLastIndex(data, predicate, fromIndex) { let indexed = data @@ -14,13 +15,13 @@ export default function findLastIndex(data, predicate, fromIndex) { } indexed = toIndexed(data) } - const length = getSize(indexed) + const length = count(indexed) if (!length) { return -1 } let index = length - 1 if (fromIndex !== undefined) { - index = _.toInteger(fromIndex) + index = toInteger(fromIndex) index = fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1) diff --git a/src/core/flattenDepth.js b/src/core/flattenDepth.js index 36efdba..1e65822 100644 --- a/src/core/flattenDepth.js +++ b/src/core/flattenDepth.js @@ -1,12 +1,12 @@ -import _ from 'lodash' import { baseFlatten } from './base' import hintConvert from './hintConvert' import isIndexed from './isIndexed' +import toInteger from './toInteger' export default function flattenDepth(data, depth) { if (!isIndexed(data)) { return hintConvert(data, []) } - depth = depth === undefined ? 1 : _.toInteger(depth) + depth = depth === undefined ? 1 : toInteger(depth) return baseFlatten(data, depth) } diff --git a/src/core/get.js b/src/core/get.js index 608b94e..a6ee854 100644 --- a/src/core/get.js +++ b/src/core/get.js @@ -1,5 +1,6 @@ import { baseGet } from './base' -import { castPath, getKey } from './util' +import { castPath } from './util' +import getKey from './getKey' export default function get(data, maybePath, defaultValue) { let result = undefined diff --git a/src/core/getKey.js b/src/core/getKey.js index b8f27aa..850ec68 100644 --- a/src/core/getKey.js +++ b/src/core/getKey.js @@ -1,5 +1,9 @@ -import { getKey } from './util' +import Keyed from './protocols/Keyed' -export default function _getKey(data, key) { - return getKey(data, key) +export default function getKey(data, key) { + if (data != null) { + return Keyed.is(data) + ? data.get(key) + : data[key] + } } diff --git a/src/core/hasKey.js b/src/core/hasKey.js index faa86d2..f8bbd7c 100644 --- a/src/core/hasKey.js +++ b/src/core/hasKey.js @@ -1,5 +1,12 @@ -import { hasKey } from './util' +import { contextHasOwnProperty } from './context' +import Keyed from './protocols/Keyed' -export default function _hasKey(data, key) { - return hasKey(data, key) +export default function hasKey(data, key) { + if (data != null) { + if (Keyed.is(data)) { + return data.has(key) + } + return contextHasOwnProperty.call(data, key) + } + return false } diff --git a/src/core/hasKeyIn.js b/src/core/hasKeyIn.js new file mode 100644 index 0000000..cf3e11f --- /dev/null +++ b/src/core/hasKeyIn.js @@ -0,0 +1,11 @@ +import Keyed from './protocols/Keyed' + +export default function hasKeyIn(data, key) { + if (data != null) { + if (Keyed.is(data)) { + return data.has(key) + } + return !!(key in data) + } + return false +} diff --git a/src/core/includes.js b/src/core/includes.js index 7237115..0371bc4 100644 --- a/src/core/includes.js +++ b/src/core/includes.js @@ -1,9 +1,9 @@ -import _ from 'lodash' import { baseIncludes } from './base' import toIndexed from './toIndexed' +import toInteger from './toInteger' export default function includes(data, value, fromIndex) { const indexed = toIndexed(data) - fromIndex = fromIndex ? _.toInteger(fromIndex) : 0 + fromIndex = fromIndex ? toInteger(fromIndex) : 0 return baseIncludes(indexed, value, fromIndex) } diff --git a/src/core/includesWith.js b/src/core/includesWith.js index 88aa2fe..fecffa0 100644 --- a/src/core/includesWith.js +++ b/src/core/includesWith.js @@ -1,9 +1,9 @@ -import _ from 'lodash' import { baseIncludesWith } from './base' import toIndexed from './toIndexed' +import toInteger from './toInteger' export default function includesWith(data, value, comparator, fromIndex) { const indexed = toIndexed(data) - fromIndex = fromIndex ? _.toInteger(fromIndex) : 0 + fromIndex = fromIndex ? toInteger(fromIndex) : 0 return baseIncludesWith(indexed, value, comparator, fromIndex) } diff --git a/src/core/index.js b/src/core/index.js index 671a0c9..61a11a7 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -54,6 +54,7 @@ export { default as getPrototype } from './getPrototype' export { default as groupBy } from './groupBy' export { default as has } from './has' export { default as hasKey } from './hasKey' +export { default as hasKeyIn } from './hasKeyIn' export { default as head } from './head' export { default as hint } from './hint' export { default as hintConvert } from './hintConvert' @@ -64,6 +65,7 @@ export { default as includes } from './includes' export { default as includesWith } from './includesWith' export { default as indexOf } from './indexOf' export { default as isArray } from './isArray' +export { default as isArrayLike } from './isArrayLike' export { default as isAssociative } from './isAssociative' export { default as isBatchable } from './isBatchable' export { default as isBoolean } from './isBoolean' @@ -108,7 +110,9 @@ export { default as isKeyable } from './isKeyable' export { default as isKeyed } from './isKeyed' export { default as isMatch } from './isMatch' export { default as isMutable } from './isMutable' +export { default as isLength } from './isLength' export { default as isNative } from './isNative' +export { default as isNil } from './isNil' export { default as isNumber } from './isNumber' export { default as isObject } from './isObject' export { default as isObjectLike } from './isObjectLike' @@ -116,6 +120,7 @@ export { default as isOrdered } from './isOrdered' export { default as isPrototype } from './isPrototype' export { default as isShallowEqual } from './isShallowEqual' export { default as isString } from './isString' +export { default as isSymbol } from './isSymbol' export { default as iterable } from './iterable' export { default as iteratee } from './iteratee' export { default as iterator } from './iterator' @@ -191,6 +196,7 @@ export { default as takeRight } from './takeRight' //export { default as takeRightWhile } from './takeRightWhile' export { default as takeWhile } from './takeWhile' export { default as toArray } from './toArray' +export { default as toFinite } from './toFinite' export { default as toIm } from './toIm' export { default as toImIndexedSeq } from './toImIndexedSeq' export { default as toImIterable } from './toImIterable' @@ -216,8 +222,11 @@ export { default as toImSet } from './toImSet' export { default as toImSetSeq } from './toImSetSeq' export { default as toImStack } from './toImStack' export { default as toIndexed } from './toIndexed' +export { default as toInteger } from './toInteger' export { default as toIterable } from './toIterable' +export { default as toKey } from './toKey' export { default as toMutable } from './toMutable' +export { default as toNumber } from './toNumber' export { default as toObject } from './toObject' export { default as toSource } from './toSource' export { default as uniq } from './uniq' diff --git a/src/core/indexOf.js b/src/core/indexOf.js index c4430ac..fdf6109 100644 --- a/src/core/indexOf.js +++ b/src/core/indexOf.js @@ -1,10 +1,10 @@ -import _ from 'lodash' import { baseIndexOf } from './base' import { nativeMax } from './native' -import { getSize } from './util' +import count from './count' import isIndexed from './isIndexed' import isOrdered from './isOrdered' import toIndexed from './toIndexed' +import toInteger from './toInteger' export default function indexOf(data, value, fromIndex) { let indexed = data @@ -14,8 +14,8 @@ export default function indexOf(data, value, fromIndex) { } indexed = toIndexed(data) } - const length = getSize(indexed) - let index = fromIndex == null ? 0 : _.toInteger(fromIndex) + const length = count(indexed) + let index = fromIndex == null ? 0 : toInteger(fromIndex) if (index < 0) { index = nativeMax(length + index, 0) } diff --git a/src/core/isArrayLike.js b/src/core/isArrayLike.js new file mode 100644 index 0000000..fba9b01 --- /dev/null +++ b/src/core/isArrayLike.js @@ -0,0 +1,5 @@ +import isLength from './isLength' + +export default function isArrayLike(data) { + return data != null && typeof data != 'function' && isLength(data.length) +} diff --git a/src/core/isKey.js b/src/core/isKey.js index bdd1f3d..2093ebc 100644 --- a/src/core/isKey.js +++ b/src/core/isKey.js @@ -1,7 +1,7 @@ -import _ from 'lodash' import { _Object } from './context' import { reIsDeepProp, reIsPlainProp } from './regex' import isArray from './isArray' +import isSymbol from './isSymbol' export default function isKey(value, object) { if (isArray(value)) { @@ -9,7 +9,7 @@ export default function isKey(value, object) { } const type = typeof value if (type == 'number' || type == 'symbol' || type == 'boolean' || - value == null || _.isSymbol(value)) { + value == null || isSymbol(value)) { return true } return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || diff --git a/src/core/isLength.js b/src/core/isLength.js new file mode 100644 index 0000000..bbc99db --- /dev/null +++ b/src/core/isLength.js @@ -0,0 +1,6 @@ +import { MAX_SAFE_INTEGER } from './constants' + +export default function isLength(data) { + return typeof data == 'number' && + data > -1 && data % 1 == 0 && data <= MAX_SAFE_INTEGER +} diff --git a/src/core/isNil.js b/src/core/isNil.js new file mode 100644 index 0000000..50488d6 --- /dev/null +++ b/src/core/isNil.js @@ -0,0 +1,3 @@ +export default function isNil(data) { + return data == null +} diff --git a/src/core/isSymbol.js b/src/core/isSymbol.js new file mode 100644 index 0000000..986630b --- /dev/null +++ b/src/core/isSymbol.js @@ -0,0 +1,7 @@ +import { SYMBOL_TAG } from './constants' +import getTag from './util/getTag' + +export default function isSymbol(data) { + const type = typeof data + return type == 'symbol' || (type == 'object' && data != null && getTag(data) === SYMBOL_TAG) +} diff --git a/src/core/lastIndexOf.js b/src/core/lastIndexOf.js index a136503..50f7592 100644 --- a/src/core/lastIndexOf.js +++ b/src/core/lastIndexOf.js @@ -1,10 +1,10 @@ -import _ from 'lodash' import { baseLastIndexOf } from './base' import { nativeMax, nativeMin } from './native' +import count from './count' import isIndexed from './isIndexed' import isOrdered from './isOrdered' -import size from './size' import toIndexed from './toIndexed' +import toInteger from './toInteger' export default function lastIndexOf(data, value, fromIndex) { let indexed = data @@ -14,10 +14,10 @@ export default function lastIndexOf(data, value, fromIndex) { } indexed = toIndexed(data) } - const length = size(indexed) + const length = count(indexed) let index = length if (fromIndex !== undefined) { - index = _.toInteger(fromIndex) + index = toInteger(fromIndex) index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1) } return baseLastIndexOf(indexed, value, index) diff --git a/src/core/mutations/mutationPushAll.js b/src/core/mutations/mutationPushAll.js index 2ae8cd0..766385a 100644 --- a/src/core/mutations/mutationPushAll.js +++ b/src/core/mutations/mutationPushAll.js @@ -1,9 +1,9 @@ -import getKey from '../util/getKey' -import size from '../size' +import count from '../count' +import getKey from '../getKey' export default function mutationPushAll(data, values) { let index = -1 - const length = size(values) + const length = count(values) while (++index < length) { data.push(getKey(values, index)) diff --git a/src/core/pop.js b/src/core/pop.js index de1b9f2..ceb0c60 100644 --- a/src/core/pop.js +++ b/src/core/pop.js @@ -1,4 +1,4 @@ -import { getSize } from './util' +import count from './count' import isOrdered from './isOrdered' import slice from './slice' @@ -6,7 +6,7 @@ export default function pop(data) { if (!isOrdered(data)) { return data } - const length = getSize(data) + const length = count(data) const endIndex = length > 0 ? length - 1 : 0 return slice(data, 0, endIndex) } diff --git a/src/core/property.js b/src/core/property.js index a579401..c98846c 100644 --- a/src/core/property.js +++ b/src/core/property.js @@ -1,6 +1,6 @@ import { baseProperty, basePropertyDeep } from './base' -import { toKey } from './util' import isKey from './isKey' +import toKey from './toKey' export default function property(path) { return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path) diff --git a/src/core/protocols/Concatable.js b/src/core/protocols/Concatable.js new file mode 100644 index 0000000..937f9f6 --- /dev/null +++ b/src/core/protocols/Concatable.js @@ -0,0 +1,8 @@ +import defprotocol from '../defprotocol' +import Function from '../types/Function' + +const Concatable = defprotocol({ + concat: Function +}) + +export default Concatable diff --git a/src/core/protocols/Counted.js b/src/core/protocols/Counted.js new file mode 100644 index 0000000..a3150a0 --- /dev/null +++ b/src/core/protocols/Counted.js @@ -0,0 +1,8 @@ +import defprotocol from '../defprotocol' +import Function from '../types/Function' + +const Counted = defprotocol({ + count: Function +}) + +export default Counted diff --git a/src/core/protocols/Sliceable.js b/src/core/protocols/Sliceable.js new file mode 100644 index 0000000..bad1ea7 --- /dev/null +++ b/src/core/protocols/Sliceable.js @@ -0,0 +1,8 @@ +import defprotocol from '../defprotocol' +import Function from '../types/Function' + +const Sliceable = defprotocol({ + slice: Function +}) + +export default Sliceable diff --git a/src/core/protocols/Stacked.js b/src/core/protocols/Stacked.js new file mode 100644 index 0000000..a430068 --- /dev/null +++ b/src/core/protocols/Stacked.js @@ -0,0 +1,9 @@ +import defprotocol from '../defprotocol' +import Function from '../types/Function' + +const Stacked = defprotocol({ + pop: Function, + push: Function +}) + +export default Stacked diff --git a/src/core/protocols/index.js b/src/core/protocols/index.js index aba8486..05f859f 100644 --- a/src/core/protocols/index.js +++ b/src/core/protocols/index.js @@ -1,9 +1,14 @@ export { default as Associative } from './Associative' +export { default as Concatable } from './Concatable' +export { default as Counted } from './Counted' export { default as Extendable } from './Extendable' export { default as Indexed } from './Indexed' export { default as Inheriter } from './Inheriter' export { default as IPersistentList } from './IPersistentList' export { default as IPersistentMap } from './IPersistentMap' export { default as IPersistentSet } from './IPersistentSet' +export { default as IPersistentStack } from './IPersistentStack' export { default as Keyed } from './Keyed' export { default as Reducable } from './Reducable' +export { default as Sliceable } from './Sliceable' +export { default as Stacked } from './Stacked' diff --git a/src/core/push.js b/src/core/push.js index b688446..d81be88 100644 --- a/src/core/push.js +++ b/src/core/push.js @@ -1,14 +1,19 @@ -import _ from 'lodash' +import { Stacked } from './protocols' +import { withMutations } from './with' import concat from './concat' -import isImmutable from './isImmutable' +import isNil from './isNil' + +const pushValues = withMutations((data, values) => { + data.push(...values) + return data +}) export default function push(data, ...values) { - if (_.isNil(data)) { + if (isNil(data)) { data = [] } - - if (isImmutable(data)) { - return data.push(...values) + if (Stacked.is(data)) { + return pushValues(data, values) } return concat(data, values) } diff --git a/src/core/regex/index.js b/src/core/regex/index.js index bf87ebc..5bec6f7 100644 --- a/src/core/regex/index.js +++ b/src/core/regex/index.js @@ -1,9 +1,15 @@ export { default as reEscapeChar } from './reEscapeChar' +export { default as reHasUnicode } from './reHasUnicode' +export { default as reIsBadHex } from './reIsBadHex' +export { default as reIsBinary } from './reIsBinary' export { default as reIsDeepProp } from './reIsDeepProp' export { default as reIsHostCtor } from './reIsHostCtor' export { default as reIsNative } from './reIsNative' +export { default as reIsOctal } from './reIsOctal' export { default as reIsPlainProp } from './reIsPlainProp' export { default as reIsUint } from './reIsUint' export { default as reLeadingDot } from './reLeadingDot' export { default as rePropName } from './rePropName' export { default as reRegExpChar } from './reRegExpChar' +export { default as reTrim } from './reTrim' +export { default as reUnicode } from './reUnicode' diff --git a/src/core/regex/reHasUnicode.js b/src/core/regex/reHasUnicode.js new file mode 100644 index 0000000..4835fdb --- /dev/null +++ b/src/core/regex/reHasUnicode.js @@ -0,0 +1,15 @@ +/** Used to compose unicode character classes. */ +const rsAstralRange = '\\ud800-\\udfff' +const rsComboMarksRange = '\\u0300-\\u036f' +const reComboHalfMarksRange = '\\ufe20-\\ufe2f' +const rsComboSymbolsRange = '\\u20d0-\\u20ff' +const rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange +const rsVarRange = '\\ufe0e\\ufe0f' + +/** Used to compose unicode capture groups. */ +const rsZWJ = '\\u200d' + +/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ +const reHasUnicode = RegExp(`[${rsZWJ + rsAstralRange + rsComboRange + rsVarRange}]`) + +export default reHasUnicode diff --git a/src/core/regex/reIsBadHex.js b/src/core/regex/reIsBadHex.js new file mode 100644 index 0000000..85e5e72 --- /dev/null +++ b/src/core/regex/reIsBadHex.js @@ -0,0 +1,2 @@ +const reIsBadHex = /^[-+]0x[0-9a-f]+$/i +export default reIsBadHex diff --git a/src/core/regex/reIsBinary.js b/src/core/regex/reIsBinary.js new file mode 100644 index 0000000..f911ff4 --- /dev/null +++ b/src/core/regex/reIsBinary.js @@ -0,0 +1,2 @@ +const reIsBinary = /^0b[01]+$/i +export default reIsBinary diff --git a/src/core/regex/reIsOctal.js b/src/core/regex/reIsOctal.js new file mode 100644 index 0000000..628d04f --- /dev/null +++ b/src/core/regex/reIsOctal.js @@ -0,0 +1,2 @@ +const reIsOctal = /^0o[0-7]+$/i +export default reIsOctal diff --git a/src/core/regex/reTrim.js b/src/core/regex/reTrim.js new file mode 100644 index 0000000..31be842 --- /dev/null +++ b/src/core/regex/reTrim.js @@ -0,0 +1,2 @@ +const reTrim = /^\s+|\s+$/g +export default reTrim diff --git a/src/core/regex/reUnicode.js b/src/core/regex/reUnicode.js new file mode 100644 index 0000000..3651f38 --- /dev/null +++ b/src/core/regex/reUnicode.js @@ -0,0 +1,29 @@ +/** Used to compose unicode character classes. */ +const rsAstralRange = '\\ud800-\\udfff' +const rsComboMarksRange = '\\u0300-\\u036f' +const reComboHalfMarksRange = '\\ufe20-\\ufe2f' +const rsComboSymbolsRange = '\\u20d0-\\u20ff' +const rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange +const rsVarRange = '\\ufe0e\\ufe0f' + +/** Used to compose unicode capture groups. */ +const rsAstral = `[${rsAstralRange}]` +const rsCombo = `[${rsComboRange}]` +const rsFitz = '\\ud83c[\\udffb-\\udfff]' +const rsModifier = `(?:${rsCombo}|${rsFitz})` +const rsNonAstral = `[^${rsAstralRange}]` +const rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}' +const rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]' +const rsZWJ = '\\u200d' + +/** Used to compose unicode regexes. */ +const reOptMod = `${rsModifier}?` +const rsOptVar = `[${rsVarRange}]?` +const rsOptJoin = `(?:${rsZWJ}(?:${[rsNonAstral, rsRegional, rsSurrPair].join('|')})${rsOptVar + reOptMod})*` +const rsSeq = rsOptVar + reOptMod + rsOptJoin +const rsNonAstralCombo = `${rsNonAstral}${rsCombo}?` +const rsSymbol = `(?:${[rsNonAstralCombo, rsCombo, rsRegional, rsSurrPair, rsAstral].join('|')})` + +/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ +const reUnicode = RegExp(`${rsFitz}(?=${rsFitz})|${rsSymbol + rsSeq}`, 'g') +export default reUnicode diff --git a/src/core/setKey.js b/src/core/setKey.js index 67117d9..9cea885 100644 --- a/src/core/setKey.js +++ b/src/core/setKey.js @@ -1,5 +1,36 @@ -import { assocKey } from './util' +import baseClone from './base/baseClone' +import baseIsEqual from './base/baseIsEqual' +//import Keyed from '../protocols/Keyed' +import getKey from './getKey' +import hasKey from './hasKey' +import isImmutable from './isImmutable' +import isImmutableSeq from './isImmutableSeq' +import isImmutableStack from './isImmutableStack' +import isKeyed from './isKeyed' export default function setKey(data, key, value) { - return data == null ? data : assocKey(data, key, value) + if (data == null) { + return data + } + const dataValue = getKey(data, key) + if (!(hasKey(data, key) && baseIsEqual(dataValue, value)) || (value === undefined && !(hasKey(data, key)))) { + if (isImmutable(data)) { + if (isImmutableSeq(data)) { + if (isKeyed(data)) { + //TODO BRN: This is SLOW... figure out a better way + data = data.map((othValue, othKey) => othKey === key ? value : othValue) + } else { + data = data.splice(key, 1, value) + } + } else if (isImmutableStack(data)) { + data = data.splice(key, 1, value) + } else { + data = data.set(key, value) + } + } else { + data = baseClone(data) + data[key] = value + } + } + return data } diff --git a/src/core/shift.js b/src/core/shift.js index 6dad9ec..6a0d36d 100644 --- a/src/core/shift.js +++ b/src/core/shift.js @@ -1,6 +1,6 @@ -import size from './size' +import count from './count' import slice from './slice' export default function shift(data, number) { - return slice(data, number, size(data)) + return slice(data, number, count(data)) } diff --git a/src/core/size.js b/src/core/size.js index c77c20b..11d2432 100644 --- a/src/core/size.js +++ b/src/core/size.js @@ -1,4 +1 @@ -import { getSize } from './util' -export default function size(data) { - return getSize(data) -} +export default from './count' diff --git a/src/core/slice.js b/src/core/slice.js index 09b5007..78a8166 100644 --- a/src/core/slice.js +++ b/src/core/slice.js @@ -1,17 +1,24 @@ -import _ from 'lodash' -import isImmutable from './isImmutable' -import size from './size' +import arraySlice from './array/arraySlice' +import Sliceable from './protocols/Sliceable' +import count from './count' +import isArray from './isArray' +import toInteger from './toInteger' -export default function slice(data, start = 0, end) { - const length = size(data) - if (_.isNil(end)) { - end = length - } +export default function slice(data, start, end) { + const length = count(data) + start = start == null ? 0 : toInteger(start) + end = end === undefined ? length : toInteger(end) if (end === length && start === 0) { return data } - return isImmutable(data) - ? data.slice(start, end) - //TODO BRN: This does not work for objects. Need to convert to an indexed key value pair array, slice that array, and then convert back to an object - : _.slice(data, start, end) + if (!length) { + return data + } + if (!isArray(data)) { + if (Sliceable.is(data)) { + return data.slice(start, end) + } + throw new Error('Expected Sliceable data type or Array') + } + return arraySlice(data, start, end) } diff --git a/src/core/splice.js b/src/core/splice.js index b97f9ca..51a6e63 100644 --- a/src/core/splice.js +++ b/src/core/splice.js @@ -1,10 +1,12 @@ import concat from './concat' -import size from './size' +import count from './count' import slice from './slice' +import toInteger from './toInteger' export default function splice(data, startIndex, removeNum, ...values) { + startIndex = toInteger(startIndex) if (startIndex < 0) { - startIndex = size(data) + startIndex + startIndex = count(data) + startIndex } if (!removeNum || removeNum < 0) { removeNum = 0 diff --git a/src/core/strict/strictIndexOf.js b/src/core/strict/strictIndexOf.js index 32f5f25..08706c5 100644 --- a/src/core/strict/strictIndexOf.js +++ b/src/core/strict/strictIndexOf.js @@ -1,9 +1,9 @@ -import getKey from '../util/getKey' -import size from '../size' +import count from '../count' +import getKey from '../getKey' export default function strictIndexOf(indexed, value, fromIndex) { let index = fromIndex - 1 - const length = size(indexed) + const length = count(indexed) while (++index < length) { if (getKey(indexed, index) === value) { diff --git a/src/core/strict/strictLastIndexOf.js b/src/core/strict/strictLastIndexOf.js index 18ed3d2..5c26bfa 100644 --- a/src/core/strict/strictLastIndexOf.js +++ b/src/core/strict/strictLastIndexOf.js @@ -1,4 +1,4 @@ -import getKey from '../util/getKey' +import getKey from '../getKey' export default function strictLastIndexOf(indexed, value, fromIndex) { let index = fromIndex + 1 diff --git a/src/core/toFinite.js b/src/core/toFinite.js new file mode 100644 index 0000000..b26ee14 --- /dev/null +++ b/src/core/toFinite.js @@ -0,0 +1,14 @@ +import { INFINITY, MAX_INTEGER } from './constants' +import toNumber from './toNumber' + +export default function toFinite(data) { + if (!data) { + return data === 0 ? data : 0 + } + data = toNumber(data) + if (data === INFINITY || data === -INFINITY) { + const sign = (data < 0 ? -1 : 1) + return sign * MAX_INTEGER + } + return data === data ? data : 0 +} diff --git a/src/core/toInteger.js b/src/core/toInteger.js new file mode 100644 index 0000000..4e5eb51 --- /dev/null +++ b/src/core/toInteger.js @@ -0,0 +1,8 @@ +import toFinite from './toFinite' + +export default function toInteger(data) { + const result = toFinite(data) + const remainder = result % 1 + + return result === result ? (remainder ? result - remainder : result) : 0 +} diff --git a/src/core/util/toKey.js b/src/core/toKey.js similarity index 56% rename from src/core/util/toKey.js rename to src/core/toKey.js index 23e2fec..4bffb15 100644 --- a/src/core/util/toKey.js +++ b/src/core/toKey.js @@ -1,8 +1,9 @@ -import _ from 'lodash' -import { INFINITY } from '../constants' +import { INFINITY } from './constants' +import isSymbol from './isSymbol' + export default function toKey(value) { - if (typeof value == 'string' || _.isSymbol(value)) { + if (typeof value == 'string' || isSymbol(value)) { return value } const result = (value + '') diff --git a/src/core/toNumber.js b/src/core/toNumber.js new file mode 100644 index 0000000..d933753 --- /dev/null +++ b/src/core/toNumber.js @@ -0,0 +1,27 @@ +import { NAN } from './constants' +import { reIsBadHex, reIsBinary, reIsOctal, reTrim } from './regex' +import isObject from './isObject' +import isSymbol from './isSymbol' + +const freeParseInt = parseInt + +export default function toNumber(data) { + if (typeof data == 'number') { + return data + } + if (isSymbol(data)) { + return NAN + } + if (isObject(data)) { + const other = typeof data.valueOf == 'function' ? data.valueOf() : data + data = isObject(other) ? `${other}` : other + } + if (typeof data != 'string') { + return data === 0 ? data : +data + } + data = data.replace(reTrim, '') + const isBinary = reIsBinary.test(data) + return (isBinary || reIsOctal.test(data)) + ? freeParseInt(data.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(data) ? NAN : +data) +} diff --git a/src/core/util/asciiSize.js b/src/core/util/asciiSize.js new file mode 100644 index 0000000..fb4a75b --- /dev/null +++ b/src/core/util/asciiSize.js @@ -0,0 +1,3 @@ +export default function asciiSize({ length }) { + return length +} diff --git a/src/core/util/assocIndexOf.js b/src/core/util/assocIndexOf.js index 9119356..0a15dac 100644 --- a/src/core/util/assocIndexOf.js +++ b/src/core/util/assocIndexOf.js @@ -1,9 +1,9 @@ -import getKey from '../util/getKey' -import getSize from '../util/getSize' +import count from '../count' import eq from '../eq' +import getKey from '../getKey' export default function assocIndexOf(indexedTupple, key) { - let length = getSize(indexedTupple) + let length = count(indexedTupple) while (length--) { if (eq(getKey(indexedTupple, length)[0], key)) { return length diff --git a/src/core/util/assocKey.js b/src/core/util/assocKey.js deleted file mode 100644 index cb90b38..0000000 --- a/src/core/util/assocKey.js +++ /dev/null @@ -1,33 +0,0 @@ -import baseClone from '../base/baseClone' -import baseIsEqual from '../base/baseIsEqual' -//import Keyed from '../protocols/Keyed' -import isImmutable from '../isImmutable' -import isImmutableSeq from '../isImmutableSeq' -import isImmutableStack from '../isImmutableStack' -import isKeyed from '../isKeyed' -import getKey from './getKey' -import hasKey from './hasKey' - -export default function assocKey(data, key, value) { - const dataValue = getKey(data, key) - if (!(hasKey(data, key) && baseIsEqual(dataValue, value)) || (value === undefined && !(hasKey(data, key)))) { - if (isImmutable(data)) { - if (isImmutableSeq(data)) { - if (isKeyed(data)) { - //TODO BRN: This is SLOW... figure out a better way - data = data.map((othValue, othKey) => othKey === key ? value : othValue) - } else { - data = data.splice(key, 1, value) - } - } else if (isImmutableStack(data)) { - data = data.splice(key, 1, value) - } else { - data = data.set(key, value) - } - } else { - data = baseClone(data) - data[key] = value - } - } - return data -} diff --git a/src/core/util/dissocKey.js b/src/core/util/dissocKey.js deleted file mode 100644 index efcbb76..0000000 --- a/src/core/util/dissocKey.js +++ /dev/null @@ -1,15 +0,0 @@ -import baseClone from '../base/baseClone' -import hasKey from './hasKey' -import isImmutable from '../isImmutable' - -export default function dissocKey(data, key) { - if (hasKey(data, key)) { - if (isImmutable(data)) { - data = data.delete(key) - } else { - data = baseClone(data) - delete data[key] - } - } - return data -} diff --git a/src/core/util/getKey.js b/src/core/util/getKey.js deleted file mode 100644 index 5a1e414..0000000 --- a/src/core/util/getKey.js +++ /dev/null @@ -1,9 +0,0 @@ -import Keyed from '../protocols/Keyed' - -export default function getKey(data, key) { - if (data != null) { - return Keyed.is(data) - ? data.get(key) - : data[key] - } -} diff --git a/src/core/util/getMatchData.js b/src/core/util/getMatchData.js index 74c0f8b..bd60fe3 100644 --- a/src/core/util/getMatchData.js +++ b/src/core/util/getMatchData.js @@ -1,12 +1,12 @@ import baseKeys from '../base/baseKeys' -import getKey from './getKey' -import getSize from './getSize' +import count from '../count' +import getKey from '../getKey' +import setKey from '../setKey' import isStrictComparable from './isStrictComparable' -import setKey from './setKey' export default function getMatchData(object) { let result = baseKeys(object) - let length = getSize(result) + let length = count(result) while (length--) { const key = getKey(result, length) diff --git a/src/core/util/getSize.js b/src/core/util/getSize.js deleted file mode 100644 index 680923b..0000000 --- a/src/core/util/getSize.js +++ /dev/null @@ -1,8 +0,0 @@ -import _ from 'lodash' -import isImmutable from '../isImmutable' - -export default function getSize(data) { - return isImmutable(data) - ? data.count() - : _.size(data) -} diff --git a/src/core/util/hasKey.js b/src/core/util/hasKey.js deleted file mode 100644 index 6f0f7cf..0000000 --- a/src/core/util/hasKey.js +++ /dev/null @@ -1,12 +0,0 @@ -import { contextHasOwnProperty } from '../context' -import Keyed from '../protocols/Keyed' - -export default function hasKey(data, key) { - if (data != null) { - if (Keyed.is(data)) { - return data.has(key) - } - return contextHasOwnProperty.call(data, key) - } - return false -} diff --git a/src/core/util/hasKeyIn.js b/src/core/util/hasKeyIn.js deleted file mode 100644 index cc4e350..0000000 --- a/src/core/util/hasKeyIn.js +++ /dev/null @@ -1,9 +0,0 @@ -import isImmutable from '../isImmutable' - -export default function hasKeyIn(data, key) { - return data != null && (( - (isImmutable(data) && data.has(key)) - ) || ( - key in data - )) -} diff --git a/src/core/util/hasUnicode.js b/src/core/util/hasUnicode.js new file mode 100644 index 0000000..c6f7489 --- /dev/null +++ b/src/core/util/hasUnicode.js @@ -0,0 +1,5 @@ +import { reHasUnicode } from '../regex' + +export default function hasUnicode(string) { + return reHasUnicode.test(string) +} diff --git a/src/core/util/index.js b/src/core/util/index.js index 5a299a0..8b6d130 100644 --- a/src/core/util/index.js +++ b/src/core/util/index.js @@ -1,23 +1,19 @@ +export { default as asciiSize } from './asciiSize' export { default as assignValue } from './assignValue' export { default as assocIndexOf } from './assocIndexOf' -export { default as assocKey } from './assocKey' export { default as castFunction } from './castFunction' export { default as castPath } from './castPath' export { default as copyObject } from './copyObject' export { default as createSet } from './createSet' -export { default as dissocKey } from './dissocKey' export { default as getAllKeys } from './getAllKeys' export { default as getIteratee } from './getIteratee' -export { default as getKey } from './getKey' export { default as getMatchData } from './getMatchData' export { default as getNative } from './getNative' export { default as getRawTag } from './getRawTag' -export { default as getSize } from './getSize' export { default as getSymbols } from './getSymbols' export { default as getTag } from './getTag' export { default as getValue } from './getValue' -export { default as hasKey } from './hasKey' -export { default as hasKeyIn } from './hasKeyIn' +export { default as hasUnicode } from './hasUnicode' export { default as isForEachable } from './isForEachable' export { default as isMaskable } from './isMaskable' export { default as isMasked } from './isMasked' @@ -25,8 +21,7 @@ export { default as isStrictComparable } from './isStrictComparable' export { default as mapToArray } from './mapToArray' export { default as matchesStrictComparable } from './matchesStrictComparable' export { default as memoizeCapped } from './memoizeCapped' -export { default as setKey } from './setKey' export { default as setToArray } from './setToArray' +export { default as stringSize } from './stringSize' export { default as stringToPath } from './stringToPath' -export { default as toKey } from './toKey' -export { default as unsetKey } from './unsetKey' +export { default as unicodeSize } from './unicodeSize' diff --git a/src/core/util/matchesStrictComparable.js b/src/core/util/matchesStrictComparable.js index b1088ae..4f3769e 100644 --- a/src/core/util/matchesStrictComparable.js +++ b/src/core/util/matchesStrictComparable.js @@ -1,6 +1,6 @@ import { _Object } from '../context' -import getKey from './getKey' -import hasKeyIn from './hasKeyIn' +import getKey from '../getKey' +import hasKeyIn from '../hasKeyIn' export default function matchesStrictComparable(key, srcValue) { return function(object) { diff --git a/src/core/util/setKey.js b/src/core/util/setKey.js deleted file mode 100644 index 843479b..0000000 --- a/src/core/util/setKey.js +++ /dev/null @@ -1,17 +0,0 @@ -import baseIsEqual from '../base/baseIsEqual' -import isImmutable from '../isImmutable' -import getKey from './getKey' -import hasKey from './hasKey' - -//WARNING: This is a MUTABLE function -export default function setKey(object, key, value) { - const objValue = getKey(object, key) - if (!(hasKey(object, key) && baseIsEqual(objValue, value)) || (value === undefined && !(hasKey(object, key)))) { - if (isImmutable(object)) { - object = object.set(key, value) - } else { - object[key] = value - } - } - return object -} diff --git a/src/core/util/stringSize.js b/src/core/util/stringSize.js new file mode 100644 index 0000000..5602215 --- /dev/null +++ b/src/core/util/stringSize.js @@ -0,0 +1,7 @@ +import asciiSize from './asciiSize' +import hasUnicode from './hasUnicode' +import unicodeSize from './unicodeSize' + +export default function stringSize(string) { + return hasUnicode(string) ? unicodeSize(string) : asciiSize(string) +} diff --git a/src/core/util/unicodeSize.js b/src/core/util/unicodeSize.js new file mode 100644 index 0000000..7b7cae3 --- /dev/null +++ b/src/core/util/unicodeSize.js @@ -0,0 +1,9 @@ +import { reUnicode } from '../regex' + +export default function unicodeSize(string) { + let result = reUnicode.lastIndex = 0 + while (reUnicode.test(string)) { + ++result + } + return result +} diff --git a/src/core/util/unsetKey.js b/src/core/util/unsetKey.js deleted file mode 100644 index a496834..0000000 --- a/src/core/util/unsetKey.js +++ /dev/null @@ -1,11 +0,0 @@ -import isImmutable from '../isImmutable' - -//WARNING: This is a MUTABLE function -export default function unsetKey(data, key) { - if (isImmutable(data)) { - data = data.delete(key) - } else { - delete data[key] - } - return data -} diff --git a/src/core/with/withFor.js b/src/core/with/withFor.js index cbfe682..2a57e27 100644 --- a/src/core/with/withFor.js +++ b/src/core/with/withFor.js @@ -1,10 +1,10 @@ -import getKey from '../util/getKey' -import getSize from '../util/getSize' +import count from '../count' +import getKey from '../getKey' export default function baseFor(iterable, iteratee, keysFunc) { let index = -1 const props = keysFunc(iterable) - let size = getSize(props) + let size = count(props) while (size--) { const key = getKey(props, ++index)