-2

I am iterating through a list an sometimes I remove an element from another list. Instead of removing the element from the other list the for loop skips the entire actual iteration. I found a solution to that, but I don't understand it quite well.

Here is a code example. It's two times the same except that I commented out the line final_dt_list.remove(dt):

dt_list = [3600, 2700, 1800, 900]
final_dt_list = dt_list
for dt in dt_list:
    print(dt)
    if not((3600/dt).is_integer()):
        print('skipped '+str(dt))
        final_dt_list.remove(dt)
        continue
    print('ok')

print('\n \n')

dt_list = [3600, 2700, 1800, 900]
final_dt_list = dt_list
for dt in dt_list:
    print(dt)
    if not((3600/dt).is_integer()):
        print('skipped '+str(dt))
        #final_dt_list.remove(dt)
        continue
    print('ok')

The result that I get is:

3600
ok
2700
skipped 2700
900
ok



3600
ok
2700
skipped 2700
1800
ok
900
ok

As you can see in the result of the first loop, 1800 is not printed, just nothing happens.

What worked for me finally was creating a list of items I want to remove. And than create a new loop that iterates through the items I want to remove. This new loop will then remove each element from final_dt_list.

See solution code here:

for r in removed:
    final_dt_list.remove(r)

Why does that work now and not the example above? I'm confused.

Thanks in advance!

smci
  • 32,567
  • 20
  • 113
  • 146
programmar
  • 594
  • 6
  • 19
  • 4
    By `final_dt_list = dt_list` you are simply referencing to the original list. Any changes to `dt_list` will be reflected in `final_dt_list` and vice versa. You can use `final_dt_list = dt_list.copy()`. For more info, read [this](https://stackoverflow.com/questions/19951816/python-changes-to-my-copy-variable-affect-the-original-variable) and [this](https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list). The second link explains how to copy a list – Sheldore Oct 17 '18 at 00:03
  • @Bazingaa thank you, I did not know that. Feels like I should have. – programmar Oct 17 '18 at 00:05
  • 1
    This is **aliasing**. Specifically you're iterating over an alias of the list and making deletions. So the deletions happen on both the original and the alias. – smci Oct 17 '18 at 00:34
  • Anyway, **list comprehensions** are a clearer, shorter and simpler code idiom to selectively pick items from a list satisfying a condition. You don't need a for-loop. In Python you don't need a for-loop for many things you'd need a list for in other languages (e.g. Java). Please read about this. – smci Oct 17 '18 at 00:34
  • `final_dt_list = [d for d in dt_list if (3600 % d)==0]`. It's a one-liner! – smci Oct 17 '18 at 00:37
  • And `(number % d) ==0` was historically the more common idiom to test divisibility by d than `(number/d).is_integer()` – smci Oct 17 '18 at 00:39
  • @smci I know for the one liner, I removed the actual code so it’s easier for the community to understand what’s the issue. – programmar Oct 17 '18 at 08:22
  • Ok but do you understand that *"creating a list of items I want to remove. And then creating a second loop that iterates through the items I want to remove"* is a very heavy weather way of just doing a simple list comprehension: `final_dt_list = [d for d in dt_list if (3600 % d)==0]` ? Please learn the basic Python idioms and do yourself a big favour. – smci Oct 17 '18 at 08:55
  • Yep I understand and use list comprehension! Thanks. But when I do a lot of operations in a loop that do not only concern lists then it makes sense to write a normal loop. Right? – programmar Oct 17 '18 at 13:45
  • programmar: not necessarily, and that's why I'm saying it's better to learn good idiomatic Python from the start. If all you're doing is evaluating a (possibly quite complex) condition purely to decide whether to keep/drop list items, then that's a list comprehension, albeit in disguise. You can pretty much always reduce that to a one-liner, and maybe abstract the condition into a helper predicate function for clarity. `list.remove()` is a strong code smell that you're really doing a list comprehension. Hence, in general there's rarely need to make copies of lists. FizzBuzz is another example. – smci Oct 18 '18 at 00:59

1 Answers1

2

dt_list and final_dt_list point to the same object (aliasing). You edit the list while loop over it. Since you only keep pointers for looping you remeber positions, but they change since elements vanish.

Try remebering the elements you want to delete by something like:

    dt_list = [3600, 2700, 1800, 900]
    rm_list = []
    for dt in dt_list:
        print(dt)
        if not((3600/dt).is_integer()):
            print('skipped '+str(dt))
            rm_list.append(dt)
            continue
        print('ok')
    dt_list = [x for x in dt_list if x not in rm_list]

EDIT:

Using List Comprehensions from the start leads to:

dt_list = [3600, 2700, 1800, 900]
dt_list = [x for x in dt_list if (3600/x).is_integer()]
smci
  • 32,567
  • 20
  • 113
  • 146
DiCaprio
  • 823
  • 1
  • 7
  • 24