Python: Altering list values from a loop - python

I am creating a simple Bin packing program in Python for a class and am having a scope issue that I cannot quite track down. I am attempting to alter the value in the ffBins list however the list the value is unaltered.
The print results show what I expect ("10 5 5") as i is subtracted from j but it is not altering the value in the actual list as I am expecting. I suspect I need to point at the list in a different way during the loop but the solution is eluding me. Is there a simple way to alter the values in the list instead of just the current j variable?
ffBins.append(10)
for i in ffItems:
itemPacked = 0
for j in ffBins:
#print(j)
if j >= i:
print(j),
print(i),
j = j-i
print(j)
itemPacked = 1
#break
if itemPacked == 0:
ffBins.append(10)
ffBins[-1] = ffBins[-1]-ffItems[i]

All you're doing in this code is rebinding j to a new value. You need to set the value back in the list by its index. You can use enumerate to get the index as well as the value.
for index, j in enumerate(ffBins):
...
ffbins[index] = j - i

Related

Output the index of an item in a set in Python/Sage

This is what I have so far.
def f(n):
my_list=[]
while n not in my_list:
my_list.append(n)
if n % 2 == 0:
n = n / 2
else:
n = 3 * n + 1
my_list.append(n)
return my_list
The above code takes any input n and outputs a list e.g. f(2) -> [2, 1, 4, 2]
Now I want to look at any range and output just the highest element in said list.
def f_2(i):
for i in range (1,101):
set_f = set(f(i))
print(max(set_f))
So I converted the list to a set and applied the max command to it. All of this has worked as I had intended it so far.
My problem is with the following issue:
I want to output all the indexes of all the highest Elements in all generated lists.
E.g. for i in range (1,101): The highest Element is 9232. I tried doing it in the above way, but a set does not have any indexes. However max() does not seem to work on a list of generated lists.
My try was:
def f_3(i):
for i in range (1,101):
set_f = set(f(i))
if max(set_f) == 9232:
print(set_f.index(9232))
else:
pass
Here I get the error that set has no index attribute.
def f_3(i):
for i in range (1,101):
if max(f(i)) == 9232:
print(f.index(9232))
else:
pass
Here I get the error that function has no index attribute.
Only the range of 1 to 100 is of interest to me. So I can use 9232 as a value.
Any help would be greatly appreciated, I feel a bit stuck on this one.
There's several things to unpack here, and I feel like the Code Review community would be a better fit.
First of all, why does f_2 have the parameter i? You're just using the i from the loop.
Second, why are you converting the list into a set at all? max works just fine on lists too.
set doesn't support indexing, and that's why you were getting that mistake.
At the other attempt with f_3, you've called index on the function f instead of f(i).
Function f_2 can be rewritten as such.
def f_2():
for i in range (1,101):
lst = f(i)
mx = max(lst)
print(lst.index(mx))
Function f_3 is inefficient, but it too can be fixed like this:
def f_3():
for i in range (1,101):
if max(f(i)) == 9232:
print(f(i).index(9232))

Swapping List Elements During Iteration

I have read in several places that it is bad practice to modify an array/list during iteration. However many common algorithms appear to do this. For example Bubble Sort, Insertion Sort, and the example below for finding the minimum number of swaps needed to sort a list.
Is swapping list items during iteration an exception to the rule? If so why?
Is there a difference between what happens with enumerate and a simple for i in range(len(arr)) loop in this regard?
def minimumSwaps(arr):
ref_arr = sorted(arr)
index_dict = {v: i for i,v in enumerate(arr)}
swaps = 0
for i,v in enumerate(arr):
print("i:", i, "v:", v)
print("arr: ", arr)
correct_value = ref_arr[i]
if v != correct_value:
to_swap_ix = index_dict[correct_value]
print("swapping", arr[to_swap_ix], "with", arr[i])
# Why can you modify list during iteration?
arr[to_swap_ix],arr[i] = arr[i], arr[to_swap_ix]
index_dict[v] = to_swap_ix
index_dict[correct_value] = i
swaps += 1
return swaps
arr = list(map(int, "1 3 5 2 4 6 7".split(" ")))
assert minimumSwaps(arr) == 3
An array should not be modified while iterating through it, because iterators cannot handle the changes. But there are other ways to go through an array, without using iterators.
This is using iterators:
for index, item in enumerate(array):
# don't modify array here
This is without iterators:
for index in range(len(array)):
item = array[index]
# feel free to modify array, but make sure index and len(array) are still OK
If the length & index need to be modified when modifying an array, do it even more "manually":
index = 0
while index < len(array):
item = array[index]
# feel free to modify array and modify index if needed
index += 1
Modifying items in a list could sometimes produce unexpected result but it's perfectly fine to do if you are aware of the effects. It's not unpredictable.
You need to understand it's not a copy of the original list you ar iterating through. The next item is always the item on the next index in the list. So if you alter the item in an index before iterator reaches it the iterator will yield the new value.
That means if you for example intend to move all items one index up by setting item at index+1 to current value yielded from enumerate(). Then you will end up with a list completely filled with the item originally on index 0.
a = ['a','b','c','d']
for i, v in enumerate(a):
next_i = (i + 1) % len(a)
a[next_i] = v
print(a) # prints ['a', 'a', 'a', 'a']
And if you appending and inserting items to the list while iterating you may never reach the end.
In your example, and as you pointed out in a lot of algorithms for e.g. combinatoric and sorting, it's a part of the algorithm to change the forthcoming items.
An iterator over a range as in for i in range(len(arr)) won't adapt to changes in the original list because the range is created before starting and is immutable. So if the list has length 4 in the beginning, the loop will try iterate exactly 4 times regardless of changes of the lists length.
# This is probably a bad idea
for i in range(len(arr)):
item = arr[i]
if item == 0:
arr.pop()
# This will work (don't ask for a use case)
for i, item in enumerate(arr):
if item == 0:
arr.pop()

