Emptying a list into a new list - python

I'm trying to make a function that when a list is empty, a second list will empty into the empty one in reverse order. Right now, I tried to do:
a = [1,2,3,4]
b = []
def a_to_b(a, b):
if not a:
print('a:',a)
print('b:',b)
for c in b:
a.append(c)
b.remove(c)
print('a:',a)
print('b:',b)
return True
else:
top = a.pop()
b.append(top)
print('a:',a)
print('b:',b)
return True
I want it to be after each run:
1) a = [1,2,3]
b = [4]
2) a = [1,2]
b = [4,3]
3) a = [1]
b = [4,3,2]
4) a = []
b = [4,3,2,1]
5) a = [1,2,3,4]
b = []
But after this fifth run it is giving me
a = [4,2]
b = [3,1]
And I cannot figure out why it is only applying to every other number in b.

This should work
a = [1,2,3,4]
b = []
for i in range(len(a)):
b.append(a[-1])
a.pop()
print a,b

Your problem is caused by you removing elements that you're hopping over in the for loop)
Pointer is going as index 0, 1, 2, 3 but you're already removing the 0th element causing the pointer to go straight to 2 (which is now index 1 in remaining list)
to avoid it, you could change your code to:
for c in b:
a.append(c)
for c in a:
b.remove(c)

Here's the reason why you're getting a weird result:
for c in b:
a.append(c)
b.remove(c)
You're changing list b as you're iterating over it. Thus things are not going to come out as you expect them to. Why don't you just do
b.reverse()
a = b
b = []
In the place of what you had before. So it'd be
a = [1,2,3,4]
b = []
def a_to_b(a, b):
if not a:
print('a:',a)
print('b:',b)
b.reverse()
a = b
b = []
print('a:',a)
print('b:',b)
return True
else:
top = a.pop()
b.append(top)
print('a:',a)
print('b:',b)
return True

def f(a, b):
if a:
b.append(a.pop())
else:
while b:
a.append(b.pop())
print 'a: %s' % a
print 'b: %s' % b
>>> a = [1, 2, 3, 4]
>>> b = []
>>> f(a, b)
a: [1, 2, 3]
b: [4]
>>> f(a, b)
a: [1, 2]
b: [4, 3]
>>> f(a, b)
a: [1]
b: [4, 3, 2]
>>> f(a, b)
a: []
b: [4, 3, 2, 1]
>>> f(a, b)
a: [1, 2, 3, 4]
b: []

The problem is here:
for c in b:
a.append(c)
b.remove(c)
You cant remove objects from the middle of an interable without confusing the loop.
Heres what happens:
You have b=[4,3,2,1] and a=[]. You start the for loop and c is pointing to the first index of b, ie 4. You remove c from the list and put it in a.
Now you have b = [3,2,1] and a=[4] like you expect.
When you try to start the next loop, your index is incremented (c is now pointing at the 2nd element) but the problem is you've messed with the structure of the iterable. So the loop removes c like its supposed to, but c=2, not 3 like you are expecting.
Now you have a=[4,2] and b=[1,3] and when the loop checks for index 3, it finds that b is only has 2 elements so it exits.

Related

Compare two lists to return a list with all elements as 0 except the ones which matched while keeping index?

I am a bit stuck on this:
a = [1,2,3,2,4,5]
b = [2,5]
I want to compare the two lists and generate a list with the same items as a, but with any items that don't occur in b set to 0. Valid outputs would be these:
c = [0,2,0,0,0,5]
# or
c = [0,0,0,2,0,5]
I would not know the number elements in either list beforehand.
I tried for loops but
['0' for x in a if x not in b]
It removes all instances of 2. Which I only want to remove once(it occurs once in b for the moment). I need to add a condition in the above loop to keep elements which match.
The following would work:
a = [1,2,3,2,4,5]
b = [2, 5]
output = []
for x in a:
if x in b:
b.remove(x)
output.append(x)
else:
output.append(0)
or for a one-liner, using the fact that b.remove(x) returns None:
a = [1,2,3,2,4,5]
b = {2, 5}
output = [(b.remove(x) or x) if x in b else 0 for x in a]
If the elements in b are unique, this is best done with a set, because sets allow very efficient membership testing:
a = [1,2,3,2,4,5]
b = {2, 5} # make this a set
result = []
for num in a:
# If this number occurs in b, remove it from b.
# Otherwise, append a 0.
if num in b:
b.remove(num)
result.append(num)
else:
result.append(0)
# result: [0, 2, 0, 0, 0, 5]
If b can contain duplicates, you can replace the set with a Counter, which represents a multiset:
import collections
a = [1,2,3,2,4,5]
b = collections.Counter([2, 2, 5])
result = []
for num in a:
if b[num] > 0:
b[num] -= 1
result.append(num)
else:
result.append(0)
# result: [0, 2, 0, 2, 0, 5]
Here's one way using set. Downside is the list copy operation and initial set conversion. Upside is O(1) removal and lookup operations.
a = [1,2,3,2,4,5]
b = [2,5]
b_set = set(b)
c = a.copy()
for i in range(len(c)):
if c[i] in b_set:
b_set.remove(c[i])
else:
c[i] = 0
print(c)
[0, 2, 0, 0, 0, 5]

Remove intersection from two lists in python

