Flatten List of Lists Recursively - python-3.x

Can someone illustrate or decompose how this recursive function is executed
def flatten(S):
if S == []:
return S
if isinstance(S[0], list):
return flatten(S[0]) + flatten(S[1:])
return S[:1] + flatten(S[1:])
s=[[1,2],[3,4]]
print("Flattened list is: ",flatten(s))
How could I trace the execution of this algorithm?

Ok so this is a recursive function as you have stated. It is a mostly 'look at the next element and decide what to do with it' method. It is started with the base case.
if S == []:
return S
So this makes sense. You have an empty list, so you would expect to get back an empty list, it's flat.
if isinstance(S[0], list):
return flatten(S[0]) + flatten(S[1:])
Next is the first 'look at the next element, decide what to do', if I receive a list and at the first element there is a list, I will get the program to run this same flattening method on the first element.
But then comes the rest of the list, we don't know if that is flat so I will be doing the same thing for that calling flatten on that as well.
When this returns they should both be flat lists. Adding two lists just joins them together into a new list so this would be returned up a level to the previous call of the recursive method or return to the user.
return S[:1] + flatten(S[1:])
From before we know that the first element of the list is not a list as the if statement was if isinstance(S[0], list) so this is just taking a list with the first element stored in it and just like before running flatten on the rest of the list as we don't know whether the rest of the list is flat or not.
As for tracing, if you don't have Pycharm or pdb is to complex for you. Throw in some prints within each of the if statements. Don't be shy, you're the one that's going to read them. do a print(f"First element was a list: {S[0]}, {S[1:]}") that will be fine if you're a beginner dealing with such a small amount of code. Otherwise try PDB or such.

Related

How do I check if a filter returns no results in Python 3?

I have a filter function that I use to clean certain items out of a list:
def filterOutPatternMatches(objList, matchKey, matchPatterns):
def checkPatterns(obj):
delete_it=True
for pat in matchPatterns:
matchString=obj[matchKey]
if pat.search(matchString):
delete_it=False
break
return delete_it
result = filter(checkPatterns, objects);
return result
It works fine, except that there is no easy way for me to find out if the filter() function has returned an empty iterable.
I want to know if the list is empty, and if so, do one thing. If not, do something else.
There are three ways to approach this:
Convert the filter object to a list, then check if it is empty:
l = list(filterObject)
if (len(l) == 0):
# The filterObject is empty, do the empty thing
The problem is that you have to convert the filterObject iterable to a list, which is potentially a very expensive operation if the iterable is very large.
Use next() to pull the first item off of the filter object iterable. If there is nothing in the list, you will get a StopIteration error, which you have to catch.
You will also need to process the first item outside of the rest, since you can't put it back on the iterable.
try:
firstItem = next(filterObject)
# Do whatever you need to do with the first item in the filterObject iterable
except StopIteration:
# Do whatever you do if there are no filter results
for o in filterObject:
# Now handle the rest of the filter results
Problems with this: you have to handle the first item outside of your for loop, which is annoying. If you want to run some aggregation function on the filterObject iterable, you have to deal with the one item that you pulled off separately. Very un-pythonic.
Iterate over the filterObject as you normally would, but set a flag if it is empty:
filterObject = filter(someFunc, someIterable)
itWasEmpty=true
for x in filterObject:
itWasEmpty=false
# Process the filterObject items
if itWasEmpty:
# Do whatever you do if it's empty.
Cons: You need to process the entire list manually. Can't pass the filter object to an aggregation function.
That's all I can come up with!

Python filter string

With the following command i can print the balance of my assets from my binance ac.
Command:
USDT_BAL = client.futures_account_balance(asset='USDT')
Return:
[{'accountAlias': 'sRuXXqTioCfWFz', 'asset': 'BNB', 'balance': '0.00000142', 'withdrawAvailable': '0.00000142', 'updateTime': 1621516315044}, {'accountAlias': 'sRuXXqTioCfWFz', 'asset': 'USDT', 'balance': '0.00000000', 'withdrawAvailable': '0.00000000', 'updateTime': 0}, {'accountAlias': 'sRuXXqTioCfWFz', 'asset': 'BUSD', 'balance': '0.00000000', 'withdrawAvailable': '0.00000000', 'updateTime': 0}]
It returns the balances of other assets, but i only need the balance of the USDT asset. How could I filter the USDT_BAL variable for it?
Expanding on my comment:
You have a list of dict. list access is done by iteration (for loops) or by indexes. my_list[0], etc..
dict access can, also done by iteration, but a big benefit is keyed access. my_dict['some_key'], etc..
Python has simplified ways to do common list and dict building commonly called "comprehensions".
So a list comprehension for something like:
my_list = []
for i in range(10):
my_list.append(i)
Could be written as
my_list = [i for i in range(10)]
What I gave you isn't necessarily a list comprehension but follows the same idea. It's called a "generator expression". The difference is it generates some output when you iterate over it but it's output as a whole isn't in the form of some built-in collection (list or dict).
The reason it makes sense in this context is:
I need to iterate over the list to find dict with the correct 'asset' key.
I expect there is only one occurrence of this so I care only about the first occurrence.
So to break it down you have a generator expression:
(i['balance'] for i in USDT_BAL if i['asset'] == 'USDT')
Which is roughly equivalent to.
def my_gen():
for i in USDT_BAL:
if i['asset'] == 'USDT':
yield i['balance']
Or if you're not familiar with generators and would like it as a list:
my_list = []
for i in USDT_BAL:
if i['asset'] == 'USDT':
my_list.append(i['balance'])
So now you can see we have a problem.
If we have it as a list comprehension it's in the form of a list with one element.
print(my_list) # ['0.00000000']
We could access it with my_list[0] but that looks ugly IMO but to each it's own.
So that's where the next function comes in.
According to the docs next calls the __next__ method on an iterator (which a generator is) and basically advances the generator.
So if our generator were to produce 1 then 2 then 3, calling next(my_gen) would produce 1 then calling it again would produce 2 and so on.
Since I expect this generator expression to only produce 1 item, I only call it once. Giving it a default of None means, if it's empty, rather than raising an error it will produce None.
So:
next((i['balance'] for i in USDT_BAL if i['asset'] == 'USDT'), None)
creates a generator that iterates over your list, only produces the 'balance' key of dicts who's 'asset' key equals 'USDT' and calls next on that generator with a default of None.

