The second to the last line of code is confusing to me - python

I am quite new to python and programming in general so I am still trying to understand the details in practice. This is a problem I found online so I can practice nested loops more. If my question is missing anything or you do not understand my question, please let me know. I would like to get better at asking good questions as well.
list =[[1, 2], [3,4]]
m = 1
print(list)
for i in range(0, 2):
m *= 10
for j in range(0, 2):
list[i][j] *= m # This part right here.
print(list)
This is what prints on the terminal:
[[1, 2], [3, 4]]
[[10, 20], [300, 400]]
I was trying to go through this block of code step by step to make sure I understand it but this part is stumping me. I understand that the whole function of this nested for loop is to multiply the items in the 1st list with 10 and the 2nd list with 100. I also know what the *= m part is, the part that's confusing to me is the code right before that on the same line.
So far I tried to just copy this specific part in google and see if anything came up. I could not find anything that would make sense. I also tried to just run this whole line and see what printed (list[i][j] *= m)(I changed the variable to numbers obviously). That only came up with a type error... There are no type variables left in list[2]. I was trying to isolate it to see what just this part does but it apparently doesn't work like that. I guess i need to think outside the box a little more maybe.

If we take i to be 1 and j to be 1, list[i][j] would be 4. list[i] = [3,4] so what you're doing is finding index 1 of the list [3, 4]

list is a list containing nested lists.
list[0] is the list [1, 2]. list[1] is the list [3, 4].
When you use two indexes like list[i][j], it first gets the nested list list[i], then accesses the [j] element of that. So when i == 0 and j == 1, this accesses the list element containing 2.
*= m then multiplies that list element. So when i == 0 and m == 10, it multiplies the values in the first sublist by 10. Then when i == 1 and m == 100 it multiplies the values in the second sublist by 100.

list = [[1,2], [3,4]] is an array of arrays.
To get a single element, you have to subscript list twice, which is exactly what list[i][j] is.
list[0] returns [1,2], the 0-th element of the list, which is a sublist
list[0][0] returns 1, the 0-th element of the 0-th sublist
list[0][1] returns 2, the 1st element of the 0-th sublist
.
list[1] returns [3,4], the 1st element of the list, which is a sublist
list[1][0] returns 3, the 0-th element of the 1st sublist
list[1][1] returns 4, the 1st element of the 1st sublist
.
Also, the reason why list[2] doesn't return anything is because list only has two elements, which have the index 0 and 1, so trying to get the element at index 2 will not work

Related

How to move items with even index to the end of the list

