Grade school

This commit is contained in:
2022-11-08 10:35:45 +01:00
parent 1e9da939c0
commit f2d367cd67
13 changed files with 16369 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,41 @@
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',
rules: {
"@typescript-eslint/semi": 0,
},
},
],
}

View File

@@ -0,0 +1,12 @@
{
"blurb": "Given students' names along with the grade that they are in, create a roster for the school",
"authors": ["masters3d"],
"contributors": ["lukaszklis", "pedrorolo", "SleeplessByte"],
"files": {
"solution": ["grade-school.ts"],
"test": ["grade-school.test.ts"],
"example": [".meta/proof.ci.ts"]
},
"source": "A pairing session with Phil Battos at gSchool",
"source_url": "http://gschool.it"
}

View File

@@ -0,0 +1 @@
{"track":"typescript","exercise":"grade-school","id":"35dfa4bfea7c4bda8252d9d6099423b9","url":"https://exercism.org/tracks/typescript/exercises/grade-school","handle":"briemens","is_requester":true,"auto_approve":false}

View File

@@ -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 grade-school.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.

View File

@@ -0,0 +1,59 @@
# Grade School
Welcome to Grade School on Exercism's TypeScript Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Given students' names along with the grade that they are in, create a roster
for the school.
In the end, you should be able to:
- Add a student's name to the roster for a grade
- "Add Jim to grade 2."
- "OK."
- Get a list of all students enrolled in a grade
- "Which students are in grade 2?"
- "We've only got Jim just now."
- Get a sorted list of all students in all grades. Grades should sort
as 1, 2, 3, etc., and students within a grade should be sorted
alphabetically by name.
- "Who all is enrolled in school right now?"
- "Let me think. We have
Anna, Barb, and Charlie in grade 1,
Alex, Peter, and Zoe in grade 2
and Jim in grade 5.
So the answer is: Anna, Barb, Charlie, Alex, Peter, Zoe and Jim"
Note that all our students only have one name. (It's a small town, what
do you want?)
## For bonus points
Did you get the tests passing and the code clean? If you want to, these
are some additional things you could try:
- If you're working in a language with mutable data structures and your
implementation allows outside code to mutate the school's internal DB
directly, see if you can prevent this. Feel free to introduce additional
tests.
Then please share your thoughts in a comment on the submission. Did this
experiment make the code better? Worse? Did you learn anything from it?
## Source
### Created by
- @masters3d
### Contributed to by
- @lukaszklis
- @pedrorolo
- @SleeplessByte
### Based on
A pairing session with Phil Battos at gSchool - http://gschool.it

View File

@@ -0,0 +1,4 @@
module.exports = {
presets: ['@exercism/babel-preset-typescript'],
plugins: [],
}

View File

@@ -0,0 +1,86 @@
import { GradeSchool } from "./grade-school";
describe("School", () => {
let school: GradeSchool;
beforeEach(() => {
school = new GradeSchool();
});
it("a new school has an empty roster", () => {
expect(school.roster()).toEqual({});
});
it("adding a student adds them to the roster for the given grade", () => {
school.add("Aimee", 2);
const expectedDb = { 2: ["Aimee"] };
expect(school.roster()).toEqual(expectedDb);
});
it("adding more students to the same grade adds them to the roster", () => {
school.add("Blair", 2);
school.add("James", 2);
school.add("Paul", 2);
const expectedDb = { 2: ["Blair", "James", "Paul"] };
expect(school.roster()).toEqual(expectedDb);
});
it("adding students to different grades adds them to the roster", () => {
school.add("Chelsea", 3);
school.add("Logan", 7);
const expectedDb = { 3: ["Chelsea"], 7: ["Logan"] };
expect(school.roster()).toEqual(expectedDb);
});
it("grade returns the students in that grade in alphabetical order", () => {
school.add("Franklin", 5);
school.add("Bradley", 5);
school.add("Jeff", 1);
const expectedStudents = ["Bradley", "Franklin"];
expect(school.grade(5)).toEqual(expectedStudents);
});
it("grade returns an empty array if there are no students in that grade", () => {
expect(school.grade(1)).toEqual([]);
});
it("the students names in each grade in the roster are sorted", () => {
school.add("Jennifer", 4);
school.add("Kareem", 6);
school.add("Christopher", 4);
school.add("Kyle", 3);
const expectedSortedStudents = {
3: ["Kyle"],
4: ["Christopher", "Jennifer"],
6: ["Kareem"],
};
expect(school.roster()).toEqual(expectedSortedStudents);
});
it("roster cannot be modified outside of module", () => {
school.add("Aimee", 2);
const roster = school.roster();
roster[2].push("Oops.");
const expectedDb = { 2: ["Aimee"] };
expect(school.roster()).toEqual(expectedDb);
});
it("roster cannot be modified outside of module using grade()", () => {
school.add("Aimee", 2);
school.grade(2).push("Oops.");
const expectedDb = { 2: ["Aimee"] };
expect(school.roster()).toEqual(expectedDb);
});
it("a student can't be in two different grades", () => {
school.add("Aimee", 2);
school.add("Aimee", 1);
expect(school.grade(2)).toEqual([]);
});
});

View File

@@ -0,0 +1,35 @@
export class GradeSchool {
private _roster: Record<number, string[]> = {};
roster(): Record<number, string[]> {
return JSON.parse(JSON.stringify(this._roster));
}
get grades(): number[] {
return Object.keys(this._roster) as unknown as number[];
}
add(name: string, grade: number): void {
const addUniqueValue = <T extends any>(
array: T[] | undefined,
value: T
): T[] => [...new Set<T>([...(array || []), value])];
const allGrades = addUniqueValue(this.grades, grade);
this._roster = allGrades.reduce(
(newRoster, rosterGrade) => ({
...newRoster,
[rosterGrade]:
rosterGrade === grade
? addUniqueValue(newRoster[rosterGrade], name).sort()
: newRoster[rosterGrade].filter((n) => n !== name),
}),
this._roster
);
}
grade(grade: number): string[] {
return this.roster()[grade] || [];
}
}

View File

@@ -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',
},
}

15996
typescript/grade-school/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
{
"name": "@exercism/typescript-grade-school",
"version": "1.0.0",
"description": "Exercism exercises in Typescript.",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/exercism/typescript"
},
"type": "module",
"engines": {
"node": "^14.13.1 || >=16.0.0"
},
"devDependencies": {
"@exercism/babel-preset-typescript": "^0.1.0",
"@exercism/eslint-config-typescript": "^0.4.1",
"@types/jest": "^27.4.0",
"@types/node": "^16.11.24",
"babel-jest": "^27.5.1",
"core-js": "^3.21.0",
"eslint": "^8.9.0",
"jest": "^27.5.1",
"typescript": "^4.5.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"
}
}

View File

@@ -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"]
}