Skip to content

Instantly share code, notes, and snippets.

@gaucheph
Last active March 1, 2019 22:25
Show Gist options
  • Save gaucheph/5f269967b5b5383dababb41f429cbbc7 to your computer and use it in GitHub Desktop.
Save gaucheph/5f269967b5b5383dababb41f429cbbc7 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"github.com/imroc/req"
"log"
"os"
"time"
)
/*
# GOAL
Given a list of pokemon names, get that pokemon's details and details about
its first 3 abilities from the public Pokemon API at https://pokeapi.co/docs/.
- Individual pokemon details gathered from the /pokemon/:pokemonName endpoint
- Individual ability details gathered from the /abilities/:abilityName endpoint
These requests should be made in parallel with a final result being a
[]map[string]interface{} like:
[
{
"pokemon": {
"height": 123,
"name": "string"
...
},
"abilities": [
{
"name": "string",
"type": "string",
"whatever": false
...
},
...
]
},
...
]
So it should kind of "fan out" per pokemon on their 3 abilities
/---> ability
pokemon ----> ability
\---> ability
/---> ability
pokemon ----> ability
\---> ability
/---> ability
pokemon ----> ability
\---> ability
Each of the 3 abilities for each pokemon should be gathered separately
so that only the abilities gleaned from each pokemon are included with
only that pokemon.
For example, if "bulbasaur" has an ability called "chlorophyll" that none
of the other pokemon have, it would be an error for the "chlorophyll" ability
to appear in another pokemon's "abilities" array.
*/
type PokemonAbilities struct {
dataPokemon interface{}
dataAbilities []interface{}
}
func main() {
start := time.Now()
catchEmAll := []string{
"incineroar",
"bulbasaur",
"ivysaur",
"pikachu",
"gengar",
"squirtle",
"charizard",
"psyduck",
"abra",
"pidgey",
}
cPokemonNeedingAbilities := make(chan interface{})
totalPokemon := len(catchEmAll)
go func() {
for _, eachPokemon := range catchEmAll {
cPokemonNeedingAbilities <- getPokemon(eachPokemon)
}
}()
cPokemonAbilities := make(chan PokemonAbilities)
go func() {
for eachPokemonNeedingAbilities := range cPokemonNeedingAbilities {
abilitiesMapSlice := getPokemonAbilities(eachPokemonNeedingAbilities)
abilityNames := getPokemonAbilitiesNames(abilitiesMapSlice, 99)
totalNames := len(abilityNames)
cThisPokemonAbilitiesNames := make(chan string)
go func() {
for _, eachAbilityName := range abilityNames {
cThisPokemonAbilitiesNames <- eachAbilityName
}
}()
cThisPokemonAbilitiesMaps := make(chan interface{})
go func() {
for eachAbilityName := range cThisPokemonAbilitiesNames {
cThisPokemonAbilitiesMaps <- getAbility(eachAbilityName)
}
}()
cAllAbilities := make(chan []interface{})
go func() {
var allAbilities []interface{}
for len(allAbilities) != totalNames {
allAbilities = append(allAbilities, <-cThisPokemonAbilitiesMaps)
}
cAllAbilities <- allAbilities
}()
pokemonAbility := PokemonAbilities{
eachPokemonNeedingAbilities,
<-cAllAbilities,
}
cPokemonAbilities <- pokemonAbility
}
}()
var pokemonAndAbilities []PokemonAbilities
for len(pokemonAndAbilities) != totalPokemon {
pokemonAndAbilities = append(pokemonAndAbilities, <-cPokemonAbilities)
}
fmt.Println(time.Since(start))
fmt.Println(len(pokemonAndAbilities))
for _, eachPokemonAbility := range pokemonAndAbilities {
fmt.Println("-----------------------------------------------")
fmt.Println("pokemon name: ")
fmt.Println("\t", eachPokemonAbility.dataPokemon.(map[string]interface{})["name"])
fmt.Println("pokemon abilities: ")
for _, eachAbility := range eachPokemonAbility.dataAbilities {
abilityMap := eachAbility.(map[string]interface{})
abilityId := abilityMap["id"]
abilityName := abilityMap["name"]
fmt.Println("\t", abilityId, abilityName)
}
fmt.Println("-----------------------------------------------")
}
}
// takes pokemon object returned by api and returns its "abilities" slice
func getPokemonAbilities(pokemon interface{}) []interface{} {
return pokemon.(map[string]interface{})["abilities"].([]interface{})
}
// takes an abilities slice and returns a slice of their names
func getPokemonAbilitiesNames(pokemonAbilities []interface{}, limit int) []string {
// container for ability names
var pokemonAbilitiesNames []string
// determine if pokemon has less abilities than passed-in limit
var total int
if limit > len(pokemonAbilities) {
total = len(pokemonAbilities)
} else {
total = limit
}
// get abilities until desired total is reached
for i := 0; i < total; i++ {
abilityMap := pokemonAbilities[i].(map[string]interface{})
abilityName := getAbilityNameFromAbilityMap(abilityMap)
pokemonAbilitiesNames = append(pokemonAbilitiesNames, abilityName)
}
// return array of ability names
return pokemonAbilitiesNames
}
// gets the name of an ability from an ability object
func getAbilityNameFromAbilityMap(abilityMap map[string]interface{}) string {
someAbilityIfc := abilityMap["ability"].(map[string]interface{})
someAbilityName := someAbilityIfc["name"].(string)
log.Println("found ability name: " + someAbilityName)
return someAbilityName
}
// makes http requests and exits if any problems occur like timeouts or json decode errors
func jsonRequestHandler(url string) interface{} {
response, e := req.Get(url)
if e != nil {
// making request failed
log.Fatal(e)
os.Exit(1)
}
var bodyJson interface{}
notOk := response.ToJSON(&bodyJson)
if notOk != nil {
// decoding body content to JSON failed
log.Fatal(notOk)
os.Exit(1)
}
return bodyJson
}
const BaseUrl = "https://pokeapi.co/api/v2"
// gets a pokemon from the api using its name
func getPokemon(pokemonName string) interface{} {
log.Println("getting pokemon: " + pokemonName)
url := BaseUrl + "/pokemon/" + pokemonName
thisPokemon := jsonRequestHandler(url)
log.Println("got pokemon: " + pokemonName)
return thisPokemon
}
// gets an ability from the api using the ability's name
func getAbility(abilityName string) interface{} {
log.Println("getting ability: " + abilityName)
url := BaseUrl + "/ability/" + abilityName
thisAbility := jsonRequestHandler(url)
log.Println("got ability: " + abilityName)
return thisAbility
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment