diff --git a/src/index.test.ts b/src/index.test.ts index 1edcef5..b606ba5 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -2,26 +2,24 @@ import UserAPI from "./index" import { mockUsers, + mockUsers2, userObject, updatingUserObject, - updatingUserObjectError, - updatingUserObjectErrorWithId, } from "./mockdata" describe("Tests will go here!", () => { beforeEach(() => {}) it("should make an instance of the UserAPI class and add a user, new user object should have an id.", () => { - const users = new UserAPI(mockUsers()) + const users = new UserAPI(mockUsers) const newUser = users.addUser(userObject) - - expect(users.list).toHaveLength(5) + expect(users.getUsers()).toHaveLength(5) expect(newUser).toEqual({ id: expect.any(String), ...userObject }) }) it("should show the correct error message, will fail if no errors are thrown", () => { - const users = new UserAPI(mockUsers()) + const users = new UserAPI(mockUsers) try { expect(() => users.addUser({})).toThrow() @@ -33,7 +31,7 @@ describe("Tests will go here!", () => { }) it("should show the correct error message, will fail if no errors are thrown", () => { - const users = new UserAPI(mockUsers()) + const users = new UserAPI(mockUsers) try { expect(() => users.addUser({})).toThrow() @@ -43,7 +41,7 @@ describe("Tests will go here!", () => { }) it("should get the correct user that matches their id", () => { - const users = new UserAPI(mockUsers()) + const users = new UserAPI(mockUsers) const targetUser1 = users.getUserById("1") const targetUser2 = users.getUserById("3") @@ -63,7 +61,7 @@ describe("Tests will go here!", () => { }) it("should get a list of all the users, or an empty array other wise", () => { - const users = new UserAPI(mockUsers()) + const users = new UserAPI(mockUsers) const emptyusers = new UserAPI() expect(users.getUsers()).toHaveLength(4) @@ -72,8 +70,11 @@ describe("Tests will go here!", () => { }) it("should update the users but return an error if user is not found or if the object doesn't have an id ", () => { - const users = new UserAPI(mockUsers()) - const updatedUser = users.updateUserById(updatingUserObject) + const users = new UserAPI(mockUsers) + const validId = "0" + const noID = "" + const notValidId = "not a valid Id" + const updatedUser = users.updateUserById(validId, updatingUserObject) expect(updatedUser).toEqual({ id: "0", @@ -83,21 +84,21 @@ describe("Tests will go here!", () => { }) try { - users.updateUserById(updatingUserObjectError) + users.updateUserById(noID, updatingUserObject) } catch (err) { expect(err.message).toEqual("We can not update a user without an id") } try { - users.updateUserById(updatingUserObjectErrorWithId) + users.updateUserById(notValidId, updatingUserObject) } catch (err) { expect(err.message).toEqual("There are no users found with that id.") } }) it("should be able to delete a user, and it should return the delete user.", () => { - const users = new UserAPI(mockUsers()) - expect(users.list).toHaveLength(4) + const users = new UserAPI(mockUsers) + expect(users.getUsers()).toHaveLength(4) const deletedUser = users.deleteUserById("3") @@ -108,7 +109,7 @@ describe("Tests will go here!", () => { name: "tim", }) - expect(users.list).toHaveLength(3) + expect(users.getUsers()).toHaveLength(3) try { users.deleteUserById("3") @@ -118,7 +119,7 @@ describe("Tests will go here!", () => { }) it("should return empty array or an array of users with any part of the their name matching the input argument.", () => { - const users = new UserAPI(mockUsers()) + const users = new UserAPI(mockUsers) const emptyArray = users.searchUserByName("not a real search") expect(emptyArray).toEqual([]) @@ -135,4 +136,60 @@ describe("Tests will go here!", () => { expect(thirdSearch).toHaveLength(2) }) + it("should return a number with the average age of all the users", () => { + const users = new UserAPI(mockUsers) + + const averageAge = users.getAverageAge() + + expect(averageAge).toEqual(30.5) + + const users2 = new UserAPI(mockUsers2) + + const averageAge2 = users2.getAverageAge() + + expect(averageAge2).toEqual(31.666666666666668) + }) + + it("should get a array of all the favorite colors of all the users", () => { + const users = new UserAPI(mockUsers) + users.addUser(userObject) + users.addUser(userObject) + users.addUser(userObject) + + const arrayOfFavoriteColors = users.getAllFavoriteColors() + + expect(arrayOfFavoriteColors).toEqual( + new Set(["green", "black", "blue", "red"]) + ) + }) + + it("should get the favorite color count of each Color.", () => { + const users = new UserAPI(mockUsers) + users.addUser(userObject) + users.addUser(userObject) + users.addUser(userObject) + users.addUser({ name: "ted" }) + users.addUser({ name: "ted" }) + users.addUser({ name: "ted" }) + + const objectOfFavoriteColors = users.getFavoriteColorCount() + expect(objectOfFavoriteColors).toEqual({ + green: 4, + blue: 1, + black: 1, + red: 1, + }) + }) + it("should return all user stats in an object", () => { + const users = new UserAPI(mockUsers) + users.addUser(userObject) + const userMeta = users.getUserMeta() + + expect(userMeta).toEqual({ + colorCount: { green: 2, black: 1, blue: 1, red: 1 }, + allColors: new Set(["green", "black", "blue", "red"]), + averageAge: 30.6, + totalUsers: 5, + }) + }) }) diff --git a/src/index.ts b/src/index.ts index 5b8b54c..6f4ecd4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,62 +1,145 @@ -//@ts-nocheck +import { Person, UserFormMeta, Users } from "./interfaces" export default class UserAPI { - constructor(users) { - this.list = users || [] + list: Users + + constructor(users: Users) { + this.list = users || {} } - //random id generator - randomId = () => { + + randomId = (): string => { return Math.random().toString(24).slice(2) } - addUser(user) { + addUser(user: UserFormMeta): Person { if (!user.name) { throw new Error("you need to at least have a name to add a user") } - const newUser = { id: this.randomId(), ...user } - this.list.push(newUser) + + const id = this.randomId() + const newUser = { id, ...user } + this.list = { ...this.list, [id]: newUser } return newUser } - getUserById(id) { - const targetUser = this.list.find(user => user.id === id) + getUserById(id: string): Person { + const targetUser = this.list[id] + if (!targetUser) { throw new Error("There are no users found with that id.") } + return targetUser } - updateUserById(updatedUser) { - if (!updatedUser.id) { + updateUserById(id: string, updatedUser: Partial): Person { + if (!id) { throw new Error("We can not update a user without an id") } - const updatedList = this.list.map(user => { - if (user.id === updatedUser.id) { - return { ...user, ...updatedUser } - } - return user - }) - this.list = updatedList - return this.getUserById(updatedUser.id) + const foundUser = this.getUserById(id) + + if (!foundUser) { + throw new Error("There are no users found with that id.") + } + + const newUpdatedUser = { ...foundUser, ...updatedUser } + const updatedUserList = { ...this.list, [id]: newUpdatedUser } + + this.list = updatedUserList + return newUpdatedUser } - getUsers() { - return this.list + getUsers(): ReadonlyArray | [] { + const usersArray: ReadonlyArray = Object.values(this.list).map( + (value: Person) => { + return value + } + ) + + return usersArray } - deleteUserById(id) { - const deletedUser = this.getUserById(id) - const newUsersList = this.list.filter(user => user.id !== id) - this.list = newUsersList + deleteUserById(id: string): Person { + if (!this.getUserById(id)) { + throw new Error("There are no users found with that id.") + } + + const { [id]: deletedUser, ...rest } = this.list + this.list = rest + return deletedUser } - searchUserByName(name) { - const filteredUserArray = this.list.filter(user => { - if (user.name.toLowerCase().includes(name.toLowerCase())) { - return user - } - }) + searchUserByName(name: string): ReadonlyArray | [] { + const filteredUserArray: ReadonlyArray = Object.values( + this.list + ).filter((value: Person) => + value.name.toLowerCase().includes(name.toLowerCase()) + ) + return filteredUserArray } + + getAverageAge(): number { + const ageOnlyArray = Object.values(this.list).reduce( + (acc, person: Person) => { + if (person.age) { + return [...acc, person.age] + } + return acc + }, + [] as number[] + ) + + const averageAge: number = + ageOnlyArray.reduce((prev: number, current: number) => prev + current) / + ageOnlyArray.length + + return averageAge + } + + getAllFavoriteColors() { + let onlyColors = new Set() + + Object.values(this.list) + .filter((users: Person) => users.favoriteColor) + .map((users: Person) => { + onlyColors.add(users.favoriteColor) + }) + + return onlyColors + } + + getFavoriteColorCount(): Record { + const colorCount = Object.values(this.list).reduce( + (acc, person: Person) => { + if (acc[person?.favoriteColor as string]) { + const newColorCount = acc[person.favoriteColor as string] + 1 + + return { + ...acc, + [person.favoriteColor as string]: newColorCount, + } + } else if (person.favoriteColor) { + return { ...acc, [person.favoriteColor]: 1 } + } + + return acc + }, + {} as Record + ) + + return colorCount + } + + getUserMeta(): Record { + const userMeta = { + colorCount: this.getFavoriteColorCount(), + allColors: this.getAllFavoriteColors(), + averageAge: this.getAverageAge(), + totalUsers: Object.keys(this.list).length, + } + + return userMeta + } } diff --git a/src/interfaces.ts b/src/interfaces.ts new file mode 100644 index 0000000..7f276f0 --- /dev/null +++ b/src/interfaces.ts @@ -0,0 +1,14 @@ +export interface Person { + readonly id: string + readonly name: string + readonly age?: number + readonly favoriteColor?: string +} + +export type Users = Record + +export interface UserFormMeta { + readonly name: string + readonly age?: number + readonly favoriteColor?: string +} diff --git a/src/mockdata.ts b/src/mockdata.ts index 978b75a..586f67a 100644 --- a/src/mockdata.ts +++ b/src/mockdata.ts @@ -1,39 +1,28 @@ -// @ts-nocheck -const mockUsers = () => { - return [ - { id: "0", name: "minoka", age: 31, favoriteColor: "green" }, - { id: "1", name: "ted", age: 23, favoriteColor: "black" }, - { id: "2", name: "ron", age: 41, favoriteColor: "blue" }, - { id: "3", name: "tim", age: 27, favoriteColor: "red" }, - ] -} - -const userObject = { name: "minoka", age: 31, favoriteColor: "green" } +import { Users, UserFormMeta } from "./interfaces" -const updatingUserObject = { - id: "0", - name: "minoka", - age: 100, - favoriteColor: "red", +const mockUsers: Users = { + "0": { id: "0", name: "minoka", age: 31, favoriteColor: "green" }, + "1": { id: "1", name: "ted", age: 23, favoriteColor: "black" }, + "2": { id: "2", name: "ron", age: 41, favoriteColor: "blue" }, + "3": { id: "3", name: "tim", age: 27, favoriteColor: "red" }, +} +const mockUsers2: Users = { + "0": { id: "0", name: "minoka", age: 31, favoriteColor: "green" }, + "1": { id: "1", name: "ted", age: 23, favoriteColor: "black" }, + "2": { id: "2", name: "ron", age: 41, favoriteColor: "blue" }, + "3": { id: "3", name: "tim", favoriteColor: "red" }, } -const updatingUserObjectError = { +const userObject: UserFormMeta = { name: "minoka", - age: 100, - favoriteColor: "red", + age: 31, + favoriteColor: "green", } -const updatingUserObjectErrorWithId = { - id: "124324614614331461346", +const updatingUserObject: UserFormMeta = { name: "minoka", age: 100, favoriteColor: "red", } -export { - mockUsers, - userObject, - updatingUserObject, - updatingUserObjectError, - updatingUserObjectErrorWithId, -} +export { mockUsers, mockUsers2, userObject, updatingUserObject }