How to include the last element in the for loop? (Run the last element along with all other elements)

I have searched around, and have only found answers to questions like, "How to detect the last element?", or, "How to extract/change/skip to the last element?", which are not quite what I need.
I want to run a for loop of a list, from the first element to the last element of the list, instead of to the second last element. I don't want the last element to be any more special than all other elements; I just want it to be included while I run the for loop.
If there is a small piece of code that I can embed in the function, that would be great. I'm also fine with creating a separate function, then call that function when needed (I'm a newbie, so if you are going to do this approach, please also include how I would call another function within my function).
Here's my code (without running the last element):
**import sympy
from sympy import Q
k,l,m,n=sympy.symbols('k l m n')
Z=sympy.symbols('Z')
sol_list=[]
def findZ(k,l,m,n,Z):
max_loop=len(coeff_list)
for i in range(max_loop): # <- Here's the trouble-maker
k=coeff_list[i][0]
l=coeff_list[i][1]
m=coeff_list[i][2]
n=coeff_list[i][3]
expr=k*Z**3+l*Z**2+m*Z+n
sol=sympy.solve(expr,'Z')
for sol in sol:
if sympy.ask(Q.real(sol))==True:
sol_list.append(sol)
if len(sol_list)>1:
max(sol_list)
remove_dup(sol_list)
return sol_list**
coeff_list=[[1, -1.0000007185951756, 0.2959936217035236, -2.1269958855852493e-07], [1, -1.0000006956368632, 0.27738242112279193, -1.9295743735357098e-07], [1, -1.0000006741001182, 0.26047291179805043, -1.755848206487531e-07],[...]
If it helps, coeff_list is a list within a list, with the length of the coeff_list is 24. Each nested list contains 4 items.

Function to iterate through a nested list and append other lists isn't functioning properly

I am currently trying to write a function to iterate through a nested list and check if one item from the list, 'team', is already in a separate list 'teams'.
If it is not, I want to append a nested list, 'player_values' with a different item from the original nested list that was examined, in the form of a new list in the nested list.
If it is, I want to append the nested list 'player_values' with the item from the original nested list, but I want to add it to the most recent list in the nested list 'player_values' instead of creating a new list.
Currently, my code looks like this :
def teams_and_games(list, player, idx):
teams = []
player_values = []
x = 0
y = -1
for rows in list:
if player == list[x][BD.player_id] and list[x][BD.team] not in teams:
teams.append(list[x][BD.team])
player_values.append([list[x][idx]])
x += 1
y += 1
elif player == list[x][BD.player_id]:
player_values[y].append(list[x][idx])
x += 1
return player_values, teams
However, when I run the code in my main, using
values, teams = teams_and_games(NiceRow, name, BD.games)
print(values)
print(teams)
It only prints empty lists. The fact that it prints empty lists shows that it is returning the correct variables, but I can't figure out why the code in the function is failing to add anything to the lists. I have tried switching the .append with a more simple list += statement, but the result has been the same so far.
Ideally, I would be getting a nested list, containing an amount of lists equal to the number of items added to the other 'teams' list, and the list of teams in the order they were added.
The data I am working with is a nested list pulled from a .csv file, which has been formatted slightly using the .strip() and .split() commands. Each number has been converted to an int, and strings left as they are. The .CSV file it is from has 19 columns and ~80,000 rows, with each column always being either a string or an int.

Printing a list method return None

I am an extremely begginer learning python to tackle some biology problems, and I came across lists and its various methods. Basically, when I am running print to my variable I get None as return.
Example, trying to print a sorted list assigned to a variable
list1=[1,3,4,2]
sorted=list1.sort()
print(sorted)
I receive None as return. Shouldn't this provide me with [1,2,3,4]
However, when printing the original list variable (list1), it gives me the sorted list fine.
Because the sort() method will always return None. What you should do is:
list1=[1,3,4,2]
list1.sort()
print(list1)
Or
list1=[1,3,4,2]
list2 = sorted(list1)
print(list2)
You can sort lists in two ways. Using list.sort() and this will sort list, or new_list = sorted(list) and this will return a sorted list new_list and list will not be modified.
So, you can do this:
list1=[1,3,4,2]
sorted=sorted(list1)
print(sorted)
Or you can so this:
list1=[1,3,4,2]
list1.sort()
print(list1)

Resources