diff --git a/.changeset/angry-trainers-remember.md b/.changeset/angry-trainers-remember.md new file mode 100644 index 00000000..8ca3ac73 --- /dev/null +++ b/.changeset/angry-trainers-remember.md @@ -0,0 +1,5 @@ +--- +"@tsplus/stdlib": patch +--- + +Fix HashMap Modify diff --git a/packages/stdlib/_src/collections/HashMap/_internal/node.ts b/packages/stdlib/_src/collections/HashMap/_internal/node.ts index 64b4d285..ba4be95f 100644 --- a/packages/stdlib/_src/collections/HashMap/_internal/node.ts +++ b/packages/stdlib/_src/collections/HashMap/_internal/node.ts @@ -214,15 +214,23 @@ export class IndexedNode { const bit = toBitmap(frag) const indx = fromBitmap(mask, bit) const exists = mask & bit - const current = exists ? children[indx]! : new EmptyNode() + const canEdit = canEditNode(this, edit) + + if (!exists) { + const _newChild = new EmptyNode().modify(edit, shift + SIZE, f, hash, key, size) + if (!_newChild) return this + return children.length >= MAX_INDEX_NODE ? + expand(edit, frag, _newChild, mask, children) : + new IndexedNode(edit, mask | bit, arraySpliceIn(canEdit, indx, _newChild, children)) + } + + const current = children[indx]! const child = current.modify(edit, shift + SIZE, f, hash, key, size) if (current === child) return this - - const canEdit = canEditNode(this, edit) let bitmap = mask let newChildren - if (exists && isEmptyNode(child)) { + if (isEmptyNode(child)) { // remove bitmap &= ~bit if (!bitmap) return new EmptyNode() @@ -231,14 +239,6 @@ export class IndexedNode { } newChildren = arraySpliceOut(canEdit, indx, children) - } else if (!exists && !isEmptyNode(child)) { - // add - if (children.length >= MAX_INDEX_NODE) { - return expand(edit, frag, child, mask, children) - } - - bitmap |= bit - newChildren = arraySpliceIn(canEdit, indx, child, children) } else { // modify newChildren = arrayUpdate(canEdit, indx, child, children) @@ -249,6 +249,7 @@ export class IndexedNode { this.children = newChildren return this } + return new IndexedNode(edit, bitmap, newChildren) } } diff --git a/packages/stdlib/_test/Differ.test.ts b/packages/stdlib/_test/Differ.test.ts index d3a3829e..fe8e99b4 100644 --- a/packages/stdlib/_test/Differ.test.ts +++ b/packages/stdlib/_test/Differ.test.ts @@ -1,8 +1,16 @@ +import { it as it_ } from "vitest" + function diffLaws( differ: Differ, gen: () => Value, equal: (a: Value, b: Value) => boolean ): void { + const it = (name: string, f: () => void) => + it_(name, () => { + for (let i = 0; i < 100; i++) { + f() + } + }) describe.concurrent("differ laws", () => { it("combining patches is associative", () => { const value1 = gen() @@ -57,9 +65,9 @@ function randomChunk(): Chunk { return Chunk.fill(20, smallInt) } -// function randomHashMap(): HashMap { -// return HashMap.from(Chunk.fill(20, smallInt).zip(Chunk.fill(20, smallInt))) -// } +function randomHashMap(): HashMap { + return HashMap.from(Chunk.fill(2, smallInt).zip(Chunk.fill(2, smallInt))) +} function randomHashSet(): HashSet { return HashSet.from(Chunk.fill(20, smallInt)) @@ -93,7 +101,7 @@ describe.concurrent("Differ", () => { describe.concurrent("hashMap", () => { diffLaws( Differ.hashMap number>(Differ.update()), - () => HashMap(Tuple(1, 2), Tuple(3, 4), Tuple(5, 6)), + randomHashMap, Equals.equals ) }) diff --git a/packages/stdlib/_test/HashMap.test.ts b/packages/stdlib/_test/HashMap.test.ts index 3715fd98..e99b9fc0 100644 --- a/packages/stdlib/_test/HashMap.test.ts +++ b/packages/stdlib/_test/HashMap.test.ts @@ -287,6 +287,11 @@ describe.concurrent("HashMap", () => { assert.isTrue(result[key(1)] == Maybe.some(value("b"))) }) + it("remove non existing key doesn't change the array", () => { + const map = HashMap(Tuple(13, 95), Tuple(90, 4)) + assert.deepEqual(map.remove(75).keySet.toArray, map.keySet.toArray) + }) + it("removeMany", () => { const hashMap = HashMap(Tuple(key(0), value("a")), Tuple(key(1), value("b")))