Word count
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
!.meta
|
||||
|
||||
# Protected or generated
|
||||
.git
|
||||
.vscode
|
||||
|
||||
# When using npm
|
||||
node_modules/*
|
||||
|
||||
# Configuration files
|
||||
.eslintrc.cjs
|
||||
babel.config.cjs
|
||||
jest.config.cjs
|
||||
@@ -0,0 +1,38 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
overrides: [
|
||||
// Student provided files
|
||||
{
|
||||
files: ['*.ts'],
|
||||
excludedFiles: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
|
||||
extends: '@exercism/eslint-config-typescript',
|
||||
},
|
||||
// Exercism given tests
|
||||
{
|
||||
files: ['*.test.ts'],
|
||||
excludedFiles: ['custom.test.ts'],
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
extends: '@exercism/eslint-config-typescript/maintainers',
|
||||
},
|
||||
// Student provided tests
|
||||
{
|
||||
files: ['custom.test.ts'],
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
extends: '@exercism/eslint-config-typescript',
|
||||
},
|
||||
// Exercism provided files
|
||||
{
|
||||
files: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
|
||||
excludedFiles: ['custom.test.ts'],
|
||||
extends: '@exercism/eslint-config-typescript/maintainers',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"authors": [
|
||||
"masters3d"
|
||||
],
|
||||
"contributors": [
|
||||
"lukaszklis",
|
||||
"SleeplessByte",
|
||||
"angelikatyborska"
|
||||
],
|
||||
"files": {
|
||||
"solution": [
|
||||
"word-count.ts"
|
||||
],
|
||||
"test": [
|
||||
"word-count.test.ts"
|
||||
],
|
||||
"example": [
|
||||
".meta/proof.ci.ts"
|
||||
]
|
||||
},
|
||||
"blurb": "Given a phrase, count the occurrences of each word in that phrase.",
|
||||
"source": "This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour."
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"track":"typescript","exercise":"word-count","id":"cce122a248e44dd89a7b6f6048ff1353","url":"https://exercism.org/tracks/typescript/exercises/word-count","handle":"briemens","is_requester":true,"auto_approve":false}
|
||||
+19812
File diff suppressed because one or more lines are too long
Generated
+2047
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+874
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
yarnPath: .yarn/releases/yarn-3.6.0.cjs
|
||||
@@ -0,0 +1,44 @@
|
||||
# Help
|
||||
|
||||
## Running the tests
|
||||
|
||||
Execute the tests with:
|
||||
|
||||
```bash
|
||||
$ yarn test
|
||||
```
|
||||
|
||||
## Skipped tests
|
||||
|
||||
In the test suites all tests but the first have been skipped.
|
||||
|
||||
Once you get a test passing, you can enable the next one by changing `xit` to
|
||||
`it`.
|
||||
|
||||
## Submitting your solution
|
||||
|
||||
You can submit your solution using the `exercism submit word-count.ts` command.
|
||||
This command will upload your solution to the Exercism website and print the solution page's URL.
|
||||
|
||||
It's possible to submit an incomplete solution which allows you to:
|
||||
|
||||
- See how others have completed the exercise
|
||||
- Request help from a mentor
|
||||
|
||||
## Need to get help?
|
||||
|
||||
If you'd like help solving the exercise, check the following pages:
|
||||
|
||||
- The [TypeScript track's documentation](https://exercism.org/docs/tracks/typescript)
|
||||
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
|
||||
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
|
||||
|
||||
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
|
||||
|
||||
To get help if you're having trouble, you can use one of the following resources:
|
||||
|
||||
- [TypeScript QuickStart](https://www.typescriptlang.org/docs/handbook/release-notes/overview.html)
|
||||
- [ECMAScript 2015 Language Specification](https://www.ecma-international.org/wp-content/uploads/ECMA-262_6th_edition_june_2015.pdf) (pdf)
|
||||
- [Mozilla JavaScript Reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference)
|
||||
- [/r/typescript](https://www.reddit.com/r/typescript) is the TypeScript subreddit.
|
||||
- [StackOverflow](https://stackoverflow.com/questions/tagged/typescript) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Word Count
|
||||
|
||||
Welcome to Word Count on Exercism's TypeScript Track.
|
||||
If you need help running the tests or submitting your code, check out `HELP.md`.
|
||||
|
||||
## Introduction
|
||||
|
||||
You teach English as a foreign language to high school students.
|
||||
|
||||
You've decided to base your entire curriculum on TV shows.
|
||||
You need to analyze which words are used, and how often they're repeated.
|
||||
|
||||
This will let you choose the simplest shows to start with, and to gradually increase the difficulty as time passes.
|
||||
|
||||
## Instructions
|
||||
|
||||
Your task is to count how many times each word occurs in a subtitle of a drama.
|
||||
|
||||
The subtitles from these dramas use only ASCII characters.
|
||||
|
||||
The characters often speak in casual English, using contractions like _they're_ or _it's_.
|
||||
Though these contractions come from two words (e.g. _we are_), the contraction (_we're_) is considered a single word.
|
||||
|
||||
Words can be separated by any form of punctuation (e.g. ":", "!", or "?") or whitespace (e.g. "\t", "\n", or " ").
|
||||
The only punctuation that does not separate words is the apostrophe in contractions.
|
||||
|
||||
Numbers are considered words.
|
||||
If the subtitles say _It costs 100 dollars._ then _100_ will be its own word.
|
||||
|
||||
Words are case insensitive.
|
||||
For example, the word _you_ occurs three times in the following sentence:
|
||||
|
||||
> You come back, you hear me? DO YOU HEAR ME?
|
||||
|
||||
The ordering of the word counts in the results doesn't matter.
|
||||
|
||||
Here's an example that incorporates several of the elements discussed above:
|
||||
|
||||
- simple words
|
||||
- contractions
|
||||
- numbers
|
||||
- case insensitive words
|
||||
- punctuation (including apostrophes) to separate words
|
||||
- different forms of whitespace to separate words
|
||||
|
||||
`"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.`
|
||||
|
||||
The mapping for this subtitle would be:
|
||||
|
||||
```text
|
||||
123: 1
|
||||
agent: 1
|
||||
cried: 1
|
||||
fled: 1
|
||||
i: 1
|
||||
password: 2
|
||||
so: 1
|
||||
special: 1
|
||||
that's: 1
|
||||
the: 2
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
### Created by
|
||||
|
||||
- @masters3d
|
||||
|
||||
### Contributed to by
|
||||
|
||||
- @lukaszklis
|
||||
- @SleeplessByte
|
||||
- @angelikatyborska
|
||||
|
||||
### Based on
|
||||
|
||||
This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour.
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
presets: ['@exercism/babel-preset-typescript'],
|
||||
plugins: [],
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
projects: ['<rootDir>'],
|
||||
testMatch: [
|
||||
'**/__tests__/**/*.[jt]s?(x)',
|
||||
'**/test/**/*.[jt]s?(x)',
|
||||
'**/?(*.)+(spec|test).[jt]s?(x)',
|
||||
],
|
||||
testPathIgnorePatterns: [
|
||||
'/(?:production_)?node_modules/',
|
||||
'.d.ts$',
|
||||
'<rootDir>/test/fixtures',
|
||||
'<rootDir>/test/helpers',
|
||||
'__mocks__',
|
||||
],
|
||||
transform: {
|
||||
'^.+\\.[jt]sx?$': 'babel-jest',
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@exercism/typescript-word-count",
|
||||
"version": "1.0.0",
|
||||
"description": "Exercism exercises in Typescript.",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/exercism/typescript"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": "^18.16.0 || >=20.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@exercism/babel-preset-typescript": "^0.4.0",
|
||||
"@exercism/eslint-config-typescript": "^0.5.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "~18.16.16",
|
||||
"babel-jest": "^29.5.0",
|
||||
"core-js": "~3.30.2",
|
||||
"eslint": "^8.42.0",
|
||||
"jest": "^29.5.0",
|
||||
"typescript": "~5.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "yarn lint:types && jest --no-cache",
|
||||
"lint": "yarn lint:types && yarn lint:ci",
|
||||
"lint:types": "yarn tsc --noEmit -p .",
|
||||
"lint:ci": "eslint . --ext .tsx,.ts"
|
||||
},
|
||||
"packageManager": "yarn@3.6.0"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"display": "Configuration for Exercism TypeScript Exercises",
|
||||
"compilerOptions": {
|
||||
// Allows you to use the newest syntax, and have access to console.log
|
||||
// https://www.typescriptlang.org/tsconfig#lib
|
||||
"lib": ["ESNEXT", "dom"],
|
||||
// Make sure typescript is configured to output ESM
|
||||
// https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-make-my-typescript-project-output-esm
|
||||
"module": "ES2020",
|
||||
// Since this project is using babel, TypeScript may target something very
|
||||
// high, and babel will make sure it runs on your local Node version.
|
||||
// https://babeljs.io/docs/en/
|
||||
"target": "ESNext", // ESLint doesn't support this yet: "es2022",
|
||||
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
// Because we'll be using babel: ensure that Babel can safely transpile
|
||||
// files in the TypeScript project.
|
||||
//
|
||||
// https://babeljs.io/docs/en/babel-plugin-transform-typescript/#caveats
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["*.ts", "*.tsx", ".meta/*.ts", ".meta/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { count } from './word-count'
|
||||
|
||||
describe('count()', () => {
|
||||
it('counts one word', () => {
|
||||
const expectedCounts = new Map(Object.entries({ word: 1 }))
|
||||
expect(count('word')).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('counts one of each', () => {
|
||||
const expectedCounts = new Map(Object.entries({ one: 1, of: 1, each: 1 }))
|
||||
expect(count('one of each')).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('counts multiple occurrences', () => {
|
||||
const expectedCounts = new Map(
|
||||
Object.entries({ one: 1, fish: 4, two: 1, red: 1, blue: 1 })
|
||||
)
|
||||
expect(count('one fish two fish red fish blue fish')).toEqual(
|
||||
expectedCounts
|
||||
)
|
||||
})
|
||||
|
||||
it('handles cramped lists', () => {
|
||||
const expectedCounts = new Map(Object.entries({ one: 1, two: 1, three: 1 }))
|
||||
expect(count('one,two,three')).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('handles expanded lists', () => {
|
||||
const expectedCounts = new Map(Object.entries({ one: 1, two: 1, three: 1 }))
|
||||
expect(count('one,\ntwo,\nthree')).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('ignores punctuation', () => {
|
||||
const expectedCounts = new Map(
|
||||
Object.entries({
|
||||
car: 1,
|
||||
carpet: 1,
|
||||
as: 1,
|
||||
java: 1,
|
||||
javascript: 1,
|
||||
})
|
||||
)
|
||||
expect(count('car: carpet as java: javascript!!&@$%^&"')).toEqual(
|
||||
expectedCounts
|
||||
)
|
||||
})
|
||||
|
||||
it('includes numbers', () => {
|
||||
const expectedCounts = new Map(Object.entries({ testing: 2, 1: 1, 2: 1 }))
|
||||
expect(count('testing, 1, 2 testing')).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('normalizes case', () => {
|
||||
const expectedCounts = new Map(Object.entries({ go: 3, stop: 2 }))
|
||||
expect(count('go Go GO Stop stop')).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('with apostrophes', () => {
|
||||
const expectedCounts = new Map(
|
||||
Object.entries({
|
||||
first: 1,
|
||||
"don't": 2,
|
||||
laugh: 1,
|
||||
then: 1,
|
||||
cry: 1,
|
||||
"you're": 1,
|
||||
getting: 1,
|
||||
it: 1,
|
||||
})
|
||||
)
|
||||
expect(
|
||||
count("'First: don't laugh. Then: don't cry. You're getting it.'")
|
||||
).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('substrings from the beginning', () => {
|
||||
const expectedCounts = new Map(
|
||||
Object.entries({
|
||||
joe: 1,
|
||||
"can't": 1,
|
||||
tell: 1,
|
||||
between: 1,
|
||||
app: 1,
|
||||
apple: 1,
|
||||
and: 1,
|
||||
a: 1,
|
||||
})
|
||||
)
|
||||
expect(count("Joe can't tell between app, apple and a.")).toEqual(
|
||||
expectedCounts
|
||||
)
|
||||
})
|
||||
|
||||
it('multiple spaces not detected as a word', () => {
|
||||
const expectedCounts = new Map(
|
||||
Object.entries({ multiple: 1, whitespaces: 1 })
|
||||
)
|
||||
expect(count(' multiple whitespaces')).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('alternating word separators not detected as a word', () => {
|
||||
const expectedCounts = new Map(Object.entries({ one: 1, two: 1, three: 1 }))
|
||||
expect(count(",\n,one,\n ,two \n 'three'")).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('quotation for word with apostrophe', () => {
|
||||
const expectedCounts = new Map(Object.entries({ can: 1, "can't": 2 }))
|
||||
expect(count("can, can't, 'can't'")).toEqual(expectedCounts)
|
||||
})
|
||||
|
||||
it('handles properties that exist on Object’s prototype', () => {
|
||||
const expectedCounts = new Map(
|
||||
Object.entries({
|
||||
reserved: 1,
|
||||
words: 1,
|
||||
like: 1,
|
||||
constructor: 1,
|
||||
and: 1,
|
||||
tostring: 1,
|
||||
ok: 1,
|
||||
})
|
||||
)
|
||||
expect(count('reserved words like constructor and toString ok?')).toEqual(
|
||||
expectedCounts
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
export const count = (sentence: string): Map<string, number> =>
|
||||
sentence
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/^'(.*?)'$/, '$1')
|
||||
.split(/[^a-zA-Z0-9']/)
|
||||
.map(value => value.trim().replace(/^'(.*?)'$/, '$1'))
|
||||
.filter(Boolean)
|
||||
.reduce(
|
||||
(acc, word) => acc.set(word, (acc.get(word) || 0) + 1),
|
||||
new Map<string, number>()
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user