PokeAPI + Angular: How to get pokemon's evolution chain

All we need is an easy explanation of the problem, so here it is.

I am an Angular novice and am learning a little by trying to pull the evolution chain for each pokemon using pokeapi but having a difficult time because of deep nesting.

A typical response object is returned like this:

{
  "baby_trigger_item": null,
  "id": 2,
  "chain": {
    "evolution_details": [],
    "evolves_to": [
      {
        "evolution_details": [
          {
            "min_level": 16,
            "min_beauty": null,
            "time_of_day": "",
            "gender": null,
            "relative_physical_stats": null,
            "needs_overworld_rain": false,
            "turn_upside_down": false,
            "item": null,
            "trigger": {
              "url": "http://pokeapi.co/api/v2/evolution-trigger/1/",
              "name": "level-up"
            },
            "known_move_type": null,
            "min_affection": null,
            "party_type": null,
            "trade_species": null,
            "party_species": null,
            "min_happiness": null,
            "held_item": null,
            "known_move": null,
            "location": null
          }
        ],
        "evolves_to": [
          {
            "evolution_details": [
              {
                "min_level": 36,
                "min_beauty": null,
                "time_of_day": "",
                "gender": null,
                "relative_physical_stats": null,
                "needs_overworld_rain": false,
                "turn_upside_down": false,
                "item": null,
                "trigger": {
                  "url": "http://pokeapi.co/api/v2/evolution-trigger/1/",
                  "name": "level-up"
                },
                "known_move_type": null,
                "min_affection": null,
                "party_type": null,
                "trade_species": null,
                "party_species": null,
                "min_happiness": null,
                "held_item": null,
                "known_move": null,
                "location": null
              }
            ],
            "evolves_to": [],
            "is_baby": false,
            "species": {
              "url": "http://pokeapi.co/api/v2/pokemon-species/6/",
              "name": "charizard"
            }
          }
        ],
        "is_baby": false,
        "species": {
          "url": "http://pokeapi.co/api/v2/pokemon-species/5/",
          "name": "charmeleon"
        }
      }
    ],
    "is_baby": false,
    "species": {
      "url": "http://pokeapi.co/api/v2/pokemon-species/4/",
      "name": "charmander"
    }
  }
}

I have to get to evolves_to property, and grab species.name as well as evolution_details.min_level and evolution_details.trigger.name, and evolution_details.item if not null

But as you can see, the evolves_to property, itself contains another evolves_to nested inside, which has another nested inside

This is my sad attempt (after http.get) and I’m just stuck now.

var evoObject = response.data;

function loopEvo(obj){
    angular.forEach(obj, function(value, key, object){
        if (key == 'evolves_to' && value != []) {
            //from here I can get top level data, but...
        }
    });
}

loopEvo(evoObject.chain);

I don’t know how to recursively dive into objects and continually grab data, can anyone provide any help? I would love to use this as a great learning opportunity in traversing complex json objects.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

You could always just avoid using Angular and stick with plain JS to build out your evolution chain… try giving this a go, it was based on your angular for loop. This should leave you with an array (evoChain) of the objects containing the data you are looking for ordered from first evolution at 0 index to last evolution at the last index.

var evoChain = [];
var evoData = response.data.chain;

do {
  var evoDetails = evoData['evolution_details'][0];

  evoChain.push({
    "species_name": evoData.species.name,
    "min_level": !evoDetails ? 1 : evoDetails.min_level,
    "trigger_name": !evoDetails ? null : evoDetails.trigger.name,
    "item": !evoDetails ? null : evoDetails.item
  });

  evoData = evoData['evolves_to'][0];
} while (!!evoData && evoData.hasOwnProperty('evolves_to'));

In your sample case above the resulting array should appear as follows:

[{
    "species_name": "charmander",
    "min_level": 1,
    "trigger_name": null,
    "item": null
}, {
    "species_name": "charmeleon",
    "min_level": 16,
    "trigger_name": "level-up",
    "item": null
}, {
    "species_name": "charizard",
    "min_level": 36,
    "trigger_name": "level-up",
    "item": null
}]

Method 2

The approved answer above does not work if there are multiple evolutions such as Eevee or Snorunt. That will only return the first evolution e.g. Vaporeon

The following checks number of evolutions and runs through them all.

let evoChain = [];
let evoData = chain.chain;

do {
  let numberOfEvolutions = evoData['evolves_to'].length;  

  evoChain.push({
    "species_name": evoData .species.name,
    "min_level": !evoData ? 1 : evoData .min_level,
    "trigger_name": !evoData ? null : evoData .trigger.name,
    "item": !evoData ? null : evoData .item
  });

  if(numberOfEvolutions > 1) {
    for (let i = 1;i < numberOfEvolutions; i++) { 
      evoChain.push({
        "species_name": evoData.evolves_to[i].species.name,
        "min_level": !evoData.evolves_to[i]? 1 : evoData.evolves_to[i].min_level,
        "trigger_name": !evoData.evolves_to[i]? null : evoData.evolves_to[i].trigger.name,
        "item": !evoData.evolves_to[i]? null : evoData.evolves_to[i].item
     });
    }
  }        

  evoData = evoData['evolves_to'][0];

} while (!!evoData && evoData.hasOwnProperty('evolves_to'));

return evoChain;

Method 3

brandudno is correct: the extra if(numberOfEvolutions) is the more complete approach (THANKS @brandudno! This really helped me solve for ALL the use cases – including eevee!)

I like the use of !!evoData in the while statement now that I took the time to understand it, but it was confusing for me at 1st, so I made a minor modification that still works and may be easier for other new developers (continues until evoData becomes undefined).

Lastly, I made a minor change in case others also prefer to use the . annotation (evoData.evolves_tovs.evoData[‘evolves_to’]`) to take advantage of autocomplete, etc.

let evoChain = [];
let evoData = chain.chain;

do {
  let numberOfEvolutions = evoData.evolves_to.length;  

  evoChain.push({
    "species_name": evoData .species.name,
    "min_level": !evoData ? 1 : evoData .min_level,
    "trigger_name": !evoData ? null : evoData .trigger.name,
    "item": !evoData ? null : evoData .item
  });

  if(numberOfEvolutions > 1) {
    for (let i = 1;i < numberOfEvolutions; i++) { 
      evoChain.push({
        "species_name": evoData.evolves_to[i].species.name,
        "min_level": !evoData.evolves_to[i]? 1 : evoData.evolves_to[i].min_level,
        "trigger_name": !evoData.evolves_to[i]? null : evoData.evolves_to[i].trigger.name,
        "item": !evoData.evolves_to[i]? null : evoData.evolves_to[i].item
     });
    }
  }        

  evoData = evoData.evolves_to[0];

} while (evoData != undefined && evoData.hasOwnProperty('evolves_to'));

return evoChain;

Method 4

I am using a recursive function to solve this.
Here’s how it goes with plain JavaScript.

let evoChain = [];

function getEvo(arr) {
  if (arr[0].evolves_to.length > 0) {
    evoChain.push(arr[0].species.name);
    getEvo(arr[0].evolves_to);
  } else {
    evoChain.push(arr[0].species.name);
    return 0;
  }
}
getEvo([data.chain]);```

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply