From 6f2765db765e872f996328df94fcabe9f8a421c6 Mon Sep 17 00:00:00 2001 From: Dave Coates Date: Mon, 12 Sep 2016 09:06:26 +1000 Subject: [PATCH 1/2] List.merge* support --- src/list.js | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ src/test/list.js | 38 +++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/list.js b/src/list.js index 978272b..7e410a8 100644 --- a/src/list.js +++ b/src/list.js @@ -28,6 +28,35 @@ const change = (list, f) => { } } +const maxSizeFromIterables = (iterables) => { + let maxSize = 0; + for (let i = 0; i < iterables.length; i++) { + const iter = Indexed(iterables[i]); + if (iter.size > maxSize) { + maxSize = iter.size; + } + } + return maxSize; +} + +const convertValuesToType = (type, values) => { + const items = [] + const iter = Indexed(values); + let index = 0 + while (index < iter.size) { + const value = iter.get(index) + const result = type[$read](value) + + if (result instanceof TypeError) { + throw TypeError(`Invalid value: ${result.message}`) + } + + items.push(result) + index = index + 1 + } + return items; +} + const clear = target => target.clear() const pop = target => target.pop() const shift = target => target.shift() @@ -236,6 +265,39 @@ class TypeInferedList extends BaseImmutableList { return this[$store].wasAltered() } + merge(...iterables) { + const maxSize = maxSizeFromIterables(iterables); + const typedIterables = iterables.map(convertValuesToType.bind(null, this[$type])); + if (maxSize > this.size) { + return change(this, store => store.setSize(maxSize)).merge(...typedIterables); + } + return change(this, store => store.merge(...typedIterables)); + } + + mergeWith(merger, ...iterables) { + const maxSize = maxSizeFromIterables(iterables); + if (maxSize > this.size) { + return change(this, store => store.setSize(maxSize).mergeWith(merger, ...iterables)); + } + return change(this, store => store.mergeWith(merger, ...iterables)); + } + + mergeDeep(...iterables) { + const maxSize = maxSizeFromIterables(iterables); + if (maxSize > this.size) { + return change(this, store => store.setSize(maxSize).mergeDeep(...iterables)); + } + return change(this, store => store.mergeDeep(...iterables)); + } + + mergeDeepWith(merger, ...iterables) { + const maxSize = maxSizeFromIterables(iterables); + if (maxSize > this.size) { + return change(this, store => store.setSize(maxSize).mergeDeepWith(merger, ...iterables)); + } + return change(this, store => store.mergeDeepWith(merger, ...iterables)); + } + __ensureOwner(ownerID) { const result = this.__ownerID === ownerID ? this : !ownerID ? this : diff --git a/src/test/list.js b/src/test/list.js index facddff..8edddad 100644 --- a/src/test/list.js +++ b/src/test/list.js @@ -5,6 +5,7 @@ import {List} from "../list" import {Typed, Union, Maybe} from "../typed" const NumberList = List(Number) +const NumberListOfNumbers = List(NumberList) const StringList = List(String) const Point = Record({x: Number(0), y: Number(0)}, @@ -1015,3 +1016,40 @@ test('flatMap', assert => { }) +test('merge', assert => { + const numbers = NumberList.of(1, 2, 3) + assert.deepEqual(numbers.merge(NumberList.of(4, 5, 6, 7)).toArray(), [4, 5, 6, 7]) + assert.deepEqual(numbers.merge(NumberList.of(4)).toArray(), [4, 2, 3]) + assert.throws(() => numbers.merge([1,2], [4, 5, 6, '7']), /is not a number/) +}) + +test('mergeWith', assert => { + const numbers = NumberList.of(1, 2, 3) + const useExisting = (prev, next) => prev != null ? prev : next; + assert.deepEqual(numbers.mergeWith(useExisting, NumberList.of(4, 5, 6, 7)).toArray(), [1, 2, 3, 7]) + assert.deepEqual(numbers.mergeWith(useExisting, NumberList.of(4)).toArray(), [1, 2, 3]) + assert.throws(() => numbers.merge(useExisting, [4, 5, 6, '7']), /is not a number/) +}) + +test('mergeDeep', assert => { + var numbers = NumberListOfNumbers.of([1, 2, 3], [4, 5, 6]) + assert.deepEqual( + numbers.mergeDeep([[10], [20, 21], [30]]).toJS(), + [[10, 2, 3], [20, 21, 6], [30]]) + assert.deepEqual( + numbers.mergeDeep([[10, 11, 12, 13], [20, 21]]).toJS(), + [[10, 11, 12, 13], [20, 21, 6]]) + assert.throws(() => numbers.mergeDeep([[10], ['11']], /is not a number/) +}) + +test('mergeDeepWith', assert => { + var numbers = NumberListOfNumbers.of([1, 2, 3], [4, 5, 6]) + const add = (prev, next) => (prev || 0) + next; + assert.deepEqual( + numbers.mergeDeepWith(add, [[10], [20, 21]]).toJS(), + [[11, 2, 3], [24, 26, 6]]) + assert.deepEqual( + numbers.mergeDeepWith(add, [[10, 11, 12, 13], [20, 21]]).toJS(), + [[11, 13, 15, 13], [24, 26, 6]]) + assert.throws(() => numbers.mergeDeep(add, [[10], ['11']], /is not a number/) +}) From 0db3ae0613b401a65447e3cd613a82f10ec7c725 Mon Sep 17 00:00:00 2001 From: Dave Coates Date: Mon, 12 Sep 2016 16:06:19 +1000 Subject: [PATCH 2/2] Fix type enforcement for mergeWith/deep + tests --- src/list.js | 15 +++++++++------ src/test/list.js | 6 +++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/list.js b/src/list.js index 7e410a8..e02fbe3 100644 --- a/src/list.js +++ b/src/list.js @@ -276,26 +276,29 @@ class TypeInferedList extends BaseImmutableList { mergeWith(merger, ...iterables) { const maxSize = maxSizeFromIterables(iterables); + const typedIterables = iterables.map(convertValuesToType.bind(null, this[$type])); if (maxSize > this.size) { - return change(this, store => store.setSize(maxSize).mergeWith(merger, ...iterables)); + return change(this, store => store.setSize(maxSize).mergeWith(merger, ...typedIterables)); } - return change(this, store => store.mergeWith(merger, ...iterables)); + return change(this, store => store.mergeWith(merger, ...typedIterables)); } mergeDeep(...iterables) { const maxSize = maxSizeFromIterables(iterables); + const typedIterables = iterables.map(convertValuesToType.bind(null, this[$type])); if (maxSize > this.size) { - return change(this, store => store.setSize(maxSize).mergeDeep(...iterables)); + return change(this, store => store.setSize(maxSize).mergeDeep(...typedIterables)); } - return change(this, store => store.mergeDeep(...iterables)); + return change(this, store => store.mergeDeep(...typedIterables)); } mergeDeepWith(merger, ...iterables) { const maxSize = maxSizeFromIterables(iterables); + const typedIterables = iterables.map(convertValuesToType.bind(null, this[$type])); if (maxSize > this.size) { - return change(this, store => store.setSize(maxSize).mergeDeepWith(merger, ...iterables)); + return change(this, store => store.setSize(maxSize).mergeDeepWith(merger, ...typedIterables)); } - return change(this, store => store.mergeDeepWith(merger, ...iterables)); + return change(this, store => store.mergeDeepWith(merger, ...typedIterables)); } __ensureOwner(ownerID) { diff --git a/src/test/list.js b/src/test/list.js index 8edddad..7329759 100644 --- a/src/test/list.js +++ b/src/test/list.js @@ -1028,7 +1028,7 @@ test('mergeWith', assert => { const useExisting = (prev, next) => prev != null ? prev : next; assert.deepEqual(numbers.mergeWith(useExisting, NumberList.of(4, 5, 6, 7)).toArray(), [1, 2, 3, 7]) assert.deepEqual(numbers.mergeWith(useExisting, NumberList.of(4)).toArray(), [1, 2, 3]) - assert.throws(() => numbers.merge(useExisting, [4, 5, 6, '7']), /is not a number/) + assert.throws(() => numbers.mergeWith(useExisting, [4, 5, 6, '7']), /is not a number/) }) test('mergeDeep', assert => { @@ -1039,7 +1039,7 @@ test('mergeDeep', assert => { assert.deepEqual( numbers.mergeDeep([[10, 11, 12, 13], [20, 21]]).toJS(), [[10, 11, 12, 13], [20, 21, 6]]) - assert.throws(() => numbers.mergeDeep([[10], ['11']], /is not a number/) + assert.throws(() => numbers.mergeDeep([[10], ['11']]), /is not a number/) }) test('mergeDeepWith', assert => { @@ -1051,5 +1051,5 @@ test('mergeDeepWith', assert => { assert.deepEqual( numbers.mergeDeepWith(add, [[10, 11, 12, 13], [20, 21]]).toJS(), [[11, 13, 15, 13], [24, 26, 6]]) - assert.throws(() => numbers.mergeDeep(add, [[10], ['11']], /is not a number/) + assert.throws(() => numbers.mergeDeepWith(add, [[10], ['11']]), /is not a number/) })