Home > OS >  Using range() as a value when creating a dictionary
Using range() as a value when creating a dictionary

Time:06-05

I am trying to use range() to fill in values in a list of dictionaries from a custom range.

I have this code:

import requests
import json
import time

test = []
for x in range(5000,5020):
    page_url = f'https://api.jikan.moe/v4/anime/{x}/full'
    response = requests.get(page_url)
    json_data = json.loads(response.text)
    test.append(json_data)
    time.sleep(1)

anime_data = []
for dic in test:
    anime = {
        'call_id': range(5000,5020),
        'title': dic.get('data',{}).get('title','title not found'),
        'mal_id': dic.get('data',{}).get('mal_id', 'id not found'),
        'url': dic.get('data',{}).get('url', 'url not found')
    }
    anime_data.append(anime)

The goal is to use numbers from 5000 to 5020 in sequence for the 'call_id' key of each dict, so that the output would look like:

[{'call_id': 5000,
  'title': 'title not found',
  'mal_id': 'id not found',
  'url': 'url not found'},
 {'call_id': 5001,
  'title': 'title not found',
  'mal_id': 'id not found',
  'url': 'url not found'},
 {'call_id': 5002,
  'title': 'Bari Bari Densetsu',
  'mal_id': 5002,
  'url': 'https://myanimelist.net/anime/5002/Bari_Bari_Densetsu'}]

The code did not work as intended. How can I get the desired result?

CodePudding user response:

Another approach to the problem: fundamentally, we would like to iterate over two lists in parallel - the raw API responses, and the numbers (from the range) that we want to use in the anime entries. So, the naive response is to use zip, thus:

for call_id, dic in zip(range(5000, 5020), test):
    anime = {
        'call_id': call_id,
        'title': dic.get('data',{}).get('title','title not found'),
        'mal_id': dic.get('data',{}).get('mal_id', 'id not found'),
        'url': dic.get('data',{}).get('url', 'url not found')
    }
    anime_data.append(anime)

However, this overlooks a more specific, built-in tool: the built-in enumerate function. We just have to set the start point appropriately; we don't need to worry about how many elements there are - it will just keep incrementing until we run out.

That looks like:

for call_id, dic in enumerate(test, 5000):
    anime = {
        'call_id': call_id,
        'title': dic.get('data',{}).get('title','title not found'),
        'mal_id': dic.get('data',{}).get('mal_id', 'id not found'),
        'url': dic.get('data',{}).get('url', 'url not found')
    }
    anime_data.append(anime)

CodePudding user response:

Since there is already a loop to produce all the same 'call_id' values that are in range(5000,5020) - in order to make the API calls in the first place - a simple approach is to just create the final data directly in the first loop, instead of storing json_data results and trying to process them in a later loop. That looks like:

anime_data = []
for x in range(5000,5020):
    page_url = f'https://api.jikan.moe/v4/anime/{x}/full'
    response = requests.get(page_url)
    json_data = json.loads(response.text)
    anime = {
        'call_id': range(5000,5020),
        'title': json_data.get('data',{}).get('title','title not found'),
        'mal_id': json_data.get('data',{}).get('mal_id', 'id not found'),
        'url': json_data.get('data',{}).get('url', 'url not found')
    }
    anime_data.append(anime)
    time.sleep(1)

We can organize the logic better by using functions to split up the tasks performed each time through the loop, and by pre-computing the .get('data',{}) result:

def query_api(anime_id):
    page_url = f'https://api.jikan.moe/v4/anime/{anime_id}/full'
    response = requests.get(page_url)
    return json.loads(response.text).get('data',{})

def make_anime_data(anime_id, raw_data):
    return {
        'call_id': anime_id,
        'title': raw_data.get('title','title not found'),
        'mal_id': raw_data.get('mal_id', 'id not found'),
        'url': raw_data.get('url', 'url not found')
    }

anime_data = []
for x in range(5000,5020):
    raw_data = query_api(x)
    anime_data.append(make_anime_data(x, raw_data))
    time.sleep(1)
  • Related