0

I have a list of values and I want to move certain (or all) values to another list if they exist in a reference list.

x = [2,3,4,5,6,7,8] # list of values
ref = [2,3,4,5,6,7,8] # reference list
result = [x.pop(i) for i, v in enumerate(x) if v in ref]

But because of popping the current index, it ends up giving every other value instead. Is there a nice straightforward way to do this?

What I want at the end of this example is x=[] and result=[2,3,4,5,6,7,8], but ref doesn't need to contain all elements of x, this was just for an example. In another case it might be:

x = [2,3,4,5,6,7,8] # list of values
ref = [2,6,7] # reference list

So then I want x = [3,4,5,8] and result = [2,6,7]

desertnaut
  • 57,590
  • 26
  • 140
  • 166
  • Similar problem: [How to remove items from a list while iterating?](/q/1207406/4518341) – wjandrea Aug 03 '22 at 00:02
  • @wjandrea It is similar but I wanted in one shot to take from list `x` and move it into `result` if the condition is met. I need both to be updated in this process, but these solutions seem to just be creating a new list or updating the current one which is not what I am trying to accomplish. Maybe I am asking for the impossible, but since pop is so nice with a single element I figured there might be some way to just pop & append in a loop – crabulus_maximus Aug 03 '22 at 00:11
  • Why? The alternative seems to (1) append matching elements to a new list, and then (2) loop back again and remove the matching elements from x. This means there is a point in time where the same element exists in both lists which is also quite ugly – crabulus_maximus Aug 03 '22 at 00:18
  • [How to split a list based on a condition?](https://stackoverflow.com/q/949098/12671057) – Kelly Bundy Aug 03 '22 at 00:55
  • *"there is a point in time where the same element exists in both lists"* - And if you pop and append individually, there is a point in time where the element exists in **neither** iist. Is that really less "ugly"? – Kelly Bundy Aug 03 '22 at 01:00

3 Answers3

2

In general, we should avoid modifying objects while iterating over them.

For this problem, we could generate result and filter x in two steps using comprehensions (avoiding appending to lists) as in the following example.

result, x = [v for v in x if v in ref], [v for v in x if v not in ref]
Richard Ambler
  • 4,816
  • 2
  • 21
  • 38
1

You could do it the old-fashioned way, with a while loop and a pointer into x:

x = [2, 3, 4, 5, 6, 7, 8]
ref = [2, 6, 7]

result = []
i = 0
while i < len(x):
    if x[i] in ref:
        result.append(x.pop(i))
    else:
        i += 1

print(x)
print(result)

Output:

[]
[2, 3, 4, 5, 6, 7, 8]
wjandrea
  • 28,235
  • 9
  • 60
  • 81
0

You can simply iterate from the end to the start to avoid pop() changing the list size while iterating. Just call reverse() on your new list after running your loop if the order of the list matters.

Jesse
  • 73
  • 7