Store results of nested for loop as single concatenated string

Question:

I’m trying to store the values of the function below to a single string that I can input into a query leveraging an F-string. The output looks correct but is really just a few separated print statements.

How can I store the output of the below to a single string?

import pandas as pd
view_dict = [{'id':'168058','viewtime_min':'2023-01-26 21:00:59.435 -0600','viewtime_max':'2023-01-26 21:59:59.435 -0600'},
                 {'id':'167268','viewtime_min':'2023-01-26 21:59:59.435 -0600','viewtime_max':'2023-01-26 21:59:59.435 -0600'},
                 {'id':'167268','viewtime_min':'2023-01-26 21:59:59.435 -0600','viewtime_max':'2023-01-26 21:59:59.435 -0600'}] 

def get_where_clause(view_dictionary: dict):
    where_clause = " "
    for index in range(len(view_dictionary)): 
        if index != max(range(len(view_dictionary))):
            print(f'''(b.id = {view_dictionary[index]['id']}
                and b.viewed_at between coalesce({view_dictionary[index]['viewtime_min']},published_at) and {view_dictionary[index]['viewtime_max']})
                or''')
        else:
            print(f'''(b.id = {view_dictionary[index]['id']}
                and b.viewed_at between coalesce({view_dictionary[index]['viewtime_min']},published_at) and {view_dictionary[index]['viewtime_max']})''')

x = get_where_clause(view_dict)

x

I’m expecting this to store to a value but when accessing the value nothing is stored.

Asked By: Doug

||

Answers:

The print command just writes the text to your screen, it does not return a value. Your function needs to return a value for it to be "storable" in a variable.

I rewrote, formatted and commented your code, it now returns a string containing the where clause:

view_dict = [
    {
        "id": "168058",
        "viewtime_min": "2023-01-26 21:00:59.435 -0600",
        "viewtime_max": "2023-01-26 21:59:59.435 -0600",
    },
    {
        "id": "167268",
        "viewtime_min": "2023-01-26 21:59:59.435 -0600",
        "viewtime_max": "2023-01-26 21:59:59.435 -0600",
    },
    {
        "id": "167268",
        "viewtime_min": "2023-01-26 21:59:59.435 -0600",
        "viewtime_max": "2023-01-26 21:59:59.435 -0600",
    },
]


def get_where_clause(view_dictionary: list[dict]) -> str:
    # This is where all the outputs will be stored
    outputs = []
    # Loop over the view_dictionary, which is actually a list of dicts
    for vd in view_dictionary:
        # Just for readability: get the relevant parts out of each dict
        bid = vd['id']
        tmin = vd['viewtime_min']
        tmax = vd['viewtime_max']
        # Place everything in an f-string
        output = f"(b.id = {bid} and b.viewed_at between coalesce('{tmin}',published_at) and '{tmax}') or"
        # Print, just for fun, you won't use this output
        print(output)
        # Place every output in the outputs list
        outputs.append(output)
    # Create the actual WHERE clause by concatenating all outputs into a single string
    # the [:-3] makes sure the last 'or' keyword is removed
    where_clause = " ".join(outputs)[:-3]
    return where_clause


x = get_where_clause(view_dict)
Answered By: Saaru Lindestøkke

You aren’t actually returning or storing anything, print simply writes to the console. Ideally, you’d collect these into something like a list to be returned, which you can then use str.join to concatenate:

view_dict = [{'id':'168058','viewtime_min':'2023-01-26 21:00:59.435 -0600','viewtime_max':'2023-01-26 21:59:59.435 -0600'},
                 {'id':'167268','viewtime_min':'2023-01-26 21:59:59.435 -0600','viewtime_max':'2023-01-26 21:59:59.435 -0600'},
                 {'id':'167268','viewtime_min':'2023-01-26 21:59:59.435 -0600','viewtime_max':'2023-01-26 21:59:59.435 -0600'}] 

def get_where_clause(view_dictionary: dict):
    # I've changed this to a list
    where_clause = []

    for index in range(len(view_dictionary)): 
        if index != max(range(len(view_dictionary))):
            where_clause.append(f'''(b.id = {view_dictionary[index]['id']}
                and b.viewed_at between coalesce({view_dictionary[index]['viewtime_min']},published_at) and {view_dictionary[index]['viewtime_max']})
                or''')
        else:
            where_clause.append(f'''(b.id = {view_dictionary[index]['id']}
                and b.viewed_at between coalesce({view_dictionary[index]['viewtime_min']},published_at) and {view_dictionary[index]['viewtime_max']})''')
    
    # join together here
    return ' '.join(where_clause)



x = get_where_clause(view_dict)

print(x)

I know it isn’t asked for, but this could be cleaned up a little more with some basic iteration techniques:

def get_where_clause(view_dictionary: list):
    # I've changed this to a list
    where_clause = []

    # grab the last element and iterate over a slice
    # rather than checking an index
    last = view_dictionary[-1]

    # iterate over the views directly, don't use an index
    for item in view_dictionary[:-1]:
        where_clause.append(f'''(b.id = {item['id']}
                and b.viewed_at between coalesce({item['viewtime_min']},published_at) and {item['viewtime_max']})
                or''')
    
     where_clause.append(f'''(b.id = {last['id']}
                and b.viewed_at between coalesce({last['viewtime_min']},published_at) and {last['viewtime_max']})''')
    
    # join together here
    return ' '.join(where_clause)

And to simplify formatting, you can indent a single string by using parentheses:

    for item in view_dictionary:
        where_clause.append(
            f"(b.id = {item['id']} "
            "and b.viewed_at between "
            f"coalesce({item['viewtime_min']},published_at) "
            f"and {item['viewtime_max']})"
        )
    
    # rather than checking for first/last, you can join on
    # 'or' 
    return ' or '.join(where_clause)
Answered By: C.Nivs
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.