From 773e81e1a2a24cbac1524422aae95b56bddb0009 Mon Sep 17 00:00:00 2001 From: bloodyowl Date: Sat, 20 Apr 2024 09:16:09 +0200 Subject: [PATCH 1/3] Add Dict.fromEntries --- src/Dict.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Dict.ts b/src/Dict.ts index bd6a5d3..a2019c7 100644 --- a/src/Dict.ts +++ b/src/Dict.ts @@ -1,5 +1,7 @@ import { LooseRecord } from "./types"; +export const fromEntries = Object.fromEntries; + export const entries = >(value: T) => { return Object.entries(value) as { [K in keyof T]-?: [K, T[K]]; From cc6829ca236102e0d927e121e119ad7a63181040 Mon Sep 17 00:00:00 2001 From: bloodyowl Date: Sat, 20 Apr 2024 09:42:59 +0200 Subject: [PATCH 2/3] Add Dict.fromOptional --- src/Dict.ts | 27 +++++++++++++++++++++++++++ test/Dict.test.ts | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/Dict.ts b/src/Dict.ts index a2019c7..58f6c61 100644 --- a/src/Dict.ts +++ b/src/Dict.ts @@ -1,3 +1,4 @@ +import { Option } from "./OptionResult"; import { LooseRecord } from "./types"; export const fromEntries = Object.fromEntries; @@ -13,3 +14,29 @@ export const keys = >(value: T) => export const values = >(value: T) => Object.values(value) as T[keyof T][]; + +const hasOwnProperty = Object.prototype.hasOwnProperty; + +export const fromOptional = >>( + dict: Dict, +): { + [K in keyof Dict]?: Dict[K] extends Option ? T : never; +} => { + const result: Record = {}; + 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 ? T : never; + }; +}; diff --git a/test/Dict.test.ts b/test/Dict.test.ts index 72a6b06..1d0b673 100644 --- a/test/Dict.test.ts +++ b/test/Dict.test.ts @@ -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([ @@ -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({}); +}); From 62cd2a3c4b4180a72a7545bbaa6092cefaf05289 Mon Sep 17 00:00:00 2001 From: bloodyowl Date: Sat, 20 Apr 2024 09:45:11 +0200 Subject: [PATCH 3/3] Add docs --- docs/docs/dict.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/docs/dict.md b/docs/docs/dict.md index 539ae70..8385e12 100644 --- a/docs/docs/dict.md +++ b/docs/docs/dict.md @@ -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` 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(), +}); +// {} +```