Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/fix list builder #47

Merged
merged 3 commits into from
Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 25 additions & 13 deletions deno_dist/collection-types/map/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,11 @@ export interface RMapBase<K, V, Tp extends RMapBase.Types = RMapBase.Types>
* @example
* HashMap.of([1, 'a']).addEntries([[2, 'b']]).toArray() // => [[1, 'a'], [2, 'b']]
*/
addEntries(
entries: StreamSource.NonEmpty<readonly [K, V]>
addEntries<V2 extends V = V>(
entries: StreamSource.NonEmpty<readonly [K, V2]>
): WithKeyValue<Tp, K, V>['nonEmpty'];
addEntries(
entries: StreamSource<readonly [K, V]>
addEntries<V2 extends V = V>(
entries: StreamSource<readonly [K, V2]>
): WithKeyValue<Tp, K, V>['normal'];
/**
* Returns the collection with the given `atKey` key modified according to given `options`.
Expand Down Expand Up @@ -477,11 +477,12 @@ export namespace RMapBase {
* the tuple will be filled with the given `fillValue`.
* @typeparam O - the type of the fill value
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @param fillValue - the value to use for the result tuple if a source does not have a certain key
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.mergeAll('none', [[2, true]], HashMap.of([3, 15]))
* const m2 = HashMap.mergeAll('none', m, [[2, true]], HashMap.of([3, 15]))
* // type of m2: HashMap<number, [string, boolean | string, number | string]>
* console.log(m2.toArray())
* // => [[1, ['a', 'none', 'none']], [2, ['b', true, 'none']], [3, ['none', 'none', 15]]]
Expand Down Expand Up @@ -510,17 +511,23 @@ export namespace RMapBase {
* Returns a Map containing all keys from this map and all the given `sources` key-value stream sources,
* and as values the result of applying the given `mergeFun` to the key and all the corresponding values for each key. If a source doesn't have a key,
* the given tuple will be filled with the given `fillValue`.
* @typeparam O - the type of the fill value
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @typeparam O - the type of the fill value
* @typeparam R - the resulting Map value type
* @param fillValue - the value to use for the result tuple if a source does not have a certain key
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @param mergeFun - a function that receives each key of the given sources and, if present, the corresponding source values, or the given fill value otherwise,
* and returns the result value to use in the resulting map.
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.mergeAllWith(
* 'q',
* (key, v1, v2, v3) => `${key}${v1}${v2}${v3}`,
* const m2 = HashMap.mergeAllWith(
* m
* [[2, 'c']],
* HashMap.of([3, 'd'])
* )(
* 'q',
* (key, v1, v2, v3) => `${key}${v1}${v2}${v3}`
* )
* // type of m2: HashMap<number, string>
* console.log(m2.toArray())
Expand Down Expand Up @@ -553,10 +560,11 @@ export namespace RMapBase {
* and as values tuples of all the corresponding values for each common key. If a source doesn't have a key,
* the key will be skipped.
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.merge([[2, true]], HashMap.of([2, 15]))
* const m2 = HashMap.merge(m, [[2, true]], HashMap.of([2, 15]))
* // type of m2: HashMap<number, [string, boolean, number]>
* console.log(m2.toArray())
* // => [[2, ['b', true, 15]]]
Expand All @@ -576,15 +584,19 @@ export namespace RMapBase {
* and as values the result of applying given `mergeFun` to the key and values of all the corresponding values for each common key.
* If a source doesn't have a key, the key will be skipped.
* @typeparam I - the array of input source value types
* @typeparam K - the common key type
* @typeparam R - the resulting Map value type
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @param mergeFun - a function taking the key and values from this map and all sources corresponding to the key, and
* returning a value for the resulting Map.
* @param sources - a non-empty set of StreamSouces containing tuples of keys and values
* @example
* const m = HashMap.of([1, 'a'], [2, 'b'])
* const m2 = m.merge(
* (key, v1, v2) => `${key}${v1}${v2}`,
* const m2 = HashMap.mergeWith(
* m,
* [[2, true]],
* HashMap.of([2, 15])
* )(
* (key, v1, v2) => `${key}${v1}${v2}`,
* )
* // type of m2: HashMap<number, string>
* console.log(m2.toArray())
Expand Down
9 changes: 5 additions & 4 deletions deno_dist/common/async-optlazy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OptLazy } from './internal.ts';
import type { OptLazy } from './internal.ts';

/**
* A type that is either a value T or a promise yielding a value of type T.
Expand All @@ -23,9 +23,10 @@ export namespace AsyncOptLazy {
* AsyncOptLazy.toMaybePromise(async () => 1) // => Promise(1)
* AsyncOptLazy.toMaybePromise(Promise.resolve(1)) // => Promise(1)
*/
export const toMaybePromise: <T>(
optLazy: AsyncOptLazy<T>
) => MaybePromise<T> = OptLazy;
export function toMaybePromise<T>(optLazy: AsyncOptLazy<T>): MaybePromise<T> {
if (optLazy instanceof Function) return optLazy();
return optLazy;
}

/**
* Returns the value contained in an `AsyncOptLazy` instance of type T as a promise.
Expand Down
11 changes: 8 additions & 3 deletions deno_dist/list/builder/gen-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,15 @@ export class GenBuilder<T> implements List.Builder<T> {
if (undefined === this.builder) {
this.builder = this.context.leafBlockBuilder([value]);
} else {
if (index === 0) return this.prepend(value);
if (index > this.length || -index > this.length + 1)
if (index === 0) {
return this.prepend(value);
}
if (index > this.length || -index > this.length + 1) {
return this.append(value);
if (index < 0) return this.insert(this.length + index, value);
}
if (index < 0) {
return this.insert(this.length + index, value);
}

this.builder.insert(index, value);
this.builder = this.builder.normalized();
Expand Down
146 changes: 146 additions & 0 deletions deno_dist/list/builder/leaf/tree-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import type { OptLazy } from '../../../common/mod.ts';
import type {
LeafBlockBuilder,
LeafBuilder,
LeafTree,
ListContext,
NonLeafBuilder,
} from '../../list-custom.ts';
import { createNonLeaf, TreeBuilderBase } from '../../list-custom.ts';

export class LeafTreeBuilder<T>
extends TreeBuilderBase<T, T>
implements LeafBuilder<T>
{
constructor(
readonly context: ListContext,
public source?: LeafTree<T>,
public _left?: LeafBlockBuilder<T>,
public _right?: LeafBlockBuilder<T>,
public _middle?: NonLeafBuilder<T, LeafBlockBuilder<T>>,
public length = source?.length ?? 0
) {
super();
}

prepareMutate(): void {
if (undefined !== this.source) {
this._left = this.context.leafBlockBuilderSource(this.source.left);
this._right = this.context.leafBlockBuilderSource(this.source.right);
this._middle =
null === this.source.middle
? undefined
: (createNonLeaf<T>(this.source.middle) as any);
this.source = undefined;
}
}

get level(): 0 {
return 0;
}

get left(): LeafBlockBuilder<T> {
this.prepareMutate();
return this._left!;
}

set left(value: LeafBlockBuilder<T>) {
this.prepareMutate();
this._left = value;
}

get right(): LeafBlockBuilder<T> {
this.prepareMutate();
return this._right!;
}

set right(value: LeafBlockBuilder<T>) {
this.prepareMutate();
this._right = value;
}

get middle(): NonLeafBuilder<T, LeafBlockBuilder<T>> | undefined {
this.prepareMutate();
return this._middle;
}

set middle(value: NonLeafBuilder<T, LeafBlockBuilder<T>> | undefined) {
this.prepareMutate();
this._middle = value;
}

getChildLength(): 1 {
return 1;
}

appendChildren(children: T[], from: number): void {
if (children.length === 0 || from >= children.length) return;

if (this.right.nrChildren < this.context.maxBlockSize) {
const items = children.slice(
from,
from + this.context.maxBlockSize - this.right.nrChildren
);
this.right.children = this.right.children.concat(items);
this.length += items.length;
this.appendChildren(children, from + items.length);
return;
}

const block = children.slice(from, from + this.context.maxBlockSize);
this.appendMiddle(this.right);
this.right = this.context.leafBlockBuilder(block);
this.length += block.length;
this.appendChildren(children, from + block.length);
}

normalized(): LeafBuilder<T> {
if (this.length <= this.context.maxBlockSize) {
this.left.concat(this.right);
return this.left;
}

if (undefined !== this.middle) {
if (this.middle.length + this.left.length <= this.context.maxBlockSize) {
this.left.concat(this.middle.first());
this.middle = undefined;
} else if (
this.middle.length + this.right.length <=
this.context.maxBlockSize
) {
const newRight = this.middle.last();
newRight.concat(this.right);
this.right = newRight;
this.middle = undefined;
}
}

return this;
}

get<O>(index: number, otherwise?: OptLazy<O>): T | O {
if (undefined !== this.source) return this.source.get(index, otherwise);

return super.get(index, otherwise);
}

build(): LeafTree<T> {
if (undefined !== this.source) return this.source;

return this.context.leafTree(
this.left.build(),
this.right.build(),
undefined === this.middle ? null : this.middle.build()
);
}

buildMap<T2>(f: (value: T) => T2): LeafTree<T2> {
if (undefined !== this.source) return this.source.map(f);

return this.context.leafTree(
this.left.buildMap(f),
this.right.buildMap(f),
undefined === this.middle ? null : (this.middle as any).buildMap(f)
);
}
}