I know that this should be easier, but I've having issues comparing float values.
I have some very basic code that is doing a few things:
packages
list.def process_packages(package_list):
light_pkgs = []
heavy_pkgs = []
light_weight = 5.00
heavy_weight = 15.00
for i in package_list:
if i < light_weight:
light_pkgs.append(i)
elif i > heavy_weight:
heavy_pkgs.append(i)
packages.remove(i)
return light_pkgs, heavy_pkgs
packages = [2.4, 3.44, 4.55, 4.9, 5.11, 5.31, 5.61, 5.99, 6.11, 7.34, 7.9, 9, 11.3, 15.1, 15.31, 15.68, 15.9, 16.7, 19.3, 21]
print(process_packages(packages))
print(packages)
I believe that due to precision errors my code isn't identifying several of the numbers in the 15.xx range.
Also, I'm limited to using built-in Python functions.
Expected Output:
([2.4, 3.44, 4.55, 4.9], [15.1, 15.31, 15.68, 15.9, 16.7, 19.3, 21])
[2.4, 3.44, 4.55, 4.9, 5.11, 5.31, 5.61, 5.99, 6.11, 7.34, 7.9, 9, 11.3]
Actual Output:
([2.4, 3.44, 4.55, 4.9], [15.1, 15.68, 16.7, 21])
[2.4, 3.44, 4.55, 4.9, 5.11, 5.31, 5.61, 5.99, 6.11, 7.34, 7.9, 9, 11.3, 15.31, 15.9, 19.3]
As you can see several numbers above 15 are not being added to the heavy_pkgs list, and aren't being removed from the original packages list.
You're experiencing the problem from How to remove items from a list while iterating? with extra steps.
packages
in global scope is the same thing as package_list
in the function's scope (they're both aliases to the same list
). When you remove
from it while iterating over it, you skip iterating over the element that immediately follows the removed element (because it moves a slot down, and the iterator advances past that slot on the next loop).
Build a new list
with the light and medium weights, and replace the contents of package_list
after the loop completes, and things should work (and work more efficiently, as each call to remove
is O(n)
, so the overall work is O(n²)
, while a two pass build-and-replace is O(n)
).
def process_packages(package_list):
light_pkgs = []
not_heavy_pkgs = [] # Temporary list to hold stuff that should remain in package_list
heavy_pkgs = []
light_weight = 5.00
heavy_weight = 15.00
for i in package_list:
if i < light_weight:
light_pkgs.append(i)
not_heavy_pkgs.append(i) # Looks like light things are supposed to stay in too
elif i > heavy_weight:
heavy_pkgs.append(i)
else:
not_heavy_pkgs.append(i) # It's not heavy, so retain it
package_list[:] = not_heavy_pkgs # Assigning to complete slice replaces contents of
# package_list in place, affecting caller's version of list
# as your original code tried to do
return light_pkgs, heavy_pkgs
That code does get a bit unwieldy. While it would involve a couple more passes over the input, it would be much more succinct to rely on a handful of list comprehensions here:
def process_packages(package_list):
heavy_weight = 15.0
# Get heavy packages
heavy_pkgs = [pkg for pkg in package_list if pkg > heavy_weight]
# Remove heavy packages from source
package_list[:] = [pkg for pkg in package_list if pkg <= heavy_weight]
# One more pass to get light packages and we're done
# This pass is somewhat cheaper, because heavy items were already removed
return [pkg for pkg in package_list if pkg < 5.0], heavy_pkgs