I want to make a function that will remove '-' from two sequences of char if both contain it. This is my code.
def normalized(seq1, seq2):
x = ''
y = ''
for a, b in zip(seq1, seq2):
if a != '-' and b != '-':
print a,b, 'add'
x += a
y += b
else:
print a, b, 'remove'
return x,y
x = 'ab--dfd--df'
y = 'rt-bfdsu-vf'
print normalized(x, y)
and this is the result.
a r add
b t add
- - remove
- b remove
d f add
f d add
d s add
**- u remove**
- - remove
d v add
f f add
('abdfddf', 'rtfdsvf')
You can see that - and u should not be removed. What's wrong with my code?
You want to use or, not and ...
Another clever way that you could do this is to use operator chaining:
if a == b == '-':
print a,b, 'remove'
else:
print a,b, 'add'
x += a
y += b
This is extremely succint and clear that you want to remove the dashes only if they appear in both strings.
If you only want to remove if both are -, then test for that:
if not (a == '-' and b == '-'):
which can be shortened to:
if not (a == b == '-'):
or use or instead of and to remove the not:
if a != '-' or b != '-':
but that is not as readable.
Perhaps no more readable, but the list comprehension would be:
def normalized(seq1, seq2):
return [''.join(v) for v in zip(*[(a, b)
for a, b in zip(seq1, seq2) if not (a == b == '-')])]
or using map(), sufficient for Python 2:
def normalized(seq1, seq2):
return map(''.join, zip(*[(a, b) for a, b in zip(seq1, seq2) if not (a == b == '-')]))
The condition should be not (a =='-' and b == '-'):
def normalized(seq1, seq2):
x = ''
y = ''
for a, b in zip(seq1, seq2):
if not (a =='-' and b == '-'): # you need `not` here
print a,b, 'add'
x += a
y += b
else:
print a, b, 'remove'
return x,y
x = 'ab--dfd--df'
y = 'rt-bfdsu-vf'
print normalized(x, y)
According to your code, - u shoud be removed.
In fact,
a != '-' and b != '-' is False
the first part is false and the second is true. False and True is False. (Boolean Algebra 101, see http://en.wikipedia.org/wiki/Boolean_algebra#Basic_operations for details)
You are currently asking for the program to match when a and b are not "-". This means that unless both are not equal to "-" then it will go to your else. The code you want is:
def normalized(seq1, seq2):
x = ''
y = ''
for a, b in zip(seq1, seq2):
if a == '-' and b == '-':
print a,b, 'remove'
else:
print a, b, 'add'
x += a
y += b
return x,y
x = 'ab--dfd--df'
y = 'rt-bfdsu-vf'
print normalized(x, y)
Related
What is the most efficient way to find and replace continuing duplicates in the string? I'm trying to make a script that finds continuing duplicates in the string and replaces them.
For example, after removing repeated substring of length 1: “abcababceccced” --> “abcababceced” (2 'c' are removed)
After removing repeated substring of length 2: “abcababceced” --> “abcabceced” (substring “ab” is removed)
and so on...
This is how I tried so far, but it seems not working properly as I wanted...
def f(a, b):
l = a
for j in range(len(a)):
try:
if l[j:j+b] == l[j+b:j+2*b]:
l = l[:j+b] + l[j+2*b:]
else: continue
except:
break
return l, b+1
x = "string"
b = 1
while True:
if b <= int(len(x)/2):
x, b = f(x,b)
else: break
print(x)
Here is a solution to the question. It tries to minimize string slicing (which can be inefficient since it copies the sliced substring) in the search for duplicates by using the find() method of the built-in str data type.
x = "abcababceccced"
print(x)
patLen = 1
while patLen * 2 < len(x):
i = 0
while i + patLen * 2 < len(x):
jPrev, j, pat = i, i + patLen, x[i:i + patLen]
while j + patLen <= len(x) and x.find(pat, j) == j:
jPrev, j = j, j + patLen
if j > i + patLen:
print(f"{' '*i + x[i:j - patLen]} removed")
x = x[:i] + x[j - patLen:]
print(x)
i += 1
patLen += 1
Output:
abcababceccced
cc removed
abcababceced
ab removed
abcabceced
ce removed
abcabced
abc removed
abced
Note that your original code can also be made to work with the following modifications (I added a couple of print statements too, to show what it's doing):
def f(a, b):
l = a
for j in range(len(a) - 2 * b):
try:
while l[j:j+b] == l[j+b:j+2*b]:
print(f"{' '*(j+b) + l[j+b:j+2*b]} removed")
l = l[:j+b] + l[j+2*b:]
else: continue
except:
break
return l, b+1
x = "abcababceccced"
b = 1
while b <= int(len(x) // 2):
print(x)
x, b = f(x,b)
print(x)
Output:
abcababceccced
c removed
c removed
abcababceced
ab removed
ce removed
abcabced
abc removed
abced
I'm trying to compare the position and elements of 3 different lists, to then save them in a new list, if at least 2 of the elements at the same position matched.
For Example:
a = [FF2, FF6, FC4]
b = [FB5, FB3, FC4]
c = [FF2, FB3, FM8]
Result = [FF2, FB3, FC4]
At the beginning I used the following code to compare 2 lists, and tried to adapt it for 3 lists, by adding an extra for loop after the for i1 and also adding an or to my if, but went horribly wrong (almost 10 times more values as expected as output).
for i, v in enumerate(a):
for i1, v1 in enumerate(b):
if (i==i1) & (v==v1):
Result.append(v)
This is my current approach, it's working fine, but I have no idea how can I append the matched value to my Result list.
Result = list(x for x, (xa, xb, xc) in enumerate(zip(a, b, c))
if xa == xb or xb == xc or xa == xc)
al = ['FF2', 'FF6', 'FC4']
bl = ['FB5', 'FB3', 'FC4']
cl = ['FF2', 'FB3', 'FM8']
res = []
for a,b,c in zip(al, bl, cl):
if a == b or b == c or c == a:
if a == b:
res.append(a)
elif b == c:
res.append(b)
elif c == a:
res.append(c)
print(res)
You can iterate through the 3 lists at the same time & append to the resulting list. Use zip()
You could avoid the issues you're having by eliminating the use of enumerate() and just using zip. We then check each unique element in each tuple created by zip and see if the count of that element is greater than 1. If so, we append this to our return list. The use of t.count() resolves the need for the untidy xa == xb or xb == xc or xa == xc condition & makes it easily extendable to n lists.
Code:
a = ['FF2', 'FF6', 'FC4']
b = ['FB5', 'FB3', 'FC4']
c = ['FF2', 'FB3', 'FM8']
r = [x for t in zip(a, b, c) for x in set(t) if t.count(x) > 1]
Output:
>>> r
['FF2', 'FB3', 'FC4']
You could do something like this,
a = ["FF2", "FF6", "FC4"]
b = ["FB5", "FB3", "FC4"]
c = ["FF2", "FB3", "FM8"]
result = []
for idx in range(len(a)):
if a[idx] == b[idx] or a[idx] == c[idx]:
result.append(a[idx])
elif b[idx] == c[idx]:
result.append(b[idx])
else:
pass
print(result)
the output will be,
['FF2', 'FB3', 'FC4']
For example:
a list just like m=[[2,3],[3,4],[1,2],[4,7],[1,7]]
every element is a little list[x,y], x is not equal to y.
if element a and b in m follow: a[1]==b[0], then a and b will "combined into" c=[a[0],b[1]],
after that,a and b will remove from m,
and c will added into m.
of course,length of m reduced by one.
my question is :
after several times combined oprations,
is there same element exist in new m?
if yes, return True else return False.
I write a demo below
from random import choice
def combine_two(a,b):
c = [None,None]
if a[1]==b[0]:
c[0]=a[0]
c[1]=b[1]
elif b[1] == a[0]:
c[0]=b[0]
c[1]=a[1]
return c
def fusion(list):
ss = list[:]
nn = len(ss)
for i in range(100):
ele_1 = choice(ss)
ele_2 = choice(ss)
c = combine_two(ele_1,ele_2)
if c != [None,None]:
ss.remove(ele_1)
ss.remove(ele_2)
ss.append(c)
return ss
def check(list):
n = len(list)
for i in range(n):
for j in range(n):
if i != j:
if list[i][0] == list[j][0] and list[i][1] == list[j][1]:
return False
return True
jj = [[2,3],[3,4],[1,2],[4,7],[11,13],[1,7]]
m = fusion(jj)
print m
n = check(m)
print n
but my fusion function is a workaround.
if the input list is very long. it will go wrong.
I have no idea of how to handle 2 element at a time Intelligently.
is there a better way to achieve this function?
Sort list second element thus can use binary search, after combine, replace one, but should move the other. Codes below:
def combine_two(a ,b):
c = [None ,None]
if b[1] == a[0]:
c[0 ] =b[0]
c[1 ] =a[1]
elif a[1] == b[0]:
c[0 ] =a[0]
c[1 ] =b[1]
return c
def binary_find(val, list):
left = 0
right = len(list)
while right - left >= 0:
mid = (left + right) / 2
if list[mid][1] > val:
right = mid - 1
elif list[mid][1] < val:
left = mid + 1
else:
return mid
return -1
def fusion_new(list):
src = list[:]
src.sort(lambda x, y: x[1] - y[1] if x[1] != y[1] else x[0] - y[0])
while True:
i = 0
merge_num = 0
while i < len(src):
elem_1 = src[i]
ref_val = elem_1[0]
##find the match case in src
index = binary_find(ref_val, src)
if index >= 0 and index != i:
elem_2 = src[index]
c = combine_two(elem_1, elem_2)
src[i] = c
src.pop(index)
merge_num += 1
else:
i += 1
if merge_num < 1:
break
return src
def check_new(list):
temp = list[:]
temp.sort(lambda x, y: x[0] - y[0] if x[0] != y[0] else x[1] - y[1])
for i in range(len(temp)-1):
if temp[i] == temp[i+1]:
return False
return True
jj = [[2 ,3] ,[3 ,4] ,[1 ,2] ,[4 ,7] ,[11 ,13] ,[1 ,7]]
m = fusion_new(jj)
print m
n = check_new(m)
print n
I am given 3 int, a, b, c. I would like to find the sum of all three int provided that they are unique. If a, b, or c has the same values as any of the other values, then they don't count towards the sum.
Example 1:
a = 3, b = 3, c =3
sum = 0
Example 2
a = 1, b = 3, c =3
sum = 1
This is what I have done. Is there a more pythonic way of doing this without so many if else statements?
def lone_sum(a, b, c):
if a != b and b != c and a != c:
return a + b + c
elif a == b == c:
return 0
elif a == b:
return c
elif b == c:
return a
elif a == c:
return b
from collections import Counter
def lone_sum(a, b, c):
d = Counter([a, b, c])
return sum(k for k in d if d[k]==1)
Add any number of numbers:
def lone_sum(*L):
d = Counter(L)
return sum(k for k in d if d[k]==1)
Add numbers repeated exactly c times:
def rep_sum(c, *L):
d = Counter(L)
return sum(k for k in d if d[k]==c)
Add numbers repeated at most c times:
def rep_sum(c, *L):
d = Counter(L)
return sum(k for k in d if d[k]<=c)
... or if you're bored and want to get really creative:
def lone_sum(*L):
nums = set()
all_nums = set()
for num in L:
if num in nums:
nums.remove(num)
elif num not in all_nums:
all_nums.add(num)
nums.add(num)
return sum(nums)
Here is a good beginners way to solve it
def lone_sum(*args):
return sum(x for x in args if args.count(x) == 1)
The issue with this is that args.count is a hidden loop, so the calculation becomes O(n2)
This doesn't matter much if there are only ever 3 arguments - ie n == 3.
A longhand way to write the same thing is
def lone_sum(a, b, c):
args = (a, b, c)
s = 0
for x in args:
if args.count(x) == 1:
s += x
return s
Here I take a list of your numbers, call it x, and then select only those x[i] which are not present in a list which is x without x[i]. That is, it removes all numbers which have a duplicate.
def lone_sum(a, b, c):
x = [a,b,c]
x = [x[i] for i in range(len(x)) if x[i] not in [x[j] for j in range(len(x)) if j!=i]]
return sum(x)
So,
[x[j] for j in range(len(x)) if j!=i]
is basically a list of elements excluding x[i]. It takes all elements apart from ith. If x[i] is in this list, it means it is a duplicate and we need to remove it. That is,
x[i] not in [x[j] for j in range(len(x)) if j!=i]
I think a lot has already been said and done on this topic and there is not much that can be added. But I too have written a program that uses sets to go about this task which is a little different from the top answer. So if you still wish to see all possible programs then here you go. Hope it helps!
def lone_sum(x):
c = set(x)
for b in c:
x.remove(b)
for a in set(x):
if a in c:
c.remove(a)
return(sum(c))
I was doing some practice problems in Coding Bat, and came across this one..
Given 3 int values, a b c, return their sum. However, if one of the values is the same as another of the values, it does not count towards the sum.
lone_sum(1, 2, 3) → 6
lone_sum(3, 2, 3) → 2
lone_sum(3, 3, 3) → 0
My solution was the following.
def lone_sum(a, b, c):
sum = a+b+c
if a == b:
if a == c:
sum -= 3 * a
else:
sum -= 2 * a
elif b == c:
sum -= 2 * b
elif a == c:
sum -= 2 * a
return sum
Is there a more pythonic way of doing this?
Another possibility that works for an arbitrary number of arguments:
from collections import Counter
def lone_sum(*args):
return sum(x for x, c in Counter(args).items() if c == 1)
Note that in Python 2, you should use iteritems to avoid building a temporary list.
How about:
def lone_sum(*args):
return sum(v for v in args if args.count(v) == 1)
A more general solution for any number of arguments is
def lone_sum(*args):
seen = set()
summands = set()
for x in args:
if x not in seen:
summands.add(x)
seen.add(x)
else:
summands.discard(x)
return sum(summands)
Could use a defaultdict to screen out any elements appearing more than once.
from collections import defaultdict
def lone_sum(*args):
d = defaultdict(int)
for x in args:
d[x] += 1
return sum( val for val, apps in d.iteritems() if apps == 1 )
def lone_sum(a, b, c):
z = (a,b,c)
x = []
for item in z:
if z.count(item)==1:
x.append(item)
return sum(x)
Had a very similar approach to what you had:
def lone_sum(a, b, c):
if a != b and b != c and c != a:
return a + b + c
elif a == b == c:
return 0
elif a == b:
return c
elif b == c:
return a
elif c == a:
return b
Since if 2 values are the same the code will automatically return the
remaining value.
I tried this on Codingbat but it doesn`t work, although it does on the code editor.
def lone_sum(a, b, c):
s = set([a,b,c])
return sum(s)