lst = ['apple', 'orange', 'kiwi', 'ananas',
'tea', 'coffee', 'milk', 'love', 'peace']
for i in range(len(lst)):
if (i + 1) % 2 == 0:
lst.append(lst[i])
lst.pop(i)
Basically here I want the items with even index to be added at the end of this list
it works for the second item but still doesn’t for the rest of them
You can use Python's wider-step ranges:
lst = lst[1::2] + lst[0::2]
The right hand side of the plus says "grab every 2nd element starting from the first" and the left hand side says "grab every 2nd element starting from the second". This basically reconstructs the list with the odd elements first and the even elements last.
It even avoids expensive pops that make your reference algorithm O(n^2)
The problem with your approach is that the elements shift after you moved the first element. So when you are at the next element with "even" index, the element that's there was originally at an odd index. Thus, after you shift the first element, you can just directly continue with the element at the next index, which previously was two indices away, then again the next one, and so on, for half the indices in the list.
Here's an example, using a list of numbers so it's easier to see what happens. If you want odd indices instead, use range(1, len(lst)//2+1).
lst = list(range(10))
for i in range(len(lst)//2):
lst.append(lst.pop(i))
# [1, 3, 5, 7, 9, 0, 2, 4, 6, 8]
However, even if this works, modifying a list while iterating it is generally a very bad idea leading to many headaches. Also, that repeated pop(i) makes the whole operation O(n²).
Instead, it would be much faster and saner to just combine two slices of the list:
lst = list(range(10))
lst = lst[1::2] + lst[0::2]
# [1, 3, 5, 7, 9, 0, 2, 4, 6, 8]
(If you need to change the list "in-place", e.g. because of other references pointing to that list, you can replace the content of the list using an assignment to a slice: lst[:] = .... This would still not be "in-place" in the sense of not using additional memory. But if the list is so big that this is a problem, then the O(n²) running time will probably be a bigger problem anyway.)
A simple way would be to build a new list by using comprehensions:
lst2 = [v for i, v in enumerate(lst) if i%2 == 0] + \
[v for i, v in enumerate(lst) if i%2 != 0]
But it is possible to change the list in place. The rule is to start from the end of the list in order not to change oddness of indices when an element is removed
last = len(lst) - 1 # when an element is popped, the list loses one element
for i in range(len(lst), 0, -1):
if (i % 2) == 0:
val = lst.pop(i - 1) # remove element with even index
lst.insert(last, val) # insert it before last inserted
last -= 1

removing elements in list(Python)

I have tried execute this, but it dosn't work properly. My goal was to remove all numbers divided by 2. Could someone advise what is wrong. I really do not understand why '4', '8' are still there.
list = [2,4,9,0,4,6,8,3,43,44]
for e in list:
if e%2==0:
list.remove(e)
print(list)
You can use a list comprehension to generate a new list with only elements you want to keep.
newList = [x for x in oldList if not isEven(x)]
where the function isEven does something like:
def isEven(target):
return target % 2 == 0
By the way, your question is a duplicate of the following How to remove items from a list while iterating?
If you want to keep the list instead of creating a new one (the answer by Thomas Milox is a good one otherwise), you should iterate backward through the list by index. When you remove an element from a list while iterating forwards through the list you may jump over some elements, not processing them. Going backward ensures that no list element removals move any elements that you may still want to process.
Here is an example of how this may look for your code:
list = [2, 4, 9, 0, 4, 6, 8, 3, 43, 44]
for i in range(len(list) - 1, -1, -1): # start at the last element, go until the first one (index 0 - the last value in the range method will not be reached), go backwards
if list[i] % 2 == 0:
del list[i]
You can read a bit more about removing an element by index instead of by value here.
This is required since you would otherwise mutate the list on the wrong position for duplicate values. It may also be a bit faster, since removeneeds to iterate through the list, searching for the element to remove while del list[i] may look up the element that needs to be removed by index.
Iterating backward through a list is also covered here.
You can try to use list.pop() with the position of the element you want to remove.
The '2' and '4' are still there because they are skipped when you remove the number before them (When you remove the '2', the '4' is moved to the previous position)
Try this:
l = [2, 3, 4, 5, 9, 10,30,45]
new=[el for el in l if el % 2]
print(new)
Actually, when you remove an element from the list, the indexing gets changed. So, you can do this list comprehension.
Also you can use:
l = [2, 3, 4, 5, 9, 10,30,45]
new=[filter(lambda x: x % 2, l)]
print(new)

del statement not working for list

I have a long list where each element is a list of length 2. The first element of each is a list is a string and the second element of each list is an integer corresponding to the string.
I want to loop through the long "parent" list and delete any "child" lists where the integer is less than three. This is my code.
for i in range(len(fontsizenum) / 2):
if int(fontsizenum[i][1]) < 3:
del fontsizenum[i]
However, it is not working as when I print the list afterwards, it still contains values with numbers less than three.
Say this is the list that I am altering.
fontsizenum = [[cereal, 1], [dog, 4], [cat, 2], [water, 5]]
The expected output is [[dog, 4], [water, 5]].
However, the actual output for me right now is still the original, unchanged list.
The resulting list still contains unexpected values because you are modifying the list while iterating over it. In almost all cases, you should avoid modifying an iterable while iterating over it.
If you change your code to the following, the resulting list should be what you expect.
new_lst = []
for idx, value in enumerate(lst):
if value[1] >= 3:
new_lst.append(value)
As noted in the comments by #AntonvBR, the above snippet can be simplified to the following list comprehension
[i for i in lst if i[1] > 3]
Example
If lst is set to
[
['forbidden', 1],
['hath', 1],
['causes', 2],
['whose', 3],
]
then the resulting list will be
[['whose', 3]]
Explanation
My code snippet creates a new list that contains elements from the old list. Notably, this allows us to avoid modifying the old list.
I changed the condition in the if-statement to check whether to include, rather than to exclude, an element. Finally, if that check is satisfied, then I append the value to the new list.
You want to use a simple list comprehension for this:
expectedoutput = [i for i in fontsizenum if i[1] >= 3]

Python text for loop list index out of range [duplicate]

This question already has answers here:
How can I iterate over overlapping (current, next) pairs of values from a list?
(12 answers)
Why do I get an IndexError (or TypeError, or just wrong results) from "ar[i]" inside "for i in ar"?
(4 answers)
Closed 6 months ago.
Given the following list
a = [0, 1, 2, 3]
I'd like to create a new list b, which consists of elements for which the current and next value of a are summed. It will contain 1 less element than a.
Like this:
b = [1, 3, 5]
(from 0+1, 1+2, and 2+3)
Here's what I've tried:
b = []
for i in a:
b.append(a[i + 1] + a[i])
The trouble is I keep getting this error:
IndexError: list index out of range
I'm pretty sure it occurs because by the time I get the the last element of a (3), I can't add it to anything because doing so goes outside of the value of it (there is no value after 3 to add). So I need to tell the code to stop at 2 while still referring to 3 for the calculation.
In your for loop, you're iterating through the elements of a list a. But in the body of the loop, you're using those items to index that list, when you actually want indexes.
Imagine if the list a would contain 5 items, a number 100 would be among them and the for loop would reach it. You will essentially attempt to retrieve the 100th element of the list a, which obviously is not there. This will give you an IndexError.
We can fix this issue by iterating over a range of indexes instead:
for i in range(len(a))
and access the a's items like that: a[i]. This won't give any errors.
In the loop's body, you're indexing not only a[i], but also a[i+1]. This is also a place for a potential error. If your list contains 5 items and you're iterating over it like I've shown in the point 1, you'll get an IndexError. Why? Because range(5) is essentially 0 1 2 3 4, so when the loop reaches 4, you will attempt to get the a[5] item. Since indexing in Python starts with 0 and your list contains 5 items, the last item would have an index 4, so getting the a[5] would mean getting the sixth element which does not exist.
To fix that, you should subtract 1 from len(a) in order to get a range sequence 0 1 2 3. Since you're using an index i+1, you'll still get the last element, but this way you will avoid the error.
There are many different ways to accomplish what you're trying to do here. Some of them are quite elegant and more "pythonic", like list comprehensions:
b = [a[i] + a[i+1] for i in range(len(a) - 1)]
This does the job in only one line.
Reduce the range of the for loop to range(len(a) - 1):
a = [0, 1, 2, 3]
b = []
for i in range(len(a) - 1):
b.append(a[i] + a[i+1])
This can also be written as a list comprehension:
b = [a[i] + a[i+1] for i in range(len(a) - 1)]
When you call for i in a:, you are getting the actual elements, not the indexes. When we reach the last element, that is 3, b.append(a[i+1]-a[i]) looks for a[4], doesn't find one and then fails. Instead, try iterating over the indexes while stopping just short of the last one, like
for i in range(0, len(a)-1): Do something
Your current code won't work yet for the do something part though ;)
You are accessing the list elements and then using them to attempt to index your list. This is not a good idea. You already have an answer showing how you could use indexing to get your sum list, but another option would be to zip the list with a slice of itself such that you can sum the pairs.
b = [i + j for i, j in zip(a, a[1:])]

having trouble understanding this code

I just started learning recursion and I have an assignment to write a program that tells the nesting depth of a list. Well, I browsed around and found working code to do this, but I'm still having trouble understanding how it works. Here's the code:
def depth(L) :
nesting = []
for c in L:
if type(c) == type(nesting) :
nesting.append(depth(c))
if len(nesting) > 0:
return 1 + max(nesting)
return 1
So naturally, I start to get confused at the line with the append that calls recursion. Does anyone have a simple way of explaining what's going on here? I'm not sure what is actually being appended, and going through it with test cases in my head isn't helping. Thanks!
edit: sorry if the formatting is poor, I typed this from my phone
Let me show it to you the easy way, change the code like this:
(### are the new lines I added to your code so you can watch what is happening there)
def depth(L) :
nesting = []
for c in L:
if type(c) == type(nesting) :
print 'nesting before append', nesting ###
nesting.append(depth(c))
print 'nesting after append', nesting ###
if len(nesting) > 0:
return 1 + max(nesting)
return 1
Now lets make a list with the depth of three:
l=[[1,2,3],[1,2,[4]],'asdfg']
You can see our list has 3 element. one of them is a list, the other is a list which has another list in itself and the last one is a string. You can clearly see the depth of this list is 3 (i.e there are 2 lists nested together in the second element of the main list)
Lets run this code:
>>> depth(l)
nesting before append []
nesting after append [1]
nesting before append [1]
nesting before append []
nesting after append [1]
nesting after append [1, 2]
3
Piece of cake! this function appends 1 to the nesting. then if the element has also another list it appends 1 + maximum number in nesting which is the number of time function has been called itself. and if the element is a string, it skips it.
At the end, it returns the maximum number in the nesting which is the maximum number of times recursion happened, which is the number of time there is a list inside list in the main list, aka depth. In our case recursion happened twice for the second element + 1=3 as we expected.
If you still have problem getting it, try to add more print statements or other variables to the function and watch them carefully and eventually you'll get it.
So what this seems to be is a function that takes a list and calculates, as you put it, the nesting depth of it. nesting is a list, so what if type(c) == type(nesting) is saying is: if the item in list L is a list, run the function again and append it and when it runs the function again, it will do the same test until there are no more nested lists in list L and then return 1 + the max amount of nested lists because every list has a depth of 1.
Please tell me if any of this is unclear
Let's start with a couple of examples.
First, let's consider a list with only one level of depth. For Example, [1, 2, 3].
In the above list, the code starts with a call to depth() with L = [1, 2, 3]. It makes an empty list nesting. Iterates over all the elements of L i.e 1, 2, 3 and does not find a single element which passes the test type(c) == type(nesting). The check that len(nesting) > 0 fails and the code returns a 1, which is the depth of the list.
Next, let's take an example with a depth of 2, i.e [[1, 2], 3]. The function depth() is called with L = [[1, 2], 3] and an empty list nesting is created. The loop iterates over the 2 elements of L i.e [1, 2] , 3 and since type([1, 2]) == type(nesting), nesting.append(depth(c)) is called. Similar to the previous example, depth(c) i.e depth([1, 2]) returns a 1 and nesting now becomes [1]. After the execution of the loop, the code evaluates the test len(nesting) > 0 which results in True and 1 + max(nesting) which is 1 + 1 = 2 is returned.
Similarly, the code follows for the depth 3 and so on.
Hope this was helpful.
This algorithm visits the nested lists and adds one for each level of recursion. The call chain is like this:
depth([1, 2, [3, [4, 5], 6], 7]) =
1 + depth([3, [4, 5], 6]) = 3
1 + depth([4, 5]) = 2
1
Since depth([4,5]) never enters the if type(c) == type(nesting) condition because no element is a list, it returns 1 from the outer return, which is the base case.
In the case where, for a given depth, you have more than one nested list, e.g. [1, [2, 3], [4, [5, 6]], both the max depth of [2,3]and [4, [5, 6]] are appended on a depth call, of which the max is returned by the inside return.

Categories