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]
Related
Let's say we try to remove all elements of a list with the following code:
a = [1, 2, 3, 4]
for i in a:
a.remove(i)
Of course this is not "permitted" and will fail. On the other hand we could do:
a = [1, 2, 3, 4]
for i in list(a):
a.remove(i)
and this would work. So, I have two questions:
Why is the second method working?
Is the second method acceptable and should be used?
Thank you in advance!
The second code works because a new list is created with list(a). You then iterate through the new list and remove items from the original list.
The second code is acceptable in the sense that you are not iterating through the same list you are modifying.
You can get some sense about the process by adding prints to your code:
a = [1, 2, 3, 4]
for i in a:
print('Deleting: {}'.format(i))
a.remove(i)
print(a)
In your first sample, the loop starts with deleting 1, and then moving on to the second element in the list. However, since you deleted 1, the next element in terms of the loop is the second element in the list, which is now 3 (2 is the first now) - so the loop deletes 3. And then it wants to delete the third element, but no such element any more.
In the second sample, list(a) actually creates a new instance of a list (with the same elements as a), so what you actually do is to iterate over the elements of this new list, and delete the corresponding elements in original a. Since both lists hold the same elements - all the elements in a are being deleted.
About what your second question about what is accepted - it all depends what you'd like to achieve.
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
How can I change this code so the I replace the elements for a different if condition?
In the code below I change replace the 3 with 5.
But what if the wanted to replace the entry to 5 if any sub-list entry is larger than 30?
Example: If L[i][j] > 30 I get [[3,3],[5,5],[5,1], [5,8]]
L = [[3,3],[444,1111],[45,1], [90,8]]
for i, x in enumerate(L):
for j, a in enumerate(x):
if 3 in a:
L[i][j] = a.replace(3, 5)
****** Disclaimer *******:
The example was taken from this enclosed question for the sake of convenience. My aim in this question is to understand how to use the indexes so it fits a different type of if condition.
Replace elements in a list of lists python
Quick and dirty answer:
L = [[3,3],[444,1111],[45,1], [90,8]]
L = [[5 if value < 30 else 5 for value in list ] for list in L]
print(L)
out:
[[3, 3], [5, 5], [5, 1], [5, 8]]
Better way using a function
If you want to replace the values then it's better to create a simple function as shown below. You seem to have copied the code from somewhere else, so I will try my best to explain to you what is going on below.
Our original list is shown below:
L = [[3,3],[444,1111],[45,1], [90,8]]
The function below is defined to accept 3 values, the first value is the list to be manipulated, the second is the condition, as in replace if number is > condition. The third value is the new value to replace the values that satisfy the condition.
def replace_greater_than(List,condition = 30,new_value = 5):
L = [[value if value < condition else new_value for value in list ] for list in List]
Here we are using a concept know as nested list comprehension. Start by reading it from right to left. We are looping through every list in List (notice the case), please note that "list" in the code can be replaced with any variable name that you want, but i just called it "list" to improve readability.
So now we are looping through our initial list and we are retrieving the inner lists with each iteration.Then we perform list comprehension again on the inner lists that we just retrieved, so now we are looping on the values in the list themselves.
Finally in the inner list comprehension we set a condition, which is passed on by the user, if the value is less than the set condition or threshold then return the same value else return the specified new value.
How to use the function:
Example: here we place the condition as 30 and the value as 5
replace_greater_than(List = L, condition = 30, value = 5)
out:
[[3, 3], [5, 5], [5, 1], [5, 8]]
Example: We can also call the function without passing any values to the parameters because we already set the default values of our function earlier; condition = 30, new_value = 5.
replace_greater_than(List = L)
out:
[[3, 3], [5, 5], [5, 1], [5, 8]]
Example: Finally, we can also pass custom values to our function. The function below will replace all the values greater than 100 with the new_value of 23
replace_greater_than(List = L, condition = 100, new_value = 23)
out:
[[3, 3], [23, 23], [45, 1], [90, 8]]
Here are some sources to help you get started with learning python:
Great free Course covering all the basics by Microsoft
List Comprehension
Functions in python
Some channels on Youtube that I recommend:
Real Python
Corey Schafer
DataCamp
Basically you have a loop through all elements, then you have condition and action when condition is met.
for i, x in enumerate(L): #loop
for j, a in enumerate(x): #loop
if 3 in a: #condition
L[i][j] = a.replace(3, 5) #action
You want to change condition to if L[i][j] > 30 (already in your question), and you want to change action to replace or reassign entry, which is straightforward L[i][j] = 5.
In above code a is L[i][j], so you can write condition a bit differently.
I will let you modify code yourself and test. You don't need to use list comprehension at this point.
Thea easy way to do it is to iterate over each item in each list and change items via an 'if' statement.
Create a statement which will return either the item it is given, or 3 if the number is equal to 5.
replace = lambda i: 3 if i==5 else i
Next, iterate through the nested lists. This can be written out in two ways, via list comprehension or as nested for statements.
l = [[replace(l2)for l2 in l1] for l1 in l]
for l1 in l:
for l2 in l1:
l2 = replace(l2)
When we run the code we get:
print (l)
>>> [[3, 3], [444, 1111], [45, 1], [90, 8]]
To make the code change any items <30 we can change the if statement we made at the start.
replace = lambda i: 3 if i<30 else i
Printing the new code gives:
print (l)
>>> [[3, 3], [444, 1111], [45, 3], [90, 3]]
Note, using x.replace() works for strings not integers, you can play with int() and str() if you need to use .replace().
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm learning Python, coming from a C#/Java background, and was playing around with list behavior. I've read some of the documentation, but I don't understand how, or why, slices with indices larger than length-1 make is possible to append items.
ls = ["a", "b", "c", "d"]
n = len(ls) # n = 4
letter = ls[4] # index out of range (list indexes are 0 - 3)
# the following all do the same thing
ls += ["echo"]
ls.append("foxtrot")
ls[len(ls):] = ["golf"] # equivalent to ls[len(ls): len(ls)]
print(ls)
Although it seems odd to me, I understand how slices can modify the list they operate on. What I don't get is why list[len(list)] results in the expected out of bounds error, but list[len(list):] doesn't. I understand that slices are fundamentally different than indexes, just not what happens internally when a slice begins with an index outside of the list values.
Why can I return a slice from a list that starts with a non-existent element (len(list))? And why does this allow me to expand the list?
Also, of the first three above methods for appending an item, which one is preferred from a convention or performance perspective? Are there performance disadvantages to any of them?
While a.append(x) and a[len(a):] = [x] do the same thing, the first is clearer and should almost always be used. Slice replacement may have a place, but you shouldn't look for reasons to do it. If the situation warrants it, it should be obvious when to do it.
Start with the fact that indexing returns an element of the list, and slicing returns a "sublist" of a list. While an element at a particular either exists or does not exist, it is mathematically convenient to think of every list have the empty list as a "sublist" between any two adjacent positions. Consider
>>> a = [1, 2, 3]
>>> a[2:2]
[]
Now consider the following sequence. At each step, we increment the starting index of the slice
>>> a[0:] # A copy of the list
[1, 2, 3]
>>> a[1:]
[2, 3]
>>> a[2:]
[3]
Each time we do so, we remove one more element from the beginning of the list for the resulting slice. So what should happen when the starting index finally equals the final index (which is implicitly the length of the list)?
>>> a[3:]
[]
You get the empty list, which is consistent with the following identities
a + [] = a
a[:k] + a[k:] = a
(The first is a special case of the second where k >= len(a).
Now, once we see why it makes sense for the slice to exist, what does it mean to replace that slice with another list? In other words, what list do we get if we replace the empty list at the end of [1, 2, 3] with the list [4]? You get [1, 2, 3, 4], which is consistent with
[1, 2, 3] + [4] = [1, 2, 3, 4]
Consider another sequence, this time involving slice replacements.
>>> a = [1,2,3]; a[0:] = ['a']; a
['a']
>>> a = [1,2,3]; a[1:] = ['a']; a
[1, 'a']
>>> a = [1,2,3]; a[2:] = ['a']; a
[1, 2, 'a']
>>> a = [1,2,3]; a[3:] = ['a']; a
[1, 2, 3, 'a']
Another way of looking at slice replacement is to treat it as appending a list to a sublist of the original. When that sublist is the list itself, then it is equal to appending to the original.
Why can I return a slice from a list that starts with a non-existent element?
I can't tell for sure, but my guess would be that it's because a reasonable value can be returned (there are no elements this refers to, so return an empty list).
And why does this allow me to expand the list?
Your slice selects the last 0 elements of the list. You then replace those elements with the elements of another list.
Also, of the first three above methods for appending an item, which one is preferred
Use .append() if you're adding a single element, and .extend() if you have elements inside another list.
I'm not sure about performance, but my guess would be that those are pretty well optimized.
I'd make a function in python, that given a list returns a list of list, in which every element is the list given decreased by one.
Input: list_decreaser([0,3,4,5,6,7,8)
Output: [[0,3,4,5,6,7],[0,3,4,5,6],[0,3,4,5],[0,3,4],[0,3],[0]]
My attempt:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list)
return listresult
The code appends the same list multiple times. It should append copy of the list.
And use del list[..] instead of list.remove(list[..]) to delete an item at specific index.
def list_decreaser(xs):
listresult = []
for i in range(len(xs)-1, 0, -1): # <--- interate backward
del xs[i]
listresult.append(xs[:]) # <----
return listresult
print(list_decreaser([0,3,4,5,6,7,8]))
Or using list comprehension:
>>> xs = [0,3,4,5,6,7,8]
>>> [xs[:i] for i in range(len(xs)-1, 0, -1)]
[[0, 3, 4, 5, 6, 7], [0, 3, 4, 5, 6], [0, 3, 4, 5], [0, 3, 4], [0, 3], [0]]
BTW, don't use list as a variable name. It shadows builtin list function.
The problem is that you're appending the same list over and over again. You keep mutating the list in-place, but you're never creating a new list. So you end up with a list of N references to the same empty list.
This is the same problem discussed in two FAQ questions. I think How do I create a multidimensional list explains it best.
Anyway, what you need to do is append a new list each time through the loop. There are two ways to do that.
First, you can append a copy of the current list, instead of the list itself:
def list_decreaser(list):
listresult = []
for x in range(len(list)-1):
list.remove(list[x])
listresult.append(list[:]) # this is the only change
return listresult
This solves your problem, but it leaves a few new problems:
First, list.remove(list[x]) is a very bad idea. If you give it, say, [0, 1, 2, 0], what happens when you try to remove that second 0? You're calling list.remove(0), and there's no way the list can know you wanted the second 0 rather than the first! The right thing to do is call del list[x] or list.pop(x).
But once you fix that, you're removing the elements from the wrong side. x is 0, then 1, then 2, and so on. You remove element 0, then element 1 (which is the original element 2), then element 2 (which is the original element 4), and eventually get an IndexError. Even if you fixed the "skipping an index" issue (which is also explained in the FAQ somewhere), you'd still be removing the first elements rather than the last ones. You can fix that by turning the range around. However, there's an even easier way: Just remove the last element each time, instead of trying to figure out which x is the right thing, which you can do by specifying -1, or just calling pop with no argument. And then you can use a much simpler loop, too:
def list_decreaser(list):
listresult = []
while list:
list.pop()
listresult.append(list[:])
return listresult
Of course this appends the last, empty list, which you apparently didn't want. You can fix that by doing while len(list) >= 1, or putting an if list: listresult.append(list[:]), or in various other ways.
Alternatively, you can make new truncated lists instead of truncating and copying the same list over and over:
def list_decreaser(list):
listresult = []
while len(list):
list = list[:-1]
listresult.append(list)
return listresult
Note that in this second version, rather than changing the value stored in list, we're creating a new list and storing that new list in list.
use this
def list_decreaser(list1):
listresult = []
for i in list1:
list1 = list[:-1]
listresult.append(list1)
return listresult