Skip to content

Instantly share code, notes, and snippets.

@jLouzado
Last active September 16, 2020 06:25
Show Gist options
  • Save jLouzado/1a521481cafa36451d073e493230de6f to your computer and use it in GitHub Desktop.
Save jLouzado/1a521481cafa36451d073e493230de6f to your computer and use it in GitHub Desktop.
Possible example for types-driven-development. Repl: https://www.typescriptlang.org/play
type EventsWithFilters = Record<
string,
{isSelected: boolean; filters: Filters}
> | null
type Filters = Record<string, {isSelected: boolean; values: Values}> | null
type Values = Record<string, boolean> | null
type SelectedFilters = {
event: string
filters: {
filter: string
values: string[]
}[]
}[]
/**
* Behaviour:
* return if `input` is null
* per `event`:
* - ignore if:
* - `filters` null
* - !selected
* - otherwise process `filters`
* per `filter`:
* - ignore if:
* - !selected
* - `values` null
* - otherwise process `values`
* per `value`:
* - is it selected
*/
type Requirement = (input: EventsWithFilters) => SelectedFilters | null
const solution: Requirement = (input) =>
input !== null
? Object.keys(input)
// transform from Object to Array
.map((event) => ({
event,
filters: input[event].filters
}))
// filter out unselected filters and all the nested nulls
.map(removeAllNulls)
.map((val) => ({
event: val.event,
filters: Object.keys(val.filters)
.map((f) => ({filter: f, values: val.filters[f]}))
// remove unselected values
.map(({filter, values}) => ({
filter,
values: Object.keys(values).filter((v) => values[v])
}))
}))
: null
/** in practice this would be several steps */
declare const removeAllNulls: (
x: {event: string; filters: Filters}
) => {event: string; filters: Record<string, NonNullable<Values>>}
type EventsWithFilters = Record<
string,
{isSelected: boolean; filters: Filters}
> | null
type Filters = Record<string, {isSelected: boolean; values: Values}> | null
type Values = Record<string, boolean> | null
type SelectedFilters = {
event: string
filters: {
filter: string
values: string[]
}[]
}[]
/**
* Behaviour:
* return if `input` is null
* per `event`:
* - ignore if:
* - `filters` null
* - !selected
* - otherwise process `filters`
* per `filter`:
* - ignore if:
* - !selected
* - `values` null
* - otherwise process `values`
* per `value`:
* - is it selected
*/
type Requirement = (input: EventsWithFilters) => SelectedFilters | null
const solution: Requirement = (input) =>
input !== null
? Object.keys(input)
// transform from Object to Array
.map((event) => ({
event,
filters: input[event].filters
}))
// remove filters that are null
.filter(
(
val
): val is {
event: string
filters: NonNullable<Filters>
} => val.filters !== null
)
// remove values that are null
.map((val) => ({
event: val.event,
filters: Object.keys(val.filters).reduce(
(
acc: Record<
string,
{values: NonNullable<Values>; selected: boolean}
>,
f
) => {
const values = val.filters[f].values
return values === null
? acc
: {
...acc,
[f]: {
selected: val.filters[f].isSelected,
values
}
}
},
{}
)
}))
// remove filters that are unselected
.map((val) => ({
event: val.event,
filters: Object.keys(val.filters).reduce(
(acc: Record<string, NonNullable<Values>>, f) => {
const {selected, values} = val.filters[f]
return selected
? {
...acc,
[f]: values
}
: acc
},
{}
)
}))
// final shape transformation
.map((val) => ({
event: val.event,
filters: Object.keys(val.filters)
.map((f) => ({filter: f, values: val.filters[f]}))
// remove unselected values
.map(({filter, values}) => ({
filter,
values: Object.keys(values).filter((v) => values[v])
}))
}))
: null
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment