I came across this problem when attempting to learn python. Consider the following function:
def swap0(s1, s2):
assert type(s1) == list and type(s2) == list
tmp = s1[:]
s1 = s2[:]
s2 = tmp
return
s1 = [1]
s2 = [2]
swap0(s1, s2)
print s1, s2
What will s1 and s2 print?
After running the problem, I found that the print statement will print 1 2. It seems that the value of s1 and s2 did not change from the swap0 function. The only explanation that I could think of was because of the line.
tmp = s1[:]
Since s1[:] is a copy, this makes sense that the value of s1 will not change in the function call. However because the parameter of swap0 is (s1, s2), I am not sure if after doing tmp = s1[:]. Anytime I do
s1 = something...
it will be a reference to the copy of s1, instead of s1 itself. Can someone offer a better explanation? Thanks.
It's because it assigns new values to s1 and s2 inside the swap0 function. These assignments do not propagate outside the function. You'll see that it works if you just copy and paste the function body in the place of the function call.
You can work around this by modifying the objects referenced by the arguments, rather than the arguments themselves:
def swap0(s1, s2):
assert type(s1) == list and type(s2) == list
tmp = s1[:]
s1[:] = s2
s2[:] = tmp
However, the easier and better way to do a swap in Python is simply:
s1, s2 = s2, s1
This, too, will only swap those particular references to the lists, but not the list contents themselves.
As it is, your final print will print out the original values of s1 and s2. This is because you're only swapping them within the scope of the function. Doing so will not affect their values outside the function (i.e. after their values after the function has been called)
If they are mutable types (list, set, dict, etc), then you could modify them in-place inside swap. However, that restricts swap to work only on mutable types.
You are therefore better off returning the inputs in reversed order:
def swap(s1, s2):
return s2, s1
s1 = 'a'
s2 = 'b'
s1, s2 = swap(s1, s2)
print s1, s2 # prints 'b a'
Of course, you could do this all in one line as follows:
s1, s2 = s2, s1
Cheers!
The other answers explain what's going wrong. Here's a version that does what you want:
def swap(s1, s2):
assert isinstance(s1, list) and isinstance(s2, list)
s1[:], s2[:] = s2[:], s1[:]
See also: isinstance vs. type
Inside the function, you're rebinding local variables s1 and s2 with the values on the right hand side (which are also local since you're using slices to make copies). Even if you change the contents of those local variables, you won't change the contents of the lists in the calling scope because they no longer refer to the same lists.
Here is a one-line function that accomplishes your goal:
swap = lambda x: (x[1], x[0])
You can also do this by old swaping method using indexing and loop if both list have same length. This is kind of old school but will help in understanding indexing
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
b = [0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
for i in range(0, len(a)):
a[i] = a[i] + b[i]
b[i] = a[i] - b[i]
a[i] = a[i] - b[i]
print(a)
print(b)
This will give the output as :
[0,9,8,7,6,5,4,3,2,1]
[1,2,3,4,5,6,7,8,9,0]
Or It can also be done using Xor. Xor operator is a bitwise operator which do the Xor operation between the operands for example.
a = 5 #0b101
b = 4 #0b100
c = a ^ b #0b001
Here 0b101 is a binary representation of 5 and 0b100 is a binary representation of 4 and when you Xor these you will the ouput as 0b001 i.e 1 . Xor return 1 output results if one, and only one, of the inputs to is 1. If both inputs are 0 or both are 1, 0 output results.
We can swap a two variables using Xor for eg:
a = 5 # 0b0101
b = 9 # 0b1001
a = a ^ b # Xor (0b0101, 0b1001) = 0b1100 (12)
b = a ^ b # Xor (0b1100, 0b1001) = 0b0101 (5)
a = a ^ b # Xor (0b1100, 0b0101) = 0b1001 (9)
print("a = {} and b = {}".format(a, b))
The Output will be a = 9 and b = 5
Similarly we can also swap two list by doing Xor operation on there items for eg:
a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
b = [ 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
for i in range(0, len(a)) :
a[i] = a[i] ^ b[i]
b[i] = a[i] ^ b[i]
a[i] = a[i] ^ b[i]
print(a)
print(b)
Output:
[0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
Lets Take another scenario, What if we need to swap the items within the list for eg:
we have a list like this x = [ 13, 3, 7, 5, 11, 1 ] and we need to swap its item like this x = [ 1, 3, 5, 7 , 11, 13 ] So we can do this by Using two bitwise operators Xor ^ and Compliments ~
Code :
# List of items
a = [ 13, 3, 7, 5, 11, 1 ]
# Calculated the length of list using len() and
# then calulated the middle index of that list a
half = len(a) // 2
# Loop from 0 to middle index
for i in range(0, half) :
# This is to prevent index 1 and index 4 values to get swap
# because they are in their right place.
if (i+1) % 2 is not 0 :
#Here ~i means the compliment of i and ^ is Xor,
# if i = 0 then ~i will be -1
# As we know -ve values index the list from right to left
# so a [-1] = 1
a[i] = a[i] ^ a[~i]
a[~i] = a[i] ^ a[~i]
a[i] = a[i] ^ a[~i]
print(a)
So Output will be [1, 3, 5, 7, 11, 13]
yo can have this:
def swap(x , y):
x , y = y , x
return x , y
x = 5
y = 10
print ('x is {0} and y is {1}'.format(x,y)) # x is 5 and y is 10
x , y = swap(x,y) # doing swap
print ('x is {0} and y is {1}'.format(x,y)) # x is 10 and y is 5
There is no need for function at all. a,b=b,a does the trick.
>>> a,b=1,2
>>> print (a,b)
(1, 2)
>>> a,b=b,a
>>> print (a,b)
(2, 1)
>>>
It works for arrays as well.
But if you are so want the function here it is
def swap(a,b)
return b,a
Related
My problem is that in python i have a list of 1000 numbers. I want to select first 20 and store in "A" (another list) and next 30 in "B"(another list), again next 20 in "A", next 30 in "B" untill the list ends. How to do that? Can anyone help please?
You could use two nested list comprehension for this:
nums = list(range(2000))
A = [x for i in range( 0, len(nums), 50) for x in nums[i:i+20]]
B = [x for i in range(20, len(nums), 50) for x in nums[i:i+30]]
That means (for A): Starting at index 0, 50, 100 etc., take the next 20 elements and add them to A. Analogous for B. You might also use itertools.islice for the nums[i:i+n] part to avoid creating a bunch of temporary sub-lists.
Of course, this assumes that A and B do not already exist. If they do, and you want to add elements to them, use a regular loop and extend (not tested):
for i in range(0, len(nums), 50):
A.extend(nums[i :i+20])
B.extend(nums[i+20:i+50])
You do not have to check the indices again within the loop; if i+20 is already bigger than the last index in the list, B will just get extended with the empty list in the last step.
I made a function that can do what you're asking
def split_list(lst, gap1, gap2):
tmp = lst.copy()
out1, out2 = [], []
while tmp:
out1.extend(tmp[:gap1])
tmp = tmp[gap1:]
out2.extend(tmp[:gap2])
tmp = tmp[gap2:]
return out1, out2
gap1 and gap2 are the length of each subset you want to store, so in your case gap1=20 and gap2=30.
Example
numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
A, B = split_list(numbers, 2, 3)
print(A)
print(B)
Let's first define the followings:
A = [] #empty list
B = [] #empty list
And let's call your first list (that includes 1000 numbers), as my_list.
Then, a simple solution can be as following:
counter = 0
c_a = 2
c_b = 3
while (counter < len(my_list)):
A = A + my_list[counter:counter+c_a]
B = B + my_list[counter+c_a : counter + c_a + c_b]
counter = counter + c_a + c_b
I suggest generating a 2D nested list, which contains sublists each having 20 elements in it. Here is a one line way of doing that with a list comprehension:
x = range(1, 1001)
output = [x[idx*20:(idx*20 + 20)] for idx in range(0, len(x) / 20)]
Here is a version which operates on a smaller set of data, for demonstration purposes:
x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
output = [x[idx*3:(idx*3 + 3)] for idx in range(0, len(x) / 3)]
print(output) # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I need to swap elements in a Python list. It works when I'm using a temporary variable to handle the swapping but doesn't seem to work when I do the same thing in a pythonic style, i.e. a, b = b, a.
Suppose the index we are dealing with is i = 1. I am trying to swap the elements at A[i] and A[A[i]].
Input A = [2,3,4,5,6,7,8,9]
Expected Output = [2,5,4,3,6,7,8,9]
First I tried the pythonic way. Didn't get the expected output.
>>> i = 1
>>> A = [2,3,4,5,6,7,8,9]
>>> A[i], A[A[i]] = A[A[i]], A[i]
>>> A
[2, 5, 4, 5, 6, 3, 8, 9]
The non-pythonic way worked.
>>> i = 1
>>> B = [2,3,4,5,6,7,8,9]
>>> temp = B[B[i]]
>>> B[B[i]] = B[i]
>>> B[i] = temp
>>> B
[2, 5, 4, 3, 6, 7, 8, 9]
I just want to know why this is and when I should avoid using simultaneous assignment.
It's because the code is assigning during the process not after, so if you do the reverse order:
A[A[i]], A[i] = A[i], A[A[i]]
The 3rd element will be fixed, but the 5th element will go wrong.
When A[i] = A[A[i]] is executed, it means 3 (wiz. on 1 pos) is now 5 then, A[A[i]] = A[i], remember, here A[i] on LHS is already 5 and on index 5 it is 7, also hence index 3 is untouched.
changing the code a bit like stated in the above answer,
A[A[i]], A[i] = A[i], A[A[i]]
will do the thing where the indexes are kinda preserved.
I want to do the following in Python:
A = [1, 2, 3, 4, 5, 6, 7, 7, 7]
C = A - [3, 4] # Should be [1, 2, 5, 6, 7, 7, 7]
C = A - [4, 3] # Should not be removing anything, because sequence 4, 3 is not found
So, I simply want to remove the first appearance of a sublist (as a sequence) from another list. How can I do that?
Edit: I am talking about lists, not sets. Which implies that ordering (sequence) of items matter (both in A and B), as well as duplicates.
Use sets:
C = list(set(A) - set(B))
In case you want to mantain duplicates and/or oder:
filter_set = set(B)
C = [x for x in A if x not in filter_set]
If you want to remove exact sequences, here is one way:
Find the bad indices by checking to see if the sublist matches the desired sequence:
bad_ind = [range(i,i+len(B)) for i,x in enumerate(A) if A[i:i+len(B)] == B]
print(bad_ind)
#[[2, 3]]
Since this returns a list of lists, flatten it and turn it into a set:
bad_ind_set = set([item for sublist in bad_ind for item in sublist])
print(bad_ind_set)
#set([2, 3])
Now use this set to filter your original list, by index:
C = [x for i,x in enumerate(A) if i not in bad_ind_set]
print(C)
#[1, 2, 5, 6, 7, 7, 7]
The above bad_ind_set will remove all matches of the sequence. If you only want to remove the first match, it's even simpler. You just need the first element of bad_ind (no need to flatten the list):
bad_ind_set = set(bad_ind[0])
Update: Here is a way to find and remove the first matching sub-sequence using a short circuiting for loop. This will be faster because it will break out once the first match is found.
start_ind = None
for i in range(len(A)):
if A[i:i+len(B)] == B:
start_ind = i
break
C = [x for i, x in enumerate(A)
if start_ind is None or not(start_ind <= i < (start_ind + len(B)))]
print(C)
#[1, 2, 5, 6, 7, 7, 7]
I considered this question was like one substring search, so KMP, BM etc sub-string search algorithm could be applied at here. Even you'd like support multiple patterns, there are some multiple pattern algorithms like Aho-Corasick, Wu-Manber etc.
Below is KMP algorithm implemented by Python which is from GitHub Gist.
PS: the author is not me. I just want to share my idea.
class KMP:
def partial(self, pattern):
""" Calculate partial match table: String -> [Int]"""
ret = [0]
for i in range(1, len(pattern)):
j = ret[i - 1]
while j > 0 and pattern[j] != pattern[i]:
j = ret[j - 1]
ret.append(j + 1 if pattern[j] == pattern[i] else j)
return ret
def search(self, T, P):
"""
KMP search main algorithm: String -> String -> [Int]
Return all the matching position of pattern string P in S
"""
partial, ret, j = self.partial(P), [], 0
for i in range(len(T)):
while j > 0 and T[i] != P[j]:
j = partial[j - 1]
if T[i] == P[j]: j += 1
if j == len(P):
ret.append(i - (j - 1))
j = 0
return ret
Then use it to calcuate out the matched position, finally remove the match:
A = [1, 2, 3, 4, 5, 6, 7, 7, 7, 3, 4]
B = [3, 4]
result = KMP().search(A, B)
print(result)
#assuming at least one match is found
print(A[:result[0]:] + A[result[0]+len(B):])
Output:
[2, 9]
[1, 2, 5, 6, 7, 7, 7, 3, 4]
[Finished in 0.201s]
PS: You can try other algorithms also. And #Pault 's answers is good enough unless you care about the performance a lot.
Here is another approach:
# Returns that starting and ending point (index) of the sublist, if it exists, otherwise 'None'.
def findSublist(subList, inList):
subListLength = len(subList)
for i in range(len(inList)-subListLength):
if subList == inList[i:i+subListLength]:
return (i, i+subListLength)
return None
# Removes the sublist, if it exists and returns a new list, otherwise returns the old list.
def removeSublistFromList(subList, inList):
indices = findSublist(subList, inList)
if not indices is None:
return inList[0:indices[0]] + inList[indices[1]:]
else:
return inList
A = [1, 2, 3, 4, 5, 6, 7, 7, 7]
s1 = [3,4]
B = removeSublistFromList(s1, A)
print(B)
s2 = [4,3]
C = removeSublistFromList(s2, A)
print(C)
For two lists I want
A = [ 1,2,3,4,5]
B = [4,5,6,7]
result
C = [1,2,3,4,5,6,7]
if I specify an overlap of 2.
Code so far:
concat_list = []
word_overlap = 2
for lst in [lst1, lst2, lst3]:
if (len(concat_list) != 0):
if (concat_list[-word_overlap:] != lst[:word_overlap]):
concat_list += lst
elif ([concat_list[-word_overlap:]] == lst[:word_overlap]):
raise SystemExit
else:
concat_list += lst
doing it for lists of strings, but should be the same thing.
EDIT:
What I want my code to do is, first, check if there is any overlap (of 1, of 2, etc), then concatenate lists, eliminating the overlap (so I don't get double elements).
[1,2,3,4,5] + [4,5,6,7] = [1,2,3,4,5,6,7]
but
[1,2,3] + [4,5,6] = [1,2,3,4,5,6]
I want it to also check for any overlap smaller than my set word_overlap.
Here's a naïve variant:
def concat_nooverlap(a,b):
maxoverlap=min(len(a),len(b))
for overlap in range(maxoverlap,-1,-1):
# Check for longest possible overlap first
if a[-overlap:]==b[:overlap]:
break # Found an overlap, don't check any shorter
return a+b[overlap:]
It would be more efficient with types that support slicing by reference, such as buffers or numpy arrays.
One quite odd thing this does is, upon reaching overlap=0, it compares the entirety of a (sliced, which is a copy for a list) with an empty slice of b. That comparison will fail unless they were empty, but it still leaves overlap=0, so the return value is correct. We can handle this case specifically with a slight rewrite:
def concat_nooverlap(a,b):
maxoverlap=min(len(a),len(b))
for overlap in range(maxoverlap,0,-1):
# Check for longest possible overlap first
if a[-overlap:]==b[:overlap]:
return a+b[overlap:]
else:
return a+b
You can use set and union
s.union(t): new set with elements from both s and t
>> list(set(A) | set(B))
[1, 2, 3, 4, 5, 6, 7]
But you can't have the exact number you need to overlap this way.
To answer you question, you will have to ruse and use a combination of sets:
get a new list with elements from both A and B
get new list with elements common to A and B
get only the number of elements you need in this list using slicing
get new list with elements in either A or B but not both
OVERLAP = 1
A = [1, 2, 3, 4, 5]
B = [4, 5, 6, 7]
C = list(set(A) | set(B)) # [1, 2, 3, 4, 5, 6, 7]
D = list(set(A) & set(B)) # [4, 5]
D = D[OVERLAP:] # [5]
print list(set(C) ^ set(D)) # [1, 2, 3, 4, 6, 7]
just for fun, a one-liner could give this:
list((set(A) | set(B)) ^ set(list(set(A) & set(B))[OVERLAP:])) # [1, 2, 3, 4, 6, 7]
Where OVERLAP is the constant where you need you reunion.
Not sure if I correctly interpreted your question, but you could do it like this:
A = [ 1,2,3,4,5]
B = [4,5,6,7]
overlap = 2
print A[0:-overlap] + B
If you want to make sure they have the same value, your check could be along the lines of:
if(A[-overlap:] == B[:overlap]):
print A[0:-overlap] + B
else:
print "error"
assuming that both lists will be consecutive, and list a will always have smaller values than list b. I come up with this solution.
This will also help you detect overlap.
def concatenate_list(a,b):
max_a = a[len(a)-1]
min_b = b[0]
if max_a >= min_b:
print 'overlap exists'
b = b[(max_a - min_b) + 1:]
else:
print 'no overlap'
return a + b
For strings you can do this also
def concatenate_list_strings(a,b):
count = 0
for i in xrange(min(len(a),len(b))):
max_a = a[len(a) - 1 - count:]
min_b = b[0:count+1]
if max_a == min_b:
b = b[count +1:]
return 'overlap count ' + str(count), a+b
count += 1
return a + b
I have these two arrays:
A = [1,2,3,4,5,6,7,8,9,0]
And:
B = [4,5,6,7]
Is there a way to check if B is a sublist in A with the same exact order of items?
issubset should help you
set(B).issubset(set(A))
e.g.:
>>> A= [1,2,3,4]
>>> B= [2,3]
>>> set(B).issubset(set(A))
True
edit: wrong, this solution does not imply the order of the elements!
How about this:
A = [1,2,3,4,5,6,7,8,9,0]
B = [4,5,6,7]
C = [7,8,9,0]
D = [4,6,7,5]
def is_slice_in_list(s,l):
len_s = len(s) #so we don't recompute length of s on every iteration
return any(s == l[i:len_s+i] for i in xrange(len(l) - len_s+1))
Result:
>>> is_slice_in_list(B,A)
True
>>> is_slice_in_list(C,A)
True
>>> is_slice_in_list(D,A)
False
Using slicing:
for i in range(len(A) - len(B)):
if A[i:i+len(B)] == B:
return True
return False
Something like that will work if your A is larger than B.
I prefer to use index to identify the starting point. With this small example, it is faster than the iterative solutions:
def foo(A,B):
n=-1
while True:
try:
n = A.index(B[0],n+1)
except ValueError:
return False
if A[n:n+len(B)]==B:
return True
Times with this are fairly constant regardless of B (long, short, present or not). Times for the iterative solutions vary with where B starts.
To make this more robust I've tested against
A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
which is longer, and repeats values.
You can use scipy.linalg.hankel to create all the sub-arrays in one line and then check if your array is in there. A quick example is as follows:
from scipy import linalg
A = [1,2,3,4,5,6,7,8,9,0]
B = [4,5,6,7]
hankel_mat = linalg.hankel(A, A[::-1][:len(B)])[:-1*len(B)+1] # Creating a matrix with a shift of 1 between rows, with len(B) columns
B in hankel_mat # Should return True if B exists in the same order inside A
This will not work if B is longer than A, but in that case I believe there is no point in checking :)
import array
def Check_array(c):
count = 0
count2 = 0
a = array.array('i',[4, 11, 20, -4, -3, 11, 3, 0, 50]);
b = array.array('i', [20, -3, 0]);
for i in range(0,len(b)):
for j in range(count2,len(a)):
if a[j]==b[i]:
count = count + 1
count2 = j
break
if count == len(b):
return bool (True);
else:
return bool (False);
res = Check_array(8)
print(res)
By subverting your list to string, you can easily verify if the string "4567" is in the string "1234567890".
stringA = ''.join([str(_) for _ in A])
stringB = ''.join([str(_) for _ in B])
stringB in stringA
>>> True
Dressed as a one line (cause is cooler)
isBinA = ''.join([str(_) for _ in B]) in ''.join([str(_) for _ in A])
isBinA
>>> True
A = [1,2,3,4,5,6,7,8,9,0]
B = [4,5,6,7]
(A and B) == B
True