Created
May 17, 2021 14:29
-
-
Save jherr/23101f2cd7980a7839a76b6bdff45583 to your computer and use it in GitHub Desktop.
Code for No BT TS - Challenge 3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class EventProcessor { | |
handleEvent(eventName: ..., data: ...): void { | |
} | |
addHandler(handler: ...) { | |
} | |
getProcessedEvents(): ...[] { | |
} | |
} | |
interface EventMap { | |
login: { user?: string; name?: string; hasSession?: boolean }; | |
logout: { user?: string }; | |
} | |
class UserEventProcessor extends EventProcessor<EventMap> {} | |
const uep = new UserEventProcessor(); | |
uep.addHandler({ | |
filterLogin: ({ user }) => Boolean(user), | |
mapLogin: (data) => ({ | |
...data, | |
hasSession: Boolean(data.user && data.name), | |
}), | |
}); | |
uep.handleEvent("login", { | |
user: null, | |
name: "jack", | |
}); | |
uep.handleEvent("login", { | |
user: "tom", | |
name: "tomas", | |
}); | |
uep.handleEvent("logout", { | |
user: "tom", | |
}); | |
console.log(uep.getProcessedEvents()); | |
/* | |
Result: | |
[ | |
{ | |
eventName: 'login', | |
data: { user: 'tom', name: 'tomas', hasSession: true } | |
}, | |
{ eventName: 'logout', data: { user: 'tom' } } | |
] | |
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class EventProcessor { | |
handleEvent(eventName: ..., data: ...): void { | |
} | |
addFilter( | |
eventName: ..., | |
filter: (data: ...) => boolean | |
): void { | |
} | |
addMap(eventName: ..., map: (data: ...) => ...): void { | |
} | |
getProcessedEvents() { | |
} | |
} | |
interface EventMap { | |
login: { user?: string; name?: string; hasSession?: boolean }; | |
logout: { user?: string }; | |
} | |
class UserEventProcessor extends EventProcessor<EventMap> {} | |
const uep = new UserEventProcessor(); | |
uep.addFilter("login", ({ user }) => Boolean(user)); | |
uep.addMap("login", (data) => ({ | |
...data, | |
hasSession: Boolean(data.user && data.name), | |
})); | |
uep.handleEvent("login", { | |
user: null, | |
name: "jack", | |
}); | |
uep.handleEvent("login", { | |
user: "tom", | |
name: "tomas", | |
}); | |
uep.handleEvent("logout", { | |
user: "tom", | |
}); | |
console.log(uep.getProcessedEvents()); | |
/* | |
Result: | |
[ | |
{ | |
eventName: 'login', | |
data: { user: 'tom', name: 'tomas', hasSession: true } | |
}, | |
{ eventName: 'logout', data: { user: 'tom' } } | |
] | |
*/ |
Holy cow what a complicated challenge! With respect to the previous one this is like 10x the complexity. Anyway, here's my attempt with the basic, I had to peek at @deamon-cool's one at the beginning as I was quite lost! It works but I'm not too happy, as I was able to implement Name extends keyof T
only in the handleEvent
method.
interface EventName<T> {
eventName: keyof T;
}
interface MyEvent<T> extends EventName<T> {
data: T[keyof T];
}
interface MyFilters<T> {
eventName: keyof T;
filter: (e: T[keyof T]) => boolean;
}
interface MyMaps<T> extends EventName<T> {
map: (e: T[keyof T]) => T[keyof T];
}
class EventProcessor<T> {
protected events: MyEvent<T>[] = [];
protected filters: MyFilters<T>[] = [];
protected maps: MyMaps<T>[] = [];
handleEvent<Name extends keyof T>(eventName: Name, data: T[Name]): void {
this.events.push({ eventName, data });
}
addFilter(eventName: keyof T, filter: (e: T[keyof T]) => boolean): void {
this.filters.push({ eventName, filter });
}
addMap(eventName: keyof T, map: (data: T[keyof T]) => T[keyof T]): void {
this.maps.push({ eventName, map });
}
getProcessedEvents() {
return this.events.reduce((prev, curr) => {
let validEvent = true;
let tempEvent: MyEvent<T> = curr;
this.filters.every((filt) => {
if (filt.eventName === curr.eventName)
if (!filt.filter(curr.data)) {
validEvent = false;
return false;
}
});
this.maps.forEach((map) => {
map.eventName === curr.eventName &&
(tempEvent = { ...tempEvent, data: { ...map.map(tempEvent.data) } });
});
return validEvent ? [...prev, tempEvent] : prev;
}, [] as MyEvent<T>[]);
}
}
interface EventMap2 {
login: { user?: string | null; name?: string; hasSession?: boolean };
logout: { user?: string };
}
class UserEventProcessor extends EventProcessor<EventMap2> {}
const uep = new UserEventProcessor();
uep.addFilter("login", ({ user }) => Boolean(user));
uep.addMap("login", (data: EventMap2["login"]) => ({
...data,
hasSession: Boolean(data.user && data.name),
}));
uep.handleEvent("login", {
user: null,
name: "jack",
});
uep.handleEvent("login", {
user: "tom",
name: "tomas",
});
uep.handleEvent("logout", {
user: "tom",
});
console.log(uep.getProcessedEvents());
In the basic solution, I'd like to remove these two lines this.maps[<keyof T>eventName] ||= [];
(in addMap
) and this.filters[<keyof T>eventName] ||= [];
(in addFilter
).
Is it possible to create a constructor
that initializes both arrays inside the Record
in this case?
Here's the solution I came to
type EventLog<TInput> = Partial<{
[K in keyof TInput]: TInput[K];
}>;
interface EventMap {
login: { user?: string | null; name?: string; hasSession?: boolean };
logout: { user?: string };
}
class EventProcessor<TInput> {
private eventLog: EventLog<TInput>[] = [];
private eventFilters: { [key in keyof TInput]?: (data: TInput[key]) => boolean } = {};
private eventMaps: { [key in keyof TInput]?: (data: TInput[key]) => TInput[key] } = {};
handleEvent(eventName: keyof TInput, data: TInput[keyof TInput]): void {
const curEvent = {
[eventName]: { ...data },
} as EventLog<TInput>;
this.eventLog.push(curEvent as EventLog<TInput>);
}
addFilter<U extends keyof TInput>(
eventName: U,
filter: (data: TInput[U]) => boolean
): void {
this.eventFilters[eventName] = filter;
}
addMap<U extends keyof TInput, O>(
eventName: U,
map: (data: TInput[U]) => TInput[U]
): void {
this.eventMaps[eventName] = map;
}
private runFilters(){
return this.eventLog.filter((eventLog) => {
const eventName = Object.keys(eventLog)[0] as keyof TInput;
const eventData = eventLog[eventName];
const filter = this.eventFilters[eventName];
return !filter || filter(eventData);
});
}
private runMaps(filteredObjs: Partial<{ [K in keyof TInput]: TInput[K]; }>[]){
return filteredObjs.map((val,idx) => {
const eventName = Object.keys(val)[0] as keyof TInput;
const eventData = val[eventName]
const curMapFunc = this.eventMaps[eventName]
const mappedData = curMapFunc? curMapFunc(eventData) : eventData
return {
eventName,
data:{
...mappedData
}
}
})
}
getProcessedEvents() {
return this.runMaps(this.runFilters());
}
}
class UserEventProcessor extends EventProcessor<EventMap> {}
const uep = new UserEventProcessor();
uep.addFilter("login", ({ user }) => Boolean(user));
uep.addMap("login", (data) => ({
...data,
hasSession: Boolean(data.user && data.name),
}));
uep.handleEvent("login", { user: null, name: "jack" });
uep.handleEvent("login", { user: "tom", name: "tomas" });
uep.handleEvent("logout", { user: "tom" });
console.log(uep.getProcessedEvents());
/*
Result:
[
{
eventName: 'login',
data: { user: 'tom', name: 'tomas', hasSession: true }
},
{ eventName: 'logout', data: { user: 'tom' } }
]
*/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi ! I see no one share any solution up to now. So I take a risk. I have some solution, of course it is not perfect, but the log result is the same :).
basic-event-handler.ts
and advanced-event-handler.ts