Skip to content

Commit

Permalink
Add convenience methods
Browse files Browse the repository at this point in the history
  • Loading branch information
bloodyowl committed Apr 30, 2022
1 parent 419d084 commit 04a4662
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 0 deletions.
22 changes: 22 additions & 0 deletions docs/docs/array.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,28 @@ Returns the index of the item if there's an exact match, return the index of the
const index = Array.binarySearchBy(array, "my value");
```

## Array.zip(arrayA, arrayB)

Create an array of pairs from two arrays.

```ts
Array.zip([1, 2, 3], ["one", "two", "three"]);
// [[1, "one"], [2, "two"], [3, "three"]]
```

## Array.unzip(arrayOfPairs)

Turns an array of pairs into two arrays.

```ts
Array.zip([
[1, "one"],
[2, "two"],
[3, "three"],
]);
// [[1, 2, 3], ["one", "two", "three"]]
```

## Array.from(arrayLike)

[Array.from](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/from), reexported for convenience when Boxed `Array` shadows the `Array` constructor in scope.
Expand Down
31 changes: 31 additions & 0 deletions docs/docs/async-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,37 @@ AsyncData.all([Result.Loading(), AsyncData.Done(2), AsyncData.Done(3)]);
// Result.Loading()
```

## AsyncData.allFromDict(asyncDatas)

```ts
allFromDict(asyncDatas: Dict<AsyncData<A>>): AsyncData<Dict<A>>
```

Turns a "dict of asyncDatas of value" into a "asyncData of dict of value".

```ts
AsyncData.allFromDict({
a: AsyncData.Done(1),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// Done({a: 1, b: 2, c: 3})

AsyncData.allFromDict({
a: Result.NotAsked(),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// Result.NotAsked()

AsyncData.allFromDict({
a: Result.Loading(),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// Result.Loading()
```

## TS Pattern interop

```ts
Expand Down
17 changes: 17 additions & 0 deletions docs/docs/future.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,23 @@ Future.all([Future.value(1), Future.value(2), Future.value(3)]);
// Future<[1, 2, 3]>
```

## Future.allFromDict(futures)

```ts
allFromDict(futures: Dict<Future<A>>): Future<Dict<A>>
```

Turns a "dict of futures of values" into a "future of dict of value".

```ts
Future.allFromDict({
a: Future.value(1),
b: Future.value(2),
c: Future.value(3),
});
// Future<{a: 1, b: 2, c: 3}>
```

## Interop

### Future.fromPromise(promise)
Expand Down
16 changes: 16 additions & 0 deletions docs/docs/option.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,22 @@ Option.all([Option.None(), Option.Some(2), Option.Some(3)]);
// None
```

## Option.allFromDict(options)

```ts
allFromDict(options: Dict<Option<A>>): Option<Dict<A>>
```

Turns a "dict of options of value" into a "option of dict of value".

```ts
Option.allFromDict({ a: Option.Some(1), b: Option.Some(2), c: Option.Some(3) });
// Some({a: 1, b: 2, c: 3})

Option.allFromDict({ a: Option.None(), b: Option.Some(2), c: Option.Some(3) });
// None
```

## TS Pattern interop

```ts
Expand Down
20 changes: 20 additions & 0 deletions docs/docs/result.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,26 @@ Result.all([Result.Error("error"), Result.Ok(2), Result.Ok(3)]);
// Error("error")
```

## Result.allFromDict(results)

```ts
allFromDict(options: Dict<Result<A, E>>): Result<Dict<A>, E>
```

Turns a "dict of results of value" into a "result of dict of value".

```ts
Result.allFromDict({ a: Result.Ok(1), b: Result.Ok(2), c: Result.Ok(3) });
// Ok({a: 1, b: 2, c: 3})

Result.allFromDict({
a: Result.Error("error"),
b: Result.Ok(2),
c: Result.Ok(3),
});
// Error("error")
```

## Interop

### Result.fromExecution(() => value)
Expand Down
2 changes: 2 additions & 0 deletions src/Array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,5 @@ export const binarySearchBy = <A>(
}
}
};

export { unzip, zip } from "./ZipUnzip";
16 changes: 16 additions & 0 deletions src/AsyncData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { keys, values } from "./Dict";
import { Option } from "./OptionResult";
import { zip } from "./ZipUnzip";

export class AsyncData<A> {
/**
Expand Down Expand Up @@ -58,6 +60,20 @@ export class AsyncData<A> {
}
};

/**
* Turns an dict of asyncData into a asyncData of dict
*/
static allFromDict = <Dict extends Record<string, AsyncData<any>>>(
dict: Dict,
): AsyncData<{
-readonly [P in keyof Dict]: Dict[P] extends AsyncData<infer T> ? T : never;
}> => {
const dictKeys = keys(dict);
return AsyncData.all(values(dict)).map((values) =>
Object.fromEntries(zip(dictKeys, values)),
);
};

static equals = <A>(
a: AsyncData<A>,
b: AsyncData<A>,
Expand Down
16 changes: 16 additions & 0 deletions src/Future.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { keys, values } from "./Dict";
import { Result } from "./OptionResult";
import { zip } from "./ZipUnzip";

type PendingPayload<A> = {
resolveCallbacks?: Array<(value: A) => void>;
Expand Down Expand Up @@ -92,6 +94,20 @@ export class Future<A> {
}
};

/**
* Turns an dict of futures into a future of dict
*/
static allFromDict = <Dict extends Record<string, Future<any>>>(
dict: Dict,
): Future<{
-readonly [P in keyof Dict]: Dict[P] extends Future<infer T> ? T : never;
}> => {
const dictKeys = keys(dict);
return Future.all(values(dict)).map((values) =>
Object.fromEntries(zip(dictKeys, values)),
);
};

tag: "Pending" | "Cancelled" | "Resolved";
value?: A;
pending?: PendingPayload<A>;
Expand Down
40 changes: 40 additions & 0 deletions src/OptionResult.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { keys, values } from "./Dict";
import { zip } from "./ZipUnzip";

export class Option<A> {
/**
* Create an AsyncData.Some value
Expand Down Expand Up @@ -82,6 +85,20 @@ export class Option<A> {
}
};

/**
* Turns an dict of options into a options of dict
*/
static allFromDict = <Dict extends Record<string, Option<any>>>(
dict: Dict,
): Option<{
-readonly [P in keyof Dict]: Dict[P] extends Option<infer T> ? T : never;
}> => {
const dictKeys = keys(dict);
return Option.all(values(dict)).map((values) =>
Object.fromEntries(zip(dictKeys, values)),
);
};

static equals = <A>(
a: Option<A>,
b: Option<A>,
Expand Down Expand Up @@ -324,6 +341,29 @@ export class Result<A, E> {
}
};

/**
* Turns an dict of results into a results of dict
*/
static allFromDict = <Dict extends Record<string, Result<any, any>>>(
dict: Dict,
): Result<
{
-readonly [P in keyof Dict]: Dict[P] extends Result<infer T, any>
? T
: never;
},
{
-readonly [P in keyof Dict]: Dict[P] extends Result<any, infer E>
? E
: never;
}[keyof Dict]
> => {
const dictKeys = keys(dict);
return Result.all(values(dict)).map((values) =>
Object.fromEntries(zip(dictKeys, values)),
);
};

static equals = <A, E>(
a: Result<A, E>,
b: Result<A, E>,
Expand Down
51 changes: 51 additions & 0 deletions src/ZipUnzip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export const unzip = <TupleArray extends [any, any][]>(array: TupleArray) => {
const length = array.length;
const arrayA = Array(length);
const arrayB = Array(length);
let index = -1;
while (++index < length) {
const match = array[index];
if (match !== undefined) {
arrayA[index] = match[0];
arrayB[index] = match[1];
}
}
return [arrayA, arrayB] as unknown as [
{
[I in keyof TupleArray]: TupleArray[I] extends [any, any]
? TupleArray[I][0] extends infer T
? T
: never
: never;
},
{
[I in keyof TupleArray]: TupleArray[I] extends [any, any]
? TupleArray[I][1] extends infer T
? T
: never
: never;
},
];
};

export const zip = <ArrayA extends any[], ArrayB extends any[]>(
arrayA: ArrayA,
arrayB: ArrayB,
) => {
const length = Math.min(arrayA.length, arrayB.length);
const array = Array(length);
let index = -1;
while (++index < length) {
array[index] = [arrayA[index], arrayB[index]];
}
return array as unknown as Array<
[
{
[I in keyof ArrayA]: ArrayA[I] extends infer T ? T : never;
}[number],
{
[I in keyof ArrayB]: ArrayB[I] extends infer T ? T : never;
}[number],
]
>;
};
31 changes: 31 additions & 0 deletions test/Array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
isArray,
keepMap,
of,
unzip,
zip,
} from "../src/Array";
import { Option } from "../src/OptionResult";

Expand Down Expand Up @@ -66,3 +68,32 @@ test("Array.of", () => {
expect(isArray([])).toEqual(true);
expect(isArray({ length: 0 })).toEqual(false);
});

test("Array.zip", () => {
expect(zip([1, 2, 3], ["one", "two", "three"])).toEqual([
[1, "one"],
[2, "two"],
[3, "three"],
]);
expect(zip([1, 2], ["one", "two", "three"])).toEqual([
[1, "one"],
[2, "two"],
]);
expect(zip([1, 2, 3], ["one", "two"])).toEqual([
[1, "one"],
[2, "two"],
]);
});

test("Array.unzip", () => {
expect(
unzip([
[1, "one"],
[2, "two"],
[3, "three"],
]),
).toEqual([
[1, 2, 3],
["one", "two", "three"],
]);
});
Loading

0 comments on commit 04a4662

Please sign in to comment.