Python – next function – list comprehension - python-3.x

I am trying to understand why code written in a compact style works and the “same code-ish” in a more verbose style does not work.
This code yields the correct answer:
which_days = [5, 12, 19, 26]
buy_day = next((i for i in which_days if i in range(13, 20)), None)
print(buy_day)
>>> 19 <- correct answer
This code does yields a pair of brackets:
which_days = [5, 12, 19, 26]
buy_day = []
for i in which_days:
if i in range((13, 20), None):
next(which_days)
print(buy_day)
>>>[] <- This is a pair of brackets

Here is the long form of the correct code:
which_days = [5, 12, 19, 26]
def _tmp_variable():
for i in which_days:
if i in range(13, 20):
yield i
buy_day = next(_tmp_variable(), None)
del _tmp_variable
The incorrect code does not work because it is doing something entirely different (not to mention illegal; you can't call next() on a list in Python 3).
Here's a different long form, which builds the entire list eagerly instead of lazily yielding one element at a time:
which_days = [5, 12, 19, 26]
_tmp_variable = []
for i in which_days:
if i in range(13, 20):
_tmp_variable.append(i)
buy_day = _tmp_variable[0] if _tmp_variable else None
del _tmp_variable
As you can see, this is wasteful because it builds up an entire list and then only uses the first element. Fortunately, the short version of your code does not do this.

Related

Python - Invert list order

I want to invert list order without changing the values.
The original list is the following:
[15, 15, 10, 8, 73, 1]
While the resulting expecting list is:
[10, 8, 15, 15, 1, 73]
The example has been taken from a real data handling problem from a more complex pandas data frame.
I proposed a list problem only to simplify the issue. So, it can also be a pandas function.
zlist = int(len(list)/2)
for i in range(0, zlist):
a, b = list.index(sorted(list, reverse=True)[i]), list.index(sorted(list,reverse=False)[i])
list[b], list[a] = list[a], list[b]

Removing duplicates in a list (Python 3.8.3)

I want to just understand why the below code is not very good at removing duplicates:
numbers = [4, 4, 4, 10, 10, 10, 20, 20, 2]
for duplicates in numbers:
numbers.count(duplicates)
if numbers.count(duplicates) > 1:
numbers.remove(duplicates)
numbers.sort()
print(numbers)
The final output is: [2, 4, 10, 10, 20]
This could just be a fundamental error in my understanding of the methods used, but I would like to know what is wrong here.
PS. I'm aware I could do an easier version of this code posted below, but I am just curious.
Easier version of above code:
numbers = [4, 4, 4, 10, 10, 10, 20, 20, 2]
unique_list = []
for duplicates_check in numbers:
if duplicates_check not in unique_list:
unique_list.append(duplicates_check)
unique_list.sort()
print(unique_list)
Thanks in advance for your help!
Modifying the list as you're iterating over it is a bad idea (since the loop gets "confused" about which index it's on).
Since you're sorting the numbers in the end, you apparently don't care about the order.
Just do
numbers = sorted(set(numbers))
– that is, convert the numbers to a set; sets can only contain each element once, then use sorted() to sort the set into a new list.
If you would care about the order,
def unique_in_order(iterable):
seen = set()
for item in iterable:
if item not in seen:
seen.add(item)
yield item
numbers = list(unique_in_order(iterable))
is your recipe.

Iterate through 2d list and append to multiple other lists

I have a 2-D list:
lst = [[1,2,3,4,5,6,7,8,9],[11,12,13,14,15]]
I want to store 0 to N-1 of each list in the 2-D list in a separate list and 1 to N in another one. So I create two new lists to be appended where they have the same length as the 2-D lst:
alpha, beta = [[]]*len(lst), [[]]*len(lst)
Then I run this code:
for i in range(len(lst)):
for j in range(len(lst[i])-1):
alpha[i].append(lst[i][j])
beta[i].append(lst[i][j+1])
But the for-loops seem to be iterating through all lists every time.
I want to get the result
alpha = [[1,2,3,4,5,6,7,8],[11,12,13,14]]
beta = [[2,3,4,5,6,7,8,9],[12,13,14,15]]
Instead, I am getting
alpha = [[1,2,3,4,5,6,7,8,11,12,13,14],[1,2,3,4,5,6,7,8,11,12,13,14]]
beta = [[2,3,4,5,6,7,8,9,12,13,14,15],[2,3,4,5,6,7,8,9,12,13,14,15]]
There is definitely something wrong with my code and I'm not able to figure it out, any help is appreciated!
I think list comprehensions might make your code more succinct:
alpha = [i[:-1] for i in lst]
beta = [i[1:] for i in lst]
>>> alpha
[[1, 2, 3, 4, 5, 6, 7, 8], [11, 12, 13, 14]]
>>> beta
[[2, 3, 4, 5, 6, 7, 8, 9], [12, 13, 14, 15]]
foo, bar= [], []
for i in range(len(lst)):
for j in range(len(lst[i])-1):
foo.append(lst[i][j])
bar.append(lst[i][j+1])
alpha[i] = foo
beta[i] = bar
foo, bar = [], []

