Skip to content

Commit

Permalink
Merge pull request #75 from swan-io/dict-utils
Browse files Browse the repository at this point in the history
dict utils
  • Loading branch information
bloodyowl authored Apr 20, 2024
2 parents 509d721 + 62cd2a3 commit b750f05
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 1 deletion.
27 changes: 27 additions & 0 deletions docs/docs/dict.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,30 @@ Contrary to the TS bindings for `Object.values`, the types are refined.
const index = Dict.values({ foo: 1, bar: 2, baz: 3 });
// [1, 2, 3];
```

## Dict.fromOptional(dictOfOptions)

Takes a dict whose values are `Option<unknown>` and returns a dict containing only the values contained in `Some`.

```ts title="Examples"
Dict.fromOptional({
foo: Option.Some(1),
bar: Option.None(),
baz: Option.None(),
});
// {foo: 1}

Dict.fromOptional({
foo: Option.Some(1),
bar: Option.Some(2),
baz: Option.None(),
});
// {foo: 1, bar: 2}

Dict.fromOptional({
foo: Option.None(),
bar: Option.None(),
baz: Option.None(),
});
// {}
```
29 changes: 29 additions & 0 deletions src/Dict.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Option } from "./OptionResult";
import { LooseRecord } from "./types";

export const fromEntries = Object.fromEntries;

export const entries = <T extends LooseRecord<unknown>>(value: T) => {
return Object.entries(value) as {
[K in keyof T]-?: [K, T[K]];
Expand All @@ -11,3 +14,29 @@ export const keys = <T extends LooseRecord<unknown>>(value: T) =>

export const values = <T extends LooseRecord<unknown>>(value: T) =>
Object.values(value) as T[keyof T][];

const hasOwnProperty = Object.prototype.hasOwnProperty;

export const fromOptional = <Dict extends LooseRecord<Option<any>>>(
dict: Dict,
): {
[K in keyof Dict]?: Dict[K] extends Option<infer T> ? T : never;
} => {
const result: Record<PropertyKey, unknown> = {};
for (const key in dict) {
if (hasOwnProperty.call(dict, key)) {
const item = dict[key];
if (item === undefined) {
// should happen, but let's make the compiler happy
continue;
}
if (item.isSome()) {
result[key] = item.get();
}
}
}

return result as {
[K in keyof Dict]?: Dict[K] extends Option<infer T> ? T : never;
};
};
35 changes: 34 additions & 1 deletion test/Dict.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, test } from "vitest";
import { entries, keys, values } from "../src/Dict";
import { entries, fromOptional, keys, values } from "../src/Dict";
import { Option } from "../src/OptionResult";

test("Dict.entries", () => {
expect(entries({ foo: 1, bar: 2 })).toEqual([
Expand All @@ -15,3 +16,35 @@ test("Dict.keys", () => {
test("Dict.values", () => {
expect(values({ foo: 1, bar: 2 })).toEqual([1, 2]);
});

test("Dict.fromOptional", () => {
expect(fromOptional({})).toEqual({});
expect(
fromOptional({
a: Option.Some(1),
b: Option.Some(2),
c: Option.Some(3),
}),
).toEqual({ a: 1, b: 2, c: 3 });
expect(
fromOptional({
a: Option.None(),
b: Option.Some(2),
c: Option.Some(3),
}),
).toEqual({ b: 2, c: 3 });
expect(
fromOptional({
a: Option.Some(1),
b: Option.None(),
c: Option.Some(3),
}),
).toEqual({ a: 1, c: 3 });
expect(
fromOptional({
a: Option.None(),
b: Option.None(),
c: Option.None(),
}),
).toEqual({});
});

0 comments on commit b750f05

Please sign in to comment.