Given two lists, what is the best way to remove the intersection of the two? For example, given:
a = [2,2,2,3]
b = [2,2,5]
I want to return:
a = [2,3]
b = [5]
Let's assume you wish to handle the general case (same elements appear more than once in each list), the so called multiset.
You can use collections.Counter:
from collections import Counter
intersection = Counter(a) & Counter(b)
multiset_a_without_common = Counter(a) - intersection
multiset_b_without_common = Counter(b) - intersection
new_a = list(multiset_a_without_common.elements())
new_b = list(multiset_b_without_common.elements())
For your values of a, b, you'll get:
a = [2,2,2,3]
b = [2,2,5]
new_a = [2, 3]
new_b = [5]
Note that for a special case of each element appearing exactly once, you can use the standard set, as the other answers are suggesting.
You can loop through the two lists and remove elements as you find an intersect point as the following:
a = [2, 2, 2, 3]
b = [2, 2, 5]
delete = []
for c in a:
for n in b:
if n == c:
delete.append(c)
delete.append(n)
break
a.remove(delete[0])
b.remove(delete[1])
delete = []
print a
print b
output:
[2, 3]
[5]
a = [2,2,2,3]
b = [2,2,5]
for i in list(b): #I call list() on b because otherwise I can't remove from it during the for loop.
if i in a:
a.remove(i)
b.remove(i)
Output:
a = [2, 3]
b = [5]

python:confusion about assignment statement:l = [a,b] = [0,1]?

In python 3.4 interactive prompt:
>>> l = [a,b] = [0,1]
>>> a is l[0]
True
>>> b is l[1]
True
>>> l[0] = 2
>>> a
0
>>> l
[2, 1]
I assume that the following statement performs in place change of the first element of the list.
l[0] = 2
Since the variable a is referencing the same object,why it's value remains 0? What happens internally in this assignment statement?
This is because you actually create three variables there a, b, and l. Only the last one is a list. What happened in line-by-line format was this:
a = 0
b = 1
l = [a, b] #which means l = [0, 1], not really the same referencing element a, b
Then if you check
a is l[0]
b is l[1]
Both return true because the value they have are the same, but again, the values, not the references
Then when you change:
l[0] = 2
Only value in l[0] changes, it does not affect a. Best is to check using good debugger tool like PyCharm. It shows you in the watch windows and inlines all the current values of the variables.
a and l[0] are two names for the same object, but when you say l[0] = 2, you are just redefining l[0]. a still refers to the same object as it did before; it's just l[0] that has changed. a refers to an object, not a position.
I think the confusion is due to the initial assignment:
l = [a,b] = [0,1]
This is equivalent to:
t = [0, 1]
l = [a, b] = t
which in turn is equivalent to:
t = [0, 1]
a = t[0]
b = t[1]
l = t
which in turn is equivalent to:
l = [0, 1]
a = l[0]
b = l[1]
The rest follows from this.

How to print elements in a list in a plain one-row way?

I have to define a procedure, union, that takes as inputs two lists.
It should modify the first input list to be the set union of the two lists. I assume the first list is a set, that is, it contains no repeated elements.
I´ve tried this, and it works:
def union(a, b):
a.append(b)
for item in b:
if item in a:
a.remove(item)
When I try to test it, this is the output:
a = [1,2,3]
b = [2,4,6]
union(a,b)
print a
The output that I should receive is for the excercise:
[1,2,3,4,6]
The output that I receive is:
[1, 3, [2, 4, 6]]
How may I print the output in order to match the desired one? Or is it the same thing?
why don't you use a set?
def union(a, b):
return list(set(a + b))
this will not modify you list BUT set is not ordered, so you can't rely on order of you elements.
if you try to find an error in your code, you could modify it like this:
def union(a, b):
for item in b:
if item in a:
a.remove(item)
    a.extend(b)
if you really want to add new items to a, you can use this:
def union(a, b):
a.extend([x for x in b if x not in a])
a = [1,2,3]
b = [2,4,6]
def union(a, b):
set_a = set(a)
for ele in b:
if ele not in set_a:
a.append(ele)
return a
Result:
>>> union(a,b)
[1, 2, 3, 4, 6]
Note that when you use remove, the first element in the list is removed:
>>> a = [1,2,3,2,4,6]
>>> a.remove(2)
>>> a
[1, 3, 2, 4, 6]
Therefore, to get your desired outcome we must keep a as it is, and add to it elements from b that are not it a.
Try this simple way:
def union(a, b):
return list(set(a + b))

How can I compare the values of two lists in python?

I want to compare the values of two lists.
For example:
a = [1, 2, 3]
b = [1, 2, 3]
I need to check if a is same as b or not. How do I do that?
a == b
This is a very simple test, it checks if all the values are equal.
If you want to check if a and b both reference the same list, you can use is.
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b # a and b have the same values but refer to different lists in memory
False
>>> a = [1, 2, 3]
>>> b = a
>>> a is b # both refer to the same list
True
simply use
a == b
the operator == will compare the value of a and b, no matter whether they refer to the same object.
#jamylak's answer is what I would go with. But if you're looking for "several options", here's a bunch:
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b
True
OR
def check(a,b):
if len(a) != len(b):
return False
for i in xrange(len(a)):
if a[i] != b[i]:
return False
return True
OR
>>> len(a)==len(b) and all((a[i]==b[i] for i in xrange(len(a))))
True
OR
def check(a,b):
if len(a) != len(b):
return False
for i,j in itertools.izip(a,b):
if i != j:
return False
return True
OR
>>> all((i==j for i,j in itertools.izip(a,b)))
True
OR (if the list is made up of just numbers)
>>> all((i is j for i,j in itertools.izip(a,b)))
True
OR
>>> all((i is j for i,j in itertools.izip(a,b)))
True
Hope that satiates your appetite ;]

Categories