Append result of iteration in python

I am pretty new to python, i am using python 3 and have some difficulties to append the result of the iteration to the array, following is my chunk of my code:
A = np.random.randn(len(meas),numx)
lengthA = np.linspace(0,len(A[0])-1,num=len(A[0]),dtype=int)
anorm = []
for j in range(0,len(lengthA)):
x_normed = A/A.max(axis=0)
anorm[:,j] = A[:,j]*x_normed[j]
Is it necessary to append the new result to empty anom ? somehow the code always tell me that the list of the indices must be integer and not tuple. Any help will be appreciated.
When assigning a value to an array at index n via the array[index] = value syntax, the argument within the square brackets must be an integer. In your situation, j is an integer but [:,j] attempts to reference multiple indices within the array.
for j in range(0,len(lengthA)):
x_normed = A/A.max(axis=0)
anorm[j] = A[:,j]*x_normed[j]
As an aside, if you want your for loop to run for as many iterations as there are elements within lengthA, then you can simply do:
for j in lengthA:
do stuff

How to update array Index in loop (IndexError: list index out of range)

I should not use advance function, as this is a logical test during interview.
Trying to remove all digits which appear more than once in array.
testcase:
a=[1,1,2,3,2,4,5,6,7]
code:
def dup(a):
i=0
arraySize = len(a)
print(arraySize)
while i < arraySize:
#print("1 = ",arraySize)
k=i+1
for k in range(k,arraySize):
if a[i] == a[k]:
a.remove(a[k])
arraySize -= 1
#print("2 = ",arraySize)
i += 1
print(a)
result should be : 1,2,3,4,5,6,7
But i keep getting index out of range. i know that it is because the array list inside the loop changed, so the "while" initial index is different with the new index.
The question is : any way to sync the new index length (array inside the loop) with the parent loop (index in "while" loop) ?
The only thing i can think of is to use function inside the loop.
any hint?
Re-Calculating Array Size Per Iteration
It looks like we have a couple issues here. The first issue is that you can't update the "stop" value in your inner loop (the range function). So first off, let's remove that and use another while loop to give us the ability to re-calculate our array size every iteration.
Re-Checking Values Shifted Into Removed List Spot
Next, after you fix that you will run into a larger issue. When you use remove it moves a value from the end of the list or shifts the entire list to the left to use the removed spot, and you are not re-checking the value that got moved into the old values removed spot. To resolve this, we need to decrement i whenever we remove an element, this makes sure we are checking the value that gets placed into the removed elements spot.
remove vs del
You should use del over remove in this case. remove iterates over the list and removes the first occurrence of the value and it looks like we already know the exact index of the value we want to remove. remove might work, but it's usage here over complicates things a bit.
Functional Code with Minimal Changeset
def dup(a):
i = 0
arraySize = len(a)
print(arraySize)
while i < arraySize:
k = i + 1
while k < arraySize: # CHANGE: use a while loop to have greater control over the array size.
if a[i] == a[k]:
print("Duplicate found at indexes %d and %d." % (i, k))
del a[i] # CHANGE: used del instead of remove.
i -= 1 # CHANGE: you need to recheck the new value that got placed into the old removed spot.
arraySize -= 1
break
k += 1
i += 1
return a
Now, I'd like to note that we have some readability and maintainability issues with the code above. Iterating through an array and manipulating the iterator in the way we are doing is a bit messy and could be prone to simple mistakes. Below are a couple ways I'd implement this problem in a more readable and maintainable manner.
Simple Readable Alternative
def remove_duplicates(old_numbers):
""" Simple/naive implementation to remove duplicate numbers from a list of numbers. """
new_numbers = []
for old_number in old_numbers:
is_duplicate = False
for new_number in new_numbers:
if old_number == new_number:
is_duplicate = True
if is_duplicate == False:
new_numbers.append(old_number)
return new_numbers
Optimized Low Level Alternative
def remove_duplicates(numbers):
""" Removes all duplicates in the list of numbers in place. """
for i in range(len(numbers) - 1, -1, -1):
for k in range(i, -1, -1):
if i != k and numbers[i] == numbers[k]:
print("Duplicate found. Removing number at index: %d" % i)
del numbers[i]
break
return numbers
You could copy contents in another list and remove duplicates from that and return the list. For example:
duplicate = a.copy()
f = 0
for j in range(len(a)):
for i in range(len(duplicate)):
if i < len(duplicate):
if a[j] == duplicate[i]:
f = f+1
if f > 1:
f = 0
duplicate.remove(duplicate[i])
f=0
print(duplicate)

How to express j+1 in this case?

I'm writing a piece of code to filter some data, and I've come across a problem. I currently have:
def accountforfilter(wavelength, flux, filterwavelength, throughput):
filteredwavelength=[]
filteredflux=[]
for i in wavelength:
for j in filterwavelength:
if wavelength==filterwavelength:
j=filterwavelength.index(wavelength)
filteredwavelength.append(wavelength)
filteredflux.append(flux*throughput)
elif filterwavelength<wavelength<filterwavelength(j+1):
filteredwavelength.append(wavelength)
filteredflux.append(flux*f(wavelength))
#f is a function that interpolates using y=mx+c when wavelength and filterwavelength are not the same
elif wavelength<filterwavelength:
i+=1
elif wavelength>filterwavelength:
j+=1
return filteredwavelength, filteredflux
The problem I have is with the line:
elif filterwavelength<wavelength<filterwavelength(j+1):
This line doesn't work, I get the error:
TypeError: 'list' object is not callable
If I change the round brackets to square brackets, i.e.
elif filterwavelength<wavelength<filterwavelength[j+1]:
I get the error:
TypeError: list indices must be integers, not float
I'm not sure how to express what I mean in another way.
You should really use more descriptive variable names to avoid numerous errors that exist in your code.
I assume that in your function parameters, wavelength and filterwavelength are lists. So let’s make that clear by using plural names:
def accountforfilter(wavelengths, flux, filterwavelengths, throughput)
As soon as you do that, comparison like these don’t make much sense anymore:
if wavelengths == filterwavelengths:
elif filterwavelengths < wavelengths < filterwavelengths[j+1]:
Especially in the second comparison, you are comparing a list, with a list, with a list item.
Finally, when using for, the loop variable (your i and j) is set to individual list items, not indexes. So you definitely can’t tread those values as indexes. To reflect that, you should name them appropriately:
# one wavelength out of all wavelengths
for wavelength in wavelengths:
# one filterwavelength out of all filterwavelengths
for filterwavelength in filterwavelengths:
Also note, that when using for, you can’t change the way they are iterating. Every iteration, the next item will be handled; you can’t skip or repeat one item (as you seem to be trying with your i += 1 and j += 1. If you need to do that, you will have to use a classic while loop instead:
i, j = 0, 0
while i < len(wavelengths):
while j < len(filterwavelengths):
if condition:
i += 1
else:
j += 1
So, in total, the function might look like this (tried my best to understand what’s going on):
def accountforfilter (wavelengths, flux, filterwavelengths, throughput):
filteredWavelengths = []
filteredFluxes = []
i, j = 0, 0
while i < len(wavelengths):
wavelength = wavelengths[i]
while j < len(filterwavelengths):
filterwavelength = filterwavelengths[j]
if wavelength == filterwavelength:
filteredWavelengths.append(wavelength)
filteredFluxes.append(flux * throughput)
elif j + 1 < len(filterwavelengths) and filterwavelength < wavelength < filterwavelengths[j + 1]:
filteredWavelengths.append(wavelength)
filteredFluxes.append(flux * f(wavelength))
if wavelength < filteredwavelength:
i += 1
else:
j += 1
# increase j even when both are equal, otherwise we end up in an infinite loop
return filteredWavelengths, filteredFluxes
A few things: This statement:
for i in wavelength:
produces a sequence of the values in the wavelength list, not indexes to them.
If you want the indexes as well, use
for i, wv in enumerate(wavelength):
...
Similarly for the j loop. Also, this comparison: wavelength==filterwavelength is dangerous if the values are floating point. It's a little hard to advise further without more information about what you're trying to acheive.

Categories

Resources