Count the number of people having a property bounded by two numbers

Viewed 26

The following code goes over the 10 pages of JSON returned by GET request to the URL. and checks how many records satisfy the condition that bloodPressureDiastole is between the specified limits. It does the job, but I was wondering if there was a better or cleaner way to achieve this in python

import urllib.request
import urllib.parse
import json

baseUrl = 'https://jsonmock.hackerrank.com/api/medical_records?page='
count = 0
for i in range(1, 11):
    url = baseUrl+str(i)
    f = urllib.request.urlopen(url)
    response = f.read().decode('utf-8')
    response = json.loads(response)
    lowerlimit = 110
    upperlimit = 120
    for elem in response['data']:
        bd = elem['vitals']['bloodPressureDiastole']
        if bd >= lowerlimit and bd <= upperlimit:
            count = count+1
print(count)
1 Answers

There is no access through fields to json content because you get dict object from json.loads (see translation scheme here). It realises access via __getitem__ method (dict[key]) instead of __getattr__ (object.field) as keys may be any hashible objects not only strings. Moreover, even strings cannot serve as fields if they starts with digits or are the same with built-in dictionary methods.

Despite this, you can define your own custom class realising desired behavior with acceptable key names. json.loads has an argument object_hook wherein you may put any callable object (function or class) that take a dict as the sole argument (not only the resulted one but every one in json recursively) & return something as the result. If your jsons match some template, you can define a class with predefined fields for the json content & even with methods in order to get a robust Python-object, a part of your domain logic.

For instance, let's realise the access through fields. I get json content from response.json method from requests but it has the same arguments as in json package. Comments in code contain remarks about how to make your code more pythonic.

from collections import Counter
from requests import get


class CustomJSON(dict):
    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        self[key] = value


LOWER_LIMIT = 110  # Constants should be in uppercase.
UPPER_LIMIT = 120

base_url = 'https://jsonmock.hackerrank.com/api/medical_records'
# It is better use special tools for handling URLs
# in order to evade possible exceptions in the future.
# By the way, your option could look clearer with f-strings
# that can put values from variables (not only) in-place:
# url = f'https://jsonmock.hackerrank.com/api/medical_records?page={i}'
counter = Counter(normal_pressure=0)
# It might be left as it was. This option is useful
# in case of additional counting any other information.

for page_number in range(1, 11):
    records = get(
        base_url, data={"page": page_number}
    ).json(object_hook=CustomJSON)
    # Python has a pile of libraries for handling url requests & responses.
    # urllib is a standard library rewritten from scratch for Python 3.
    # However, there is a more featured (connection pooling, redirections, proxi,
    # SSL verification &c.) & convenient third-party
    # (this is the only disadvantage) library: urllib3.
    # Based on it, requests provides an easier, more convenient & friendlier way
    # to work with url-requests. So I highly recommend using it
    # unless you are aiming for complex connections & url-processing.

    for elem in records.data:
        if LOWER_LIMIT <= elem.vitals.bloodPressureDiastole <= UPPER_LIMIT:
            counter["normal_pressure"] += 1
print(counter)
Related