diff --git a/1.map-filter-find.js b/1.map-filter-find.js index 8b374c2..28f4af2 100644 --- a/1.map-filter-find.js +++ b/1.map-filter-find.js @@ -3,10 +3,30 @@ const getPokeNames = (pokemons) => { } const getPokemonById = (pokemons, id) => { + return pokemons.find(pokemon => pokemon.id === id) +} + +const getRarePokemons = (pokemons) => { + return pokemons.filter(pokemon => pokemon.spawn_chance < 0.10) +} + +const getMidSizedPokemon = (pokemons) => { + return pokemons.find(pokemon => pokemon.weight === "38.0 kg") +} + +const getAdultPokemons = (pokemons) => { + return pokemons.filter(pokemon => pokemon.egg === "Not in Eggs") +} +const getPokemonImages = (pokemons) => { + return pokemons.map(pokemon => pokemon.img) } module.exports = { getPokeNames, getPokemonById, + getRarePokemons, + getMidSizedPokemon, + getAdultPokemons, + getPokemonImages } \ No newline at end of file diff --git a/1.map-filter-find.test.js b/1.map-filter-find.test.js index 49b0b35..346db7c 100644 --- a/1.map-filter-find.test.js +++ b/1.map-filter-find.test.js @@ -11,11 +11,6 @@ test('getPokeNames: Transforms an array of pokemons into an array of pokemon nam test('getPokemonById: Gets a pokemon object by their id', () => { const id = 25 const pokemon = getPokemonById(pokemons, id) - expect(pokemon).toEqual(expect.objectContaining({ - id: expect.any(Number), - name: expect.any(String), - height: expect.any(String), - })) expect(pokemon.id).toBe(25) expect(pokemon.name).toBe('Pikachu') expect(pokemon.height).toBe('0.41 m') diff --git a/2.reduce.js b/2.reduce.js index da39414..9d84640 100644 --- a/2.reduce.js +++ b/2.reduce.js @@ -5,10 +5,72 @@ const calculateTotalPokemonWeight = (pokemons) => { } const calculateAverageSpawnChance = (pokemons) => { + const totalPokemonCount = pokemons.length + const averageSpawnChance = pokemons.reduce((totalSpawnChance, currentPokemon) => { + return totalSpawnChance + currentPokemon.spawn_chance + }, 0) / totalPokemonCount + return averageSpawnChance +} + +const calculateTotalEggDistance = (pokemons) => { + return pokemons.reduce((totalDistance, currentPokemon) => { + if (currentPokemon.egg === "Not in Eggs") { + return totalDistance + } + + return totalDistance + parseInt(currentPokemon.egg) + }, 0) +} + +// Alternate solution using ternary operator +// const calculateTotalEggDistance = (pokemons) => { +// return pokemons.reduce((totalDistance, currentPokemon) => { +// const distance = currentPokemon.egg === "Not in Eggs" ? 0 : parseInt(currentPokemon.egg) +// return totalDistance + distance +// }, 0) +// } + +const getHeaviestPokemon = (pokemons) => { + return pokemons.reduce((heaviestSoFar, currentPokemon) => { + if (parseInt(heaviestSoFar.weight) > parseInt(currentPokemon.weight)) { + return heaviestSoFar + } + + return currentPokemon + }) +} + +const categorizePokemonsByRarity = (pokemons) => { + const RARE_SPAWN_CHANCE = 0.1 + const LEGENDARY_SPAWN_CHANCE = 0.01 + + const initialAccumulator = { + common: [], + rare: [], + legendary: [] + } + + return pokemons.reduce((categories, currentPokemon) => { + if (currentPokemon.spawn_chance > RARE_SPAWN_CHANCE) { + categories.common.push(currentPokemon) + return categories + } + + if (currentPokemon.spawn_chance > LEGENDARY_SPAWN_CHANCE) { + categories.rare.push(currentPokemon) + return categories + } + + categories.legendary.push(currentPokemon) + return categories + }, initialAccumulator) } module.exports = { calculateTotalPokemonWeight, calculateAverageSpawnChance, + calculateTotalEggDistance, + getHeaviestPokemon, + categorizePokemonsByRarity } \ No newline at end of file diff --git a/3.data-mining.js b/3.data-mining.js index 4716592..02bb7cf 100644 --- a/3.data-mining.js +++ b/3.data-mining.js @@ -1,21 +1,77 @@ -const getGymLeader = (gym, trainers) => { +const { getPokemonById } = require('./1.map-filter-find') +const { categorizePokemonsByRarity } = require('./2.reduce') +const getGymLeader = (gym, trainers) => { + return trainers.find(trainer => trainer.id === gym.trainerId) } - const getTrainerPokemons = (trainer, pokemons) => { - + return pokemons.filter(pokemon => trainer.pokemonIds.includes(pokemon.id)) } const getTrainersPokemons = (trainers, pokemons) => { - + return trainers.map(trainer => { + const trainerPokemons = getTrainerPokemons(trainer, pokemons) + return { + id: trainer.id, + name: trainer.name, + pokemons: trainerPokemons + } + }) } + +// Multi step solution: +// +// const getBigGyms = (gyms, trainers) => { +// return gyms +// .map(gym => { +// return { +// id: gym.id, +// city: gym.city, +// gymLeader: getGymLeader(gym, trainers) +// } +// }) +// .filter(gym => { +// return gym.gymLeader.pokemonIds.length > 3 +// }) +// .map(gym => gym.city) +// } + const getBigGyms = (gyms, trainers) => { + return gyms.reduce((bigGymNames, currentGym) => { + const gymLeader = getGymLeader(currentGym, trainers) + + if (gymLeader.pokemonIds.length > 3) { + bigGymNames.push(currentGym.city) + return bigGymNames + } + return bigGymNames + }, []) } const getRarestGym = (gyms, trainers, pokemons) => { + return gyms + .map(gym => { + const gymLeader = getGymLeader(gym, trainers) + const trainerPokemons = getTrainerPokemons(gymLeader, pokemons) + + return { + gym, + pokemonsByRarity: categorizePokemonsByRarity(trainerPokemons) + } + }) + .reduce((rarestGymSoFar, currentGym) => { + const legendaryCountRarestSoFar = rarestGymSoFar.pokemonsByRarity.legendary.length + const legendaryCountCurrentGym = currentGym.pokemonsByRarity.legendary.length + + if (legendaryCountCurrentGym > legendaryCountRarestSoFar) { + return currentGym + } + return rarestGymSoFar + }) + .gym } module.exports = { diff --git a/3.data-mining.test.js b/3.data-mining.test.js index edfc009..2a55218 100644 --- a/3.data-mining.test.js +++ b/3.data-mining.test.js @@ -13,11 +13,6 @@ const { test('getGymleader: gets the gymleader belonging to a gym', () => { const fuchsiaCity = gyms.find(gym => gym.city === 'Fuchsia City') const gymLeader = getGymLeader(fuchsiaCity, trainers) - expect(gymLeader).toEqual(expect.objectContaining({ - id: expect.any(Number), - name: expect.any(String), - pokemonIds: expect.any(Array) - })) expect(gymLeader.name).toBe('Koga') }) @@ -35,8 +30,6 @@ test(`getTrainersPokemons: replaces trainerIds with the pokemons belonging to a trainer for an array of trainers`, () => { const trainersWithPokemons = getTrainersPokemons(trainers, pokemons) - expect(trainersWithPokemons).toEqual(expect.any(Array)) - trainersWithPokemons.forEach(trainer => { expect(trainer).toEqual(expect.objectContaining({ id: expect.any(Number), diff --git a/4.student-exercises.js b/4.student-exercises.js new file mode 100644 index 0000000..465950c --- /dev/null +++ b/4.student-exercises.js @@ -0,0 +1,72 @@ +const getTrainersAndGymsAndPokemons = (gyms, trainers, pokemons) => { + // Combine trainers and pokemons and gyms + return ( + trainers + // Map trainers to include their pokemons + .map(trainer => { + // Include the trainer's pokemons + const trainerPokemons = pokemons.filter(pokemon => + trainer.pokemonIds.includes(pokemon.id) + ); + // Include the trainer's gym + const trainerGym = gyms.find(gym => gym.trainerId === trainer.id); + return { + ...trainer, + pokemons: trainerPokemons, + gym: trainerGym + } + }) + ); +}; + +const getPsychicTrainersAndGyms = (gyms, trainers, pokemons) => { + return ( + // Step 1: Combine the trainers, gyms and pokemons + getTrainersAndGymsAndPokemons(gyms, trainers, pokemons) + // Step 2: Filter out only trainers that have a psychic pokemon + .filter(trainer => + trainer.pokemons.some(pokemon => pokemon.type.includes("Psychic")) + ) + ); +}; + +const getGymsWithPokemons = (gyms, trainers, pokemons, ...pokemonsToFind) => { + // Retrieve the pokemon objects + return ( + pokemons + // Filter all the pokemons based on the ones we requested to find + .filter(pokemon => pokemonsToFind.includes(pokemon.name)) + .map(pokemon => { + // Construct the object to be returned + return { + id: pokemon.id, + name: pokemon.name, + // Retrieve the trainers that own this pokemon + trainers: trainers.filter(trainer => + trainer.pokemonIds.includes(pokemon.id) + ), + // Retrieve the gyms that are owned by these trainers + gyms: gyms.filter(gym => { + return trainers + .filter(trainer => trainer.pokemonIds.includes(pokemon.id)) + .find(trainer => trainer.id === gym.trainerId); + }) + }; + }) + ); + + return getTrainersAndGymsAndPokemons(gyms, trainers, pokemons) + .filter(trainer => + trainer.pokemons.some(pokemon => pokemonsToFind.includes(pokemon.name)) + ) + .map(trainer => { + return {}; + trainer.gym; + }); +}; + +module.exports = { + getPsychicTrainersAndGyms, + getTrainersAndGymsAndPokemons, + getGymsWithPokemons +}; diff --git a/4.student-exercises.test.js b/4.student-exercises.test.js new file mode 100644 index 0000000..d39deca --- /dev/null +++ b/4.student-exercises.test.js @@ -0,0 +1,94 @@ +const pokemons = require("./pokeData"); +const trainers = require("./trainerData"); +const gyms = require("./gymData"); + +const { + getPsychicTrainersAndGyms, + getTrainersAndGymsAndPokemons, + getGymsWithPokemons +} = require("./4.student-exercises"); + +/** + * This function should return an array of trainer objects + * that also include an array of pokemons and an object of gym + */ +test("getTrainersAndGymsAndPokemons", () => { + // Get an array of trainers that contain their gyms and pokemons + const trainersAndGymsAndPokemons = getTrainersAndGymsAndPokemons( + gyms, + trainers, + pokemons + ); + expect(trainersAndGymsAndPokemons.length).toBe(9); + // Expect each trainer to have a gym object + expect(trainersAndGymsAndPokemons.map(trainer => trainer.gym)).toEqual( + expect.any(Object) + ); + // Expect each trainer to have a pokemon array + expect(trainersAndGymsAndPokemons.map(trainer => trainer.pokemons)).toEqual( + expect.any(Array) + ); +}); + +/** + * This function should return an array of trainers that have + * at least one psychic pokemon. In each trainer object an array + * of their own pokemons and an object representing their gym should exist + */ +test(`getPsychicTrainersAndGyms: expect a list of trainers with psychic pokemons. + also include (all) their pokemons and their gym`, () => { + const result = getPsychicTrainersAndGyms(gyms, trainers, pokemons); + expect(result.length).toEqual(2); + expect(result[0].name).toBe("Sabrina"); + expect(result[1].name).toBe("Misty"); + // Results should also contain gym + expect(result[0].gym).toEqual(expect.any(Object)); + expect(result[1].gym).toEqual(expect.any(Object)); + expect(result[0].gym.city).toBe("Saffron City"); + expect(result[1].gym.city).toBe("Cerulean City"); + // Results should also contain array of pokemons + expect(result[0].pokemons).toEqual(expect.any(Array)); + expect(result[1].pokemons).toEqual(expect.any(Array)); + expect(result[0].pokemons.length).toBe(4); + expect(result[1].pokemons.length).toBe(2); +}); + +/** + * Returns an array of objects with the id and name of pokemon and an array + * of gym objects where these pokemons can be found. + */ +test("getGymsWithPokemons: expect a list of gyms where certain pokemons can be found", () => { + const result = getGymsWithPokemons( + gyms, + trainers, + pokemons, + "Gloom", + "Starmie" + ); + // Expect the result to be an array + result.forEach(pokemon => { + // It is an array of pokemons + expect(pokemon).toEqual( + expect.objectContaining({ + id: expect.any(Number), + name: expect.any(String), + trainers: expect.any(Array), // Should contain a list of trainers + gyms: expect.any(Array) // Should contain a list of gyms + }) + ); + }); + // Length of array should be 2 + expect(result.length).toEqual(2); + // Check owners + expect(result[0].trainers.length).toEqual(2); + expect(result[1].trainers.length).toEqual(1); + expect(result[0].trainers[0].name).toBe("Brock"); + expect(result[0].trainers[1].name).toBe("Erika"); + expect(result[1].trainers[0].name).toBe("Misty"); + // Check Gyms + expect(result[0].gyms.length).toEqual(2); + expect(result[1].gyms.length).toEqual(1); + expect(result[0].gyms[0].city).toBe("Celadon City"); + expect(result[0].gyms[1].city).toBe("Pewter City"); + expect(result[1].gyms[0].city).toBe("Cerulean City"); +}); diff --git a/gymData.js b/gymData.js index 2bda417..887fdf7 100644 --- a/gymData.js +++ b/gymData.js @@ -1,10 +1,10 @@ module.exports = [ - { id: 1, city: 'Saffron City', trainerId: 2 }, - { id: 2, city: 'Fuchsia City', trainerId: 3 }, - { id: 3, city: 'Cinnabar Island', trainerId: 5 }, - { id: 4, city: 'Celadon City ', trainerId: 7 }, - { id: 5, city: 'Cerulean City', trainerId: 8 }, - { id: 6, city: 'Vermilion City', trainerId: 6 }, - { id: 7, city: 'Pewter City', trainerId: 1 }, - { id: 8, city: 'Viridian City', trainerId: 4 }, -] \ No newline at end of file + { id: 1, city: "Saffron City", trainerId: 2 }, + { id: 2, city: "Fuchsia City", trainerId: 3 }, + { id: 3, city: "Cinnabar Island", trainerId: 5 }, + { id: 4, city: "Celadon City", trainerId: 7 }, + { id: 5, city: "Cerulean City", trainerId: 8 }, + { id: 6, city: "Vermilion City", trainerId: 6 }, + { id: 7, city: "Pewter City", trainerId: 1 }, + { id: 8, city: "Viridian City", trainerId: 4 } +]; diff --git a/package.json b/package.json index 04a5cdc..2437586 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,15 @@ { - "name": "data-transformations", - "version": "1.0.0", - "description": "A set of exercises for the data transformation day", - "main": "index.js", - "scripts": { - "test": "jest --watch", - "exercise1": "jest 1.map-filter-find.test --watch", - "exercise2": "jest 2.reduce.test --watch", - "exercise3": "jest 3.data-mining.test --watch" - }, - "author": "Codaisseur", - "license": "ISC", - "devDependencies": { - "jest": "^24.7.1" - } -} \ No newline at end of file + "name": "data-transformations", + "version": "1.0.0", + "description": "A set of exercises for the data transformation day", + "main": "index.js", + "scripts": { + "test": "jest --watch", + "exercise": "jest 4.student-exercises.test --watch" + }, + "author": "Codaisseur", + "license": "ISC", + "devDependencies": { + "jest": "^24.7.1" + } +} diff --git a/trainerData.js b/trainerData.js index bf27fd2..152bb1d 100644 --- a/trainerData.js +++ b/trainerData.js @@ -1,11 +1,11 @@ module.exports = [ - { id: 1, name: 'Brock', pokemonIds: [74, 95] }, - { id: 2, name: 'Sabrina', pokemonIds: [64, 122, 49, 65] }, - { id: 3, name: 'Koga', pokemonIds: [109, 110, 109, 89] }, - { id: 4, name: 'Giovanni', pokemonIds: [112, 51, 31, 34, 113] }, - { id: 5, name: 'Blaine', pokemonIds: [58, 77, 78, 59] }, - { id: 6, name: 'Lt. Surge', pokemonIds: [100, 25, 26] }, - { id: 7, name: 'Erika', pokemonIds: [71, 114, 44] }, - { id: 8, name: 'Misty', pokemonIds: [120, 121] }, - { id: 9, name: 'Ash', pokemonIds: [25, 1, 7, 6, 17, 12] }, -] \ No newline at end of file + { id: 1, name: "Brock", pokemonIds: [74, 95, 44] }, + { id: 2, name: "Sabrina", pokemonIds: [64, 122, 49, 65] }, + { id: 3, name: "Koga", pokemonIds: [109, 110, 109, 89] }, + { id: 4, name: "Giovanni", pokemonIds: [112, 51, 31, 34, 113] }, + { id: 5, name: "Blaine", pokemonIds: [58, 77, 78, 59] }, + { id: 6, name: "Lt. Surge", pokemonIds: [100, 25, 26] }, + { id: 7, name: "Erika", pokemonIds: [71, 114, 44] }, + { id: 8, name: "Misty", pokemonIds: [120, 121] }, + { id: 9, name: "Ash", pokemonIds: [25, 1, 7, 6, 17, 12] } +];