From 109b18cbd7396db768d99ab54507e0d1f2a13ba3 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:37:38 +0200 Subject: [PATCH 01/14] Exercise 00: Solution. --- exercises/exercise-00/index.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/exercises/exercise-00/index.ts b/exercises/exercise-00/index.ts index 4b815840..85e15366 100644 --- a/exercises/exercise-00/index.ts +++ b/exercises/exercise-00/index.ts @@ -26,7 +26,13 @@ Run this exercise: */ -const users: unknown[] = [ +interface User { + name: string; + age: number; + occupation: string; +} + +const users: User[] = [ { name: 'Max Mustermann', age: 25, @@ -39,7 +45,7 @@ const users: unknown[] = [ } ]; -function logPerson(user: unknown) { +function logPerson(user: User) { console.log(` - ${chalk.green(user.name)}, ${user.age}`); } From 824e81d2e54c7861217ffe4b890a022c47efcd29 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:38:25 +0200 Subject: [PATCH 02/14] Exercise 01: Solution. --- exercises/exercise-01/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exercises/exercise-01/index.ts b/exercises/exercise-01/index.ts index 6970e2a7..95d75f70 100644 --- a/exercises/exercise-01/index.ts +++ b/exercises/exercise-01/index.ts @@ -40,7 +40,9 @@ interface Admin { role: string; } -const persons: User[] /* <- Person[] */ = [ +type Person = User | Admin; + +const persons: Person[] = [ { name: 'Max Mustermann', age: 25, @@ -63,7 +65,7 @@ const persons: User[] /* <- Person[] */ = [ } ]; -function logPerson(user: User) { +function logPerson(user: Person) { console.log(` - ${chalk.green(user.name)}, ${user.age}`); } From 062ee381d2e0eda28e8eb550d7102a4943c06499 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:39:05 +0200 Subject: [PATCH 03/14] Exercise 02: Solution. --- exercises/exercise-02/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/exercise-02/index.ts b/exercises/exercise-02/index.ts index c28bfa4e..0e8ff05d 100644 --- a/exercises/exercise-02/index.ts +++ b/exercises/exercise-02/index.ts @@ -65,7 +65,7 @@ const persons: Person[] = [ function logPerson(person: Person) { let additionalInformation: string; - if (person.role) { + if ('role' in person) { additionalInformation = person.role; } else { additionalInformation = person.occupation; From 52395e99e1885edc89e25be8293d4a8d8afb07ea Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:40:09 +0200 Subject: [PATCH 04/14] Exercise 03: Solution. --- exercises/exercise-03/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/exercise-03/index.ts b/exercises/exercise-03/index.ts index 6a7c911c..3efb5a5d 100644 --- a/exercises/exercise-03/index.ts +++ b/exercises/exercise-03/index.ts @@ -48,11 +48,11 @@ const persons: Person[] = [ { type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' } ]; -function isAdmin(person: Person) { +function isAdmin(person: Person): person is Admin { return person.type === 'admin'; } -function isUser(person: Person) { +function isUser(person: Person): person is User { return person.type === 'user'; } From df0acda1a354be8c325d3e62cfff7cb8138693d8 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:43:48 +0200 Subject: [PATCH 05/14] Exercise 04: Solution. --- exercises/exercise-04/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exercises/exercise-04/index.ts b/exercises/exercise-04/index.ts index fd114b0e..51d55eab 100644 --- a/exercises/exercise-04/index.ts +++ b/exercises/exercise-04/index.ts @@ -95,9 +95,11 @@ function logPerson(person: Person) { console.log(` - ${chalk.green(person.name)}, ${person.age}, ${additionalInformation}`); } -function filterUsers(persons: Person[], criteria: User): User[] { +type FilterUsersCriteria = Partial>; + +function filterUsers(persons: Person[], criteria: FilterUsersCriteria): User[] { return persons.filter(isUser).filter((user) => { - let criteriaKeys = Object.keys(criteria) as (keyof User)[]; + let criteriaKeys = Object.keys(criteria) as (keyof FilterUsersCriteria)[]; return criteriaKeys.every((fieldName) => { return user[fieldName] === criteria[fieldName]; }); From ae84796be6123a82ebbe0aa007c4800ec9b19d80 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:49:06 +0200 Subject: [PATCH 06/14] Exercise 05: Solution. --- exercises/exercise-05/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/exercises/exercise-05/index.ts b/exercises/exercise-05/index.ts index 45e349ae..06d1287b 100644 --- a/exercises/exercise-05/index.ts +++ b/exercises/exercise-05/index.ts @@ -64,11 +64,19 @@ function logPerson(person: Person) { ); } -function filterPersons(persons: Person[], personType: string, criteria: unknown): unknown[] { +function getObjectKeys(obj: O): (keyof O)[] { + return Object.keys(obj) as (keyof O)[]; +} + +type FilterCriteria = Partial>; + +function filterPersons(persons: Person[], personType: 'admin', criteria: FilterCriteria): Admin[]; +function filterPersons(persons: Person[], personType: 'user', criteria: FilterCriteria): User[]; +function filterPersons(persons: Person[], personType: 'admin' | 'user', criteria: FilterCriteria): Person[] { return persons .filter((person) => person.type === personType) .filter((person) => { - let criteriaKeys = Object.keys(criteria) as (keyof Person)[]; + let criteriaKeys = getObjectKeys(criteria); return criteriaKeys.every((fieldName) => { return person[fieldName] === criteria[fieldName]; }); From a1f3cb8d90910dcc955f7789755f95025a411415 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:50:32 +0200 Subject: [PATCH 07/14] Exercise 06: Solution. --- exercises/exercise-06/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/exercise-06/index.ts b/exercises/exercise-06/index.ts index cf186c13..cb293f9c 100644 --- a/exercises/exercise-06/index.ts +++ b/exercises/exercise-06/index.ts @@ -88,7 +88,7 @@ const users: User[] = [ } ]; -function swap(v1, v2) { +function swap(v1: T1, v2: T2): [T2, T1] { return [v2, v1]; } From 284ed6d60d8a7fce50783e50c7163c723c4e1a33 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:52:25 +0200 Subject: [PATCH 08/14] Exercise 07: Solution. --- exercises/exercise-07/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exercises/exercise-07/index.ts b/exercises/exercise-07/index.ts index 7b77e571..553f557a 100644 --- a/exercises/exercise-07/index.ts +++ b/exercises/exercise-07/index.ts @@ -41,7 +41,9 @@ interface Admin { role: string; } -type PowerUser = unknown; +type PowerUser = Omit & { + type: 'powerUser' +}; type Person = User | Admin | PowerUser; From d07076675fd0e378fd2877e3a9531f3f21dfac2e Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 11:56:40 +0200 Subject: [PATCH 09/14] Exercise 08: Solution. --- exercises/exercise-08/index.ts | 37 ++++++++++++---------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/exercises/exercise-08/index.ts b/exercises/exercise-08/index.ts index 1638eddf..a5414b90 100644 --- a/exercises/exercise-08/index.ts +++ b/exercises/exercise-08/index.ts @@ -63,50 +63,39 @@ const users: User[] = [ { type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' } ]; -type AdminsApiResponse = ( - { - status: 'success'; - data: Admin[]; - } | - { - status: 'error'; - error: string; - } +type ApiResponse = ( + { + status: 'success'; + data: T; + } | + { + status: 'error'; + error: string; + } ); -function requestAdmins(callback: (response: AdminsApiResponse) => void) { +function requestAdmins(callback: (response: ApiResponse) => void) { callback({ status: 'success', data: admins }); } -type UsersApiResponse = ( - { - status: 'success'; - data: User[]; - } | - { - status: 'error'; - error: string; - } -); - -function requestUsers(callback: (response: UsersApiResponse) => void) { +function requestUsers(callback: (response: ApiResponse) => void) { callback({ status: 'success', data: users }); } -function requestCurrentServerTime(callback: (response: unknown) => void) { +function requestCurrentServerTime(callback: (response: ApiResponse) => void) { callback({ status: 'success', data: Date.now() }); } -function requestCoffeeMachineQueueLength(callback: (response: unknown) => void) { +function requestCoffeeMachineQueueLength(callback: (response: ApiResponse) => void) { callback({ status: 'error', error: 'Numeric value has exceeded Number.MAX_SAFE_INTEGER.' From fc6755e4720cdfa0e2187b1cf71ea9b457bcb8ec Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 12:01:32 +0200 Subject: [PATCH 10/14] Exercise 09: Solution. --- exercises/exercise-09/index.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/exercises/exercise-09/index.ts b/exercises/exercise-09/index.ts index 66607dfc..38bb95eb 100644 --- a/exercises/exercise-09/index.ts +++ b/exercises/exercise-09/index.ts @@ -71,8 +71,19 @@ type ApiResponse = ( } ); -function promisify(arg: unknown): unknown { - return null; +type PromisifyOldFunctionDefinition = (callback: (response: ApiResponse) => void) => void; +type PromisifyNewFunctionDefinition = () => Promise; + +function promisify(oldFunction: PromisifyOldFunctionDefinition): PromisifyNewFunctionDefinition { + return () => new Promise((resolve, reject) => { + oldFunction((response) => { + if (response.status === 'error') { + reject(new Error(response.error)); + return; + } + resolve(response.data); + }); + }); } const oldApi = { From 68363742a6d3a513e725a050234e2cbff927a733 Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 12:07:47 +0200 Subject: [PATCH 11/14] Exercise 10: Solution. --- exercises/exercise-10/declarations/str-utils/index.d.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/exercises/exercise-10/declarations/str-utils/index.d.ts b/exercises/exercise-10/declarations/str-utils/index.d.ts index 53567493..4deded1f 100644 --- a/exercises/exercise-10/declarations/str-utils/index.d.ts +++ b/exercises/exercise-10/declarations/str-utils/index.d.ts @@ -1,4 +1,9 @@ declare module 'str-utils' { - // export const ... - // export function ... + type StrUtil = (input: string) => string; + + export const strReverse: StrUtil; + export const strToLower: StrUtil; + export const strToUpper: StrUtil; + export const strRandomize: StrUtil; + export const strInvertCase: StrUtil; } From d200a2e52d725dd33ea8a32ee1f717b5c589c2dc Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 12:17:06 +0200 Subject: [PATCH 12/14] Exercise 11: Solution. --- exercises/exercise-11/declarations/stats/index.d.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/exercises/exercise-11/declarations/stats/index.d.ts b/exercises/exercise-11/declarations/stats/index.d.ts index da99fbfc..f8747df8 100644 --- a/exercises/exercise-11/declarations/stats/index.d.ts +++ b/exercises/exercise-11/declarations/stats/index.d.ts @@ -1,3 +1,13 @@ declare module 'stats' { - export function getMaxIndex(input: unknown, comparator: unknown): unknown; + type Comparator = (a: I, b: I) => number; + type StatIndexFunction = (input: I[], comparator: Comparator) => number; + type StatElementFunction = (input: I[], comparator: Comparator) => I; + + export const getMaxIndex: StatIndexFunction; + export const getMinIndex: StatIndexFunction; + export const getMedianIndex: StatIndexFunction; + export const getMaxElement: StatElementFunction; + export const getMinElement: StatElementFunction; + export const getMedianElement: StatElementFunction; + export const getAverageValue: (items: I[], getValue: (item: I) => O) => O; } From 1a6ade1088c22c839ed831e8e4d793585dcc82dd Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 12:21:34 +0200 Subject: [PATCH 13/14] Exercise 12: Solution. --- .../exercise-12/module-augmentations/date-wizard/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/exercises/exercise-12/module-augmentations/date-wizard/index.ts b/exercises/exercise-12/module-augmentations/date-wizard/index.ts index 8355c24a..23e942e0 100644 --- a/exercises/exercise-12/module-augmentations/date-wizard/index.ts +++ b/exercises/exercise-12/module-augmentations/date-wizard/index.ts @@ -2,5 +2,10 @@ import 'date-wizard'; declare module 'date-wizard' { - // Add your module extensions here. + interface DateDetails { + hours: number; + minutes: number; + seconds: number; + } + export function pad(num: number): string; } From ba6905b0a5b69cd4d08ee7bb0b9fb1824b7ed71e Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 30 Apr 2020 14:03:26 +0200 Subject: [PATCH 14/14] Exercise 13: Solution. --- exercises/exercise-13/database.ts | 170 +++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 4 deletions(-) diff --git a/exercises/exercise-13/database.ts b/exercises/exercise-13/database.ts index 9f938b85..47ead706 100644 --- a/exercises/exercise-13/database.ts +++ b/exercises/exercise-13/database.ts @@ -1,13 +1,175 @@ +import {readFile} from 'fs'; + +type FieldQuery = + | {$eq: FT} + | {$gt: FT} + | {$lt: FT} + | {$in: FT[]}; + +type Query = {[K in keyof T]?: FieldQuery} & { + $text?: string; + $and?: Query[]; + $or?: Query[]; +}; + +interface Documents { + [key: string]: boolean; +} + +function intersectSearchResults(documents: Documents[]) { + const result: Documents = {}; + if (documents.length === 0) { + return result; + } + for (let key of Object.keys(documents[0])) { + let keep = true; + for (let i = 1; i < documents.length; i++) { + if (!documents[i][key]) { + keep = false; + break; + } + } + if (keep) { + result[key] = true; + } + } + return result; +} + +function mergeSearchResults(documents: Documents[]) { + const result: Documents = {}; + for (const document of documents) { + for (const key of Object.keys(document)) { + result[key] = true; + } + } + return result; +} + export class Database { protected filename: string; - protected fullTextSearchFieldNames: unknown[]; + protected fullTextSearchFieldNames: (keyof T)[]; + protected getDocumentsPromise: Promise | null = null; + protected getFullTextSearchIndexPromise: Promise | null = null; - constructor(filename: string, fullTextSearchFieldNames) { + constructor(filename: string, fullTextSearchFieldNames: (keyof T)[]) { this.filename = filename; this.fullTextSearchFieldNames = fullTextSearchFieldNames; } - async find(query): Promise { - return []; + async find(query: Query): Promise { + const documents = await this.getDocuments(); + return Object.keys(await this.findMatchingDocuments(query)) + .map(Number) + .map((index) => documents[index]); + } + + protected getDocuments() { + return this.getDocumentsPromise || (this.getDocumentsPromise = new Promise((resolve, reject) => { + readFile(this.filename, 'utf8', (error, data) => { + if (error) { + reject(error); + return; + } + resolve( + data + .trim() + .split('\n') + .filter((line) => line[0] === 'E') + .map((line) => JSON.parse(line.substr(1))) + ); + }); + })); + } + + protected getFullTextSearchIndex() { + return this.getFullTextSearchIndexPromise || ( + this.getFullTextSearchIndexPromise = this.getDocuments().then((documents) => { + const fullTextSearchIndex = new FullTextSearchIndex(); + documents.forEach((document, id) => { + fullTextSearchIndex.addDocument( + id, + this.fullTextSearchFieldNames.map((key) => String(document[key])) + ); + }); + return fullTextSearchIndex; + }) + ); + } + + protected async getMatchingDocumentIds(comparator: (document: T) => boolean) { + const result: Documents = {}; + const documents = await this.getDocuments(); + for (let i = 0; i < documents.length; i++) { + if (comparator(documents[i])) { + result[i] = true; + } + } + return result; + } + + protected async findMatchingDocuments(query: Query): Promise { + const result: Documents[] = []; + for (const key of Object.keys(query) as (keyof Query)[]) { + if (key === '$text') { + result.push((await this.getFullTextSearchIndex()).search(query.$text!)) + } else if (key === '$and') { + result.push( + intersectSearchResults(await Promise.all(query.$and!.map(this.findMatchingDocuments, this))) + ); + } else if (key === '$or') { + result.push(mergeSearchResults(await Promise.all(query.$or!.map(this.findMatchingDocuments, this)))); + } else { + const fieldQuery = query[key] as FieldQuery; + if ('$eq' in fieldQuery) { + result.push(await this.getMatchingDocumentIds((document) => document[key] === fieldQuery.$eq)); + } else if ('$gt' in fieldQuery) { + result.push( + await this.getMatchingDocumentIds((document) => Number(document[key]) > Number(fieldQuery.$gt)) + ); + } else if ('$lt' in fieldQuery) { + result.push( + await this.getMatchingDocumentIds((document) => Number(document[key]) < Number(fieldQuery.$lt)) + ); + } else if ('$in' in fieldQuery) { + const index: {[key: string]: boolean} = {}; + for (const val of fieldQuery.$in) { + index[String(val)] = true; + } + result.push( + await this.getMatchingDocumentIds((document) => index.hasOwnProperty(String(document[key]))) + ); + } else { + throw new Error('Incorrect query.'); + } + } + } + return intersectSearchResults(result); + } +} + +class FullTextSearchIndex { + protected wordsToDocuments: {[word: string]: Documents} = {}; + + protected breakTextIntoWords(text: string) { + return text.toLowerCase().replace(/\W+/g, ' ').trim().split(' '); + } + + addDocument(document: number, texts: string[]) { + for (const text of texts) { + const words = this.breakTextIntoWords(text); + for (let word of words) { + this.wordsToDocuments[word] = this.wordsToDocuments[word] || {}; + this.wordsToDocuments[word][document] = true; + } + } + } + + search(query: string): Documents { + return intersectSearchResults( + this.breakTextIntoWords(query) + .map((word) => this.wordsToDocuments[word]) + .filter(Boolean) + ); } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy