Python: Iterate through a list by value, not by index - python

How can I iterate through by something other than the index?
I have
L = [[1,2,3],[5,3,6],[5,4,14],[23,5,2],....,[11,13,6]]
notice how the middle element is increasing always by 1. the outer elements are pretty much random.
I want to be able to say something like this:
for i in L[2:4]:
which would iterate through the elements: [1,2,3],[5,3,6],[5,4,14], as opposed to looking at 2:4 as indexes.
so obviously the syntax of my for loop is incorrect. How can I do this?

[item for item in L if 2 <= item[1] <= 4]
is one way of doing it
def slice_special(a_list,slice_idx,minimum_value,maximum_value):
return [item for item in a_list if minimum_value <= item[slice_idx] <= maximum_value]
print slice_special(L,1,2,4)
or something more sophisticated like a dedicated data structure
class MyDataStructure:
def __init__(self,a_list):
self.my_data = a_list
def __getitem__(self,arg):
if isinstance(arg,slice):
return [item for item in self.my_data if arg.start <= item[arg.step] <= arg.stop]
raise Exception("%s is unscriptable"%self)
print MyDataStructure([[1,2,3],[5,3,6],[5,4,14],[23,5,2]])[2:4:1]

If you know the size of the elements of the list, you can unpack the sublists into the iteration variables:
>>> L = [[1,2,3],[5,3,6],[5,4,14],[23,5,2]]
>>> for a, b, c in L:
... print(a,b,c)
...
1 2 3
5 3 6
5 4 14
23 5 2
>>>
If you don't want to use a certain index, you can assign it to _, which is the standard way of notating unused variables.

Related

Nested loop won't update [duplicate]

This question already has answers here:
Multiply every element in a nested list by a constant
(3 answers)
Closed 6 months ago.
i'm trying to multiply all items in a list by -1, but the list won't update.
the original list goes like:
[[[[-0.04344771 -0.07890235 -0.08350667 ... -0.05058916 -0.02590174
0.01833121]
[-0.03187432 -0.06377442 -0.07528157 ... -0.0153968 0.00928687
0.05289121]
[-0.0030058 -0.02783908 -0.04554714 ... 0.01647086 0.02895362
0.05640405]
...
[ 0.00193604 0.03679746 0.06137059 ... 0.04944649 0.06763638
0.08346977]
[ 0.01469174 0.04900428 0.0724168 ... 0.09451687 0.08840736
0.0754609 ]
[ 0.0307981 0.05116013 0.06343959 ... 0.08668113 0.05572119
0.01737073]]]]
i try to update it with:
for value in data:
for a in value:
for b in a:
for item in b:
item = item * -1
but when i try to print the list again, nothing has changed.
When i try to print it however, with:
for value in data:
for a in value:
for b in a:
print(b * -1)
it does print the list correctly:
[-0.0307981 -0.05116013 -0.06343959 ... -0.08668113 -0.05572119
-0.01737073]
how do i fix this?
You assign to variable, not to list content. You can assign to i-th element of list instead:
for value in data:
for a in value:
for b in a:
for i, item in enumerate(b):
b[i] = -item
Or use augmented assignment to list item:
for value in data:
for a in value:
for b in a:
for i in range(len(b)):
b[i] *= -1
This happens because the line
for item in b
copies the value of the item in the innermost array, rather than directly referencing it. For an array that is nested specifically three deep, you'd need to do something like this:
for i in range(len(x)):
for j in range(len(x[i])):
for k in range(len(x[i][j])):
for l in range(len(x[i][j][k])):
x[i][j][k][l] *= -1
This changes the actual values rather than creating a copy, since the line x[i][j][k][l] *= -1 changes the value stored in the array as opposed to a copy

How to handle operating on items in a list without perfectly even len()?

I'm trying to operate on every 5 items in a list, but can't figure out how to handle the remaining items if they don't divide evenly into 5. Right now I'm using modulo, but I can't shake the feeling it's not quite the right answer. Here's an example...
list = ["ValA","ValB","ValC","ValD","ValE","ValF","ValG","ValH","ValI","ValJ","ValK","ValL","ValM","ValN",]
newlist = []
i = 0
for o in list:
i += 1
newlist.append(o)
if i % 5 == 0:
for obj in newlist:
function_for(obj)
newlist.clear()
This code will execute function_for() twice, but not a third time to handle the remaining 4 values. If I add an 'else' statement it runs on every execution.
What's the correct way to handle a situation like this?
This way is pretty easy, if you don't mind modifying the list:
mylist = ["ValA","ValB","ValC","ValD","ValE","ValF","ValG","ValH","ValI","ValJ","ValK","ValL","ValM","ValN",]
while mylist:
function_for( mylist[:5] )
mylist = mylist[5:]
You can also check if the index is equal to the length of the list. (Additionally, it is more idiomatic to use enumerate instead of a counter variable here.)
lst = ["ValA","ValB","ValC","ValD","ValE","ValF","ValG","ValH","ValI","ValJ","ValK","ValL","ValM","ValN",]
newlist = []
for i, o in enumerate(lst, 1):
newlist.append(o)
if i % 5 == 0 or i == len(lst):
print(newlist)
newlist.clear()

Multiply odd indices by 2?

So I'm writing a function that is going to multiply each number at an odd index in a list by 2. I'm stuck though, as I really don't know how to approach it.
This is my code.
def produkt(pnr):
for i in pnr:
if i % 2 != 0:
i = i * 2
return pnr
If I, for example, type produkt([1,2,3]) I get [1,2,3] back but I would want it to be [2,2,6].
note that modifying i in your example does not change the value from the input list (integers are immutable). And you're also mixing up the values with their position.
Also, since indices start at 0 in python, you got it the wrong way.
In those cases, a simple list comprehension with a ternary expression will do, using enumerate to be able to get hold of the indices (making it start at 1 to match your case, you can adjust at will):
[p*2 if i%2 else p for i,p in enumerate(pnr,1)]
(note if i%2 is shorter that if i%2 != 0)
using list comprehensions:
multiply odd numbers by 2:
[x*2 if x%2 else x for x in pnr]
After clarification of question wording:
multiply numbers at odd indices by 2:
[x*2 if i%2 else x for i,x in enumerate(pnr)]
Consider using list comprehensions:
def produkt(pnr):
return [k * 2 if k % 2 else k for k in pnr]
Doing i = i * 2 you just override a local variable.
UPDATE (question was changed):
def produkt(pnr):
return [k * 2 if i % 2 else k for i, k in enumerate(pnr, 1)]
You can get the indices using enumerate, however that starts by default with index 0 (not 1) but it accepts a start argument to override that default.
The problem with your approach is that you don't change the actual list contents, you just assign a different value to the name i (which represented a list element until you assigned a different value to it with i = i*2). If you want it to work in-place you would need to modify the list itself: e.g. pnr[idx] *= 2 or pnr[idx] = pnr[idx] * 2.
However, it's generally easier to just create a new list instead of modifying an existing one.
For example:
def produkt(pnr):
newpnr = [] # create a new list
for idx, value in enumerate(pnr, 1):
# If you're testing for not-zero you can omit the "!=0" because every
# non-zero number is "truthy".
if idx % 2:
newpnr.append(value * 2) # append to the new list
else:
newpnr.append(value) # append to the new list
return newpnr # return the new list
>>> produkt([1,2,3])
[2, 2, 6]
Or even better: use a generator function instead of using all these appends:
def produkt(pnr):
for idx, value in enumerate(pnr, 1):
if idx % 2:
yield value * 2
else:
yield value
>>> list(produkt([1,2,3])) # generators should be consumed, for example by "list"
[2, 2, 6]
Of course you could also just use a list comprehension:
def produkt(pnr):
return [value * 2 if idx % 2 else value for idx, value in enumerate(pnr, 1)]
>>> produkt([1,2,3])
[2, 2, 6]
Try this:
def produkt(pnr):
return [ 2*x if i % 2 == 0 else x for i, x in enumerate(pnr)]
It will double every element in your list with an odd index.
>>> produkt([1,2,3])
[2, 2, 6]
Your code does not work, as i is no reference to the value inside the list, but just its value.
You have to store the new value in the list again.
def produkt(pnr):
for i in range(len(pnr)):
if pnr[i] % != 0:
pnr[i] *= 2
return pnr
or use this more convenient solution:
def produkt(pnr):
return [x * 2 if x % 2==0 else x for x in pnr]
Edit: As the question has been changed (completely) you should use this code:
def produkt(pnr):
return [x * 2 if ind % 2 else x for ind, x in enumerate(pnr)]
The first examples multiply each odd index by 2 and the former code multiplies the numbers at odd indices by 2.
Your problem is that i is a copy of the values in the pnr list, not the value in the list itself. So, you are not changing the list when doing i = i * 2.
The existing answers are already good and show the idiomatic way to achieve your goal. However, here is the minimum change to make it work as expected for learning purpose.
produkt(pnr):
new_pnr = list(pnr)
for ix in len(new_pnr):
if new_pnr[ix] % 2 != 0:
new_pnr[ix] *= 2
return new_pnr
Without new_pnr you'd be changing the list in place and then you wouldn't need to return it.