How to use iter and next in a function to return a list of peaks?

Problem:
I need to define a peaks function which passed to an iterable as parameter, by computing this iterable, the function should return a list of peaks. The only data structure that I can create is the list is returning; use no intermediate data structures to help the computation: e.g., I cannot create a list with all the values in the iterable. Note that I also cannot assume the argument is indexable, nor can I compute len for it: it is just iterable.
For example:
peaks([0,1,-1,3,8,4,3,5,4,3,8]) returns [1, 8, 5].
This result means the values 1, 8, and 5 are strictly bigger than the values immediately preceding and following them.
def peaks(iterable):
l=[]
i=iter(iterable)
v=next(i)
try:
while True:
if v>next(iter(iterable)):
l.append(v)
v=next(i)
except StopIteration:
pass
return l
Calling these should give me:
peaks([0,1,-1,3,8,4,3,5,4,3,8]) --> [1,8,5]
peaks([5,2,4,9,6,1,3,8,0,7]) -->[9,8]
But I am getting:
peaks([0,1,-1,3,8,4,3,5,4,3,8]) --> [1, 3, 8, 4, 3, 5, 4, 3, 8]
peaks([5,2,4,9,6,1,3,8,0,7]) --> [9, 6, 8, 7]
Please help me with this problem, I have spent so much time on it and am making no progress on it. And, I don't know how to write the if statement to check the values immediately preceding and following. Any helps would be great! Actual codes would be really appreciated since my English is bad.
def peaks(L):
answer = []
a,b,c = itertools.tee(L, 3)
next(b)
next(c)
next(c)
for first, second, third in zip(a,b,c):
if first <= second >= third:
answer.append(second)
return answer
In [61]: peaks([0,1,-1,3,8,4,3,5,4,3,8])
Out[61]: [1, 8, 5]
In [62]: peaks([5,2,4,9,6,1,3,8,0,7])
Out[62]: [9, 8]

List Comprehensions to replace element

for i in range(1, len(A)):
A[i] = A[i-1] + A[i]
You can't do that with a list comprehension as they don't allow assignments.
You can use a simple generator function:
def func(lis):
yield lis[0]
for i,x in enumerate(lis[1:],1):
lis[i] = lis[i-1] + x
yield lis[i]
>>> A = [1, 2, 3, 4, 5, 6, 7]
>>> list(func(A))
[1, 3, 6, 10, 15, 21, 28]
though less efficient, this does give the desired output. But I think I'm getting closer to O(n**2) on this one.
A = [sum(A[:i+1]) for i, _ in enumerate(A)]
afaik this can't be done with a list comprehension the way you want to. I would suggest using the for loop version you've provided. Even if it was possible with a list comprehension, there's no point when you can just modify the list in place.
Use B (another temporary variable).
This should do the trick.
def func(L):
it = iter(L)
v = it.next()
yield v
for x in it:
v += x
yield v
A = [1, 2, 3, 4, 5, 6, 7]
print list(func(A))
This creates an iterator that returns one value at the time. To get the full new list at once you need to use a list() call around the function call, like:
list(func(A))
This generator function should work on any iterable (also those that don't support getting value based on index, like L[0])
I don't think there's an efficient way to do this with a comprehension list.
A one-line solution use reduce:
>>> the_list = [1,2,3,4,5]
>>> reduce(lambda result, x: result+[result[-1] + x] ,the_list, [0])[1:]
[1, 3, 6, 10, 15]

Resources