3
\$\begingroup\$

What can I do to reduce the number of steps until I get to the final code result?
Is it possible or is this the way with the least number of lines of code?

import requests
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }
url = f'https://api.sofascore.com/api/v1/event/9567795/graph'
response = requests.get(url, headers=headers).json()
goal_minute = 88
graphs = response['graphPoints']
graph_minutes = graphs[-5:]
minutes_list = []
for minute in graph_minutes:
    minutes_list.append(minute["minute"])
if goal_minute in minutes_list:
    print('Ok')
\$\endgroup\$

2 Answers 2

14
\$\begingroup\$

Lines of code is typically not a useful metric. Many other characteristics are more important: correctness, robustness, readability, simplicity, and so forth. But even if we focus narrowly on code size, number of lines if usually the wrong way to think about it. Less code can be beneficial sometimes: if done well, for example, less code is easier to read than more code. But it's quite possible to have both more lines and less code (if the lines are short). Many short lines, especially if well organized into meaningful groups prefaced by substantive comments, are often easier to read and understand than a small number of long, dense lines of code.

You might need more code. HTTP requests can fail. You should probably be writing code to handle that scenario.

try:
    response = requests.get(url, headers=headers).json()
except Exception as e:
    # Adjust after you decide how you want to respond to failure.
    print(e)
    quit()

The primary opportunity for brevity. You can collect the needed minutes in less code by dropping intermediate variables and using a comprehension. Although it could be written in a single line, why not help your reader a bit by laying it out nicely? Whitespace imposes no tax on the reader: be generous with it.

minutes = [
    d['minute']
    for d in response['graphPoints'][-5:]
]
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Also, consider using a set instead of a list if there might be a large number of minutes and many goal_minutes need to be checked \$\endgroup\$ Commented Jan 18, 2022 at 11:41
  • 1
    \$\begingroup\$ Hi @TamoghnaChowdhury Could you show us this tip in an answer and in code form so that I and others can understand how this improvement can and should be implemented? Thanks in advance! \$\endgroup\$ Commented Jan 18, 2022 at 14:55
  • \$\begingroup\$ @j.g. has already incorporated my suggestion into their answer. \$\endgroup\$ Commented Jan 19, 2022 at 13:42
3
\$\begingroup\$

In general, high line count is at most a code smell (i.e. a possible hint one should refactor). But if it is, that's because more declarative code might be clearer.

In this case, I want to discuss an unrelated issue we should address in such an attempt. Creating your list in the first place is unnecessarily eager. Your last if statement's boolean can be changed to any(minute['minute'] == goal_minute for minute in response['graphPoints'][-5:]). (I've still "wasted" a line making goal_minute, because == 88 isn't as clear in its significance.)

In a comment under @FMc's answer, @TamoghnaChowdhury suggested making a set instead of a list, which makes membership checking quicker. The smallest change to your original code to achieve this is using minutes_set = set() (then append becomes add); if you want something declarative instead of imperative, which reduces line count as you originally suggested, you can try goal_minute in {minute['minute'] for minute in response['graphPoints'][-5:]}. But I don't recommend this, because even a set is still eager, i.e. it takes up space we don't need. Admittedly, response['graphPoints'] has to be eager no matter what, but it's good to get into the habit of passing a generator to any. (If nothing else, it can be even faster if an example is found early.)

\$\endgroup\$
6
  • \$\begingroup\$ Good catch that it doesn't need to be an eager collection! If they need it again, though... Also, if there are multiple goal minutes, they can all be in a set and minute == goal_minute will become minute in goal_minutes \$\endgroup\$ Commented Jan 19, 2022 at 13:41
  • \$\begingroup\$ Why don't you just pass a generator to in? Then it would be lazy as well. \$\endgroup\$ Commented Jan 22, 2022 at 2:42
  • \$\begingroup\$ @KellyBundy Maybe I'm missing something. How can I replace response['graphPoints'][-5:] with something lazy? \$\endgroup\$
    – J.G.
    Commented Jan 22, 2022 at 8:01
  • \$\begingroup\$ I guess you could make the [-5:] lazy with for example islice on reversed or with a range from -5 to -1. But what I meant is goal_minute in (minute['minute'] for minute in response['graphPoints'][-5:]). \$\endgroup\$ Commented Jan 22, 2022 at 8:04
  • \$\begingroup\$ @KellyBundy I suppose the conceptual advantage of any is it doesn't require us to characterize the condition in terms of such a collection. But this is the kind of decision which, in a large codebase, matters less than consistency with the rest of it. \$\endgroup\$
    – J.G.
    Commented Jan 22, 2022 at 8:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.