Summing integers in python list but excluding the number 13 and the number directly after it

How would I make a better function than what I have?
def sum13(nums):
total = 0
wheresNumAfter13 = nums.index(13) + 1
numAfter13Value = nums[wheresNumAfter13]
for num in nums:
if num != 13:
total += num
return total - numAfter13Value
print(sum13([1,2,3,4,5,13,4]))
You can do the sum of the relevant slice, similar to what you're doing, look for the index of 13, and then do the sum for the parts of the list before it and after the next element to 13.
You can also use the built-in sum function on the resulting list of combining both parts:
def sum13(nums):
wheres13 = nums.index(13)
return sum(nums[0:wheres13]+nums[wheres13+2:])
Note this solution, like yours, only works for the case where you only have a 13 to exclude in the list.
Also they will fail if the list doesn't contain 13.
Explanation, for an example list like: [1,2,3,4,5,13,4,10]:
Look for 13: wheres13 = nums.index(13)
Get the part of the list before the index of 13: nums[0:wheres13]. This produces a list like [1,2,3,4,5], i.e. from index 0 to index wheres13 (not including it).
Get the part of the list after 13: nums[wheres13+2:]. This produces a list like [10], i.e. from the index of the number after the number after 13 (which has index wheres13 plus 2), to the end of the list.
Join both lists: nums[0:wheres13]+nums[wheres13+2:]. This produces a list like [1,2,3,4,5,10]
Use python's builtin sum function on the list from the previous point
For completeness, heres a version modified to remove every 13 and next number:
def sum13(nums):
n=nums
while 13 in n:
wheres13 = n.index(13)
n = n[0:wheres13]+n[wheres13+2:]
return sum(n)
I'd be tempted to write this as a simple-ish generator comprehension. This works even if there are multiple instances of 13s:
>>> x = [1,1,1,13,99,1,1,13,99,1,13,13,13,1]
>>> sum(cur for cur,prev in zip(x, [None]+x) if 13 not in (cur,prev))
6
Read: sum up all of the numbers cur for which cur or the previous number aren't 13.
Maybe writing it as a generator function is more readable, though:
def exclude_13s(x):
for cur,prev in zip(x, [None]+x):
if 13 not in (cur,prev):
yield cur
So that
>>> sum(exclude_13s(x))
6
def sum13(nums):
if 13 in nums:
index13 = nums.index(13)
del nums[index13:index13 + 2]
return sum13(nums)
else:
return sum(nums)
print(sum13([1,2,3,4,5,13,4]))
You can try this way too:
def The13(l):
size = len(l)
bucket = [y for x in range(size) for y in range(x,x+2) if(l[x]==13)]
return sum([l[x] for x in range(size) if x not in bucket])
bucket contains all indexes of element having 13 and the number directly after it
You can do this without having to use list.index or worrying if 13 is present or not:
from itertools import takewhile, islice
def sum13(iterable):
it = iter(iterable)
return sum(takewhile(lambda L: L != 13, it)) + sum(islice(it, 1, None))
This will sum up until, but not including 13, then resume summing from the iterable ignoring the number immediately after 13.

Multiply every other element in a list

I have a list, let's say: list = [6,2,6,2,6,2,6], and I want it to create a new list with every other element multiplied by 2 and every other element multiplied by 1 (stays the same).
The result should be: [12,2,12,2,12,2,12].
def multi():
res = 0
for i in lst[0::2]:
return i * 2
print(multi)
Maybe something like this, but I don't know how to move on from this. How is my solution wrong?
You can use slice assignment and list comprehension:
l = oldlist[:]
l[::2] = [x*2 for x in l[::2]]
Your solution is wrong because:
The function doesn't take any arguments
res is declared as a number and not a list
Your loop has no way of knowing the index
You return on the first loop iteration
Not related to the function, but you didn't actually call multi
Here's your code, corrected:
def multi(lst):
res = list(lst) # Copy the list
# Iterate through the indexes instead of the elements
for i in range(len(res)):
if i % 2 == 0:
res[i] = res[i]*2
return res
print(multi([12,2,12,2,12,2,12]))
You can reconstruct the list with list comprehenstion and enumerate function, like this
>>> [item * 2 if index % 2 == 0 else item for index, item in enumerate(lst)]
[12, 2, 12, 2, 12, 2, 12]
enumerate function gives the current index of them item in the iterable and the current item, in each iteration. We then use the condition
item * 2 if index % 2 == 0 else item
to decide the actual value to be used. Here, if index % 2 == 0 then item * 2 will be used otherwise item will be used as it is.

Categories