How to move element around element in a string - python

I want a function that moves the last element in str_a around the string until the end. (In this case: 'adbefc') and then the second last element in str_1 moves one, then the last element moves till the end again. I want to move all the elements in str_a except for the first element.
def arrange(str_a, str_b): #You don't need to use this variable
#TODO
>>> arrange('abc', 'def') #Example 1
['abcdef', 'abdcef', 'abdecf', 'abdefc', 'adbcef', 'adbecf', 'adbefc', 'adebcf', 'adebfc', 'adefbc']
>>> arrange('OXY', 'OOO') #Example 1a (Better insight)
['OXYOOO', 'OXOYOO', 'OXOOYO', 'OXOOOY', 'OOXYOO', 'OOXOYO', 'OOXOOY', 'OOOXYO', 'OOOXOY', 'OOOOXY']
>>> arrange('ab', 'cd') #Example 2
['abcd', 'acbd', 'acdb']
Here's what I have done, it only works with a 2 character string. Hopefully, this will give some idea of what I'm trying to accomplish
def arrange(str_a, str_b, idx = 0, lst = []):
idx += 1
length = len(str_a + str_b)
if idx == length - 1:
return lst
str_full = str_a + str_b
list_full = list(str_full)
elem = list_full[idx]
list_full[idx] = list_full[idx + 1]
list_full[idx + 1] = elem
lst.append(''.join(list_full))
middle_index = int(length/2)
str_a = ''.join(list_full[:middle_index])
str_b = ''.join(list_full[middle_index:])
return arrange(str_a, str_b, original, idx, rev,lst)
if __name__ == '__main__':
result = arrange('ab', 'cd')
print(result)```

itertools.combinations is your friend.
import itertools
def arrange_helper(str1, str2):
m, n = len(str1), len(str2)
for indices in itertools.combinations(range(1, m+n), m-1):
characters = [None] * (m+n)
characters[0] = str1[0]
for index, character in zip(indices, str1[1:]):
characters[index] = character
it = iter(str2)
for index, character in enumerate(characters):
if character is None:
characters[index] = next(it)
yield ''.join(characters)
def arrange(str1, str2):
return list(arrange_helper(str1, str2))
Examples:
>>> arrange('123', '456')
['123456', '124356', '124536', '124563', '142356', '142536', '142563', '145236', '145263', '145623']
>>> arrange('OXY', 'OOO')
['OXYOOO', 'OXOYOO', 'OXOOYO', 'OXOOOY', 'OOXYOO', 'OOXOYO', 'OOXOOY', 'OOOXYO', 'OOOXOY', 'OOOOXY']

Related

Python Inserting a string

I need to insert a string (character by character) into another string at every 3rd position
For example:- string_1:-wwwaabkccgkll
String_2:- toadhp
Now I need to insert string2 char by char into string1 at every third position
So the output must be wwtaaobkaccdgkhllp
Need in Python.. even Java is ok
So i tried this
Test_str="hiimdumbiknow"
challenge="toadh"
new_st=challenge [k]
Last=list(test_str)
K=0
For i in range(Len(test_str)):
if(i%3==0):
last.insert(i,new_st)
K+=1
and the output i get
thitimtdutmbtiknow
You can split test_str into sub-strings to length 2, and then iterate merging them with challenge:
def concat3(test_str, challenge):
chunks = [test_str[i:i+2] for i in range(0,len(test_str),2)]
result = []
i = j = 0
while i<len(chunks) or j<len(challenge):
if i<len(chunks):
result.append(chunks[i])
i += 1
if j<len(challenge):
result.append(challenge[j])
j += 1
return ''.join(result)
test_str = "hiimdumbiknow"
challenge = "toadh"
print(concat3(test_str, challenge))
# hitimoduambdikhnow
This method works even if the lengths of test_str and challenge are mismatching. (The remaining characters in the longest string will be appended at the end.)
You can split Test_str in to groups of two letters and then re-join with each letter from challenge in between as follows;
import itertools
print(''.join(f'{two}{letter}' for two, letter in itertools.zip_longest([Test_str[i:i+2] for i in range(0,len(Test_str),2)], challenge, fillvalue='')))
Output:
hitimoduambdikhnow
*edited to split in to groups of two rather than three as originally posted
you can try this, make an iter above the second string and iterate over the first one and select which character should be part of the final string according the position
def add3(s1, s2):
def n():
try:
k = iter(s2)
for i,j in enumerate(s1):
yield (j if (i==0 or (i+1)%3) else next(k))
except:
try:
yield s1[i+1:]
except:
pass
return ''.join(n())
def insertstring(test_str,challenge):
result = ''
x = [x for x in test_str]
y = [y for y in challenge]
j = 0
for i in range(len(x)):
if i % 2 != 0 or i == 0:
result += x[i]
else:
if j < 5:
result += y[j]
result += x[i]
j += 1
get_last_element = x[-1]
return result + get_last_element
print(insertstring(test_str,challenge))
#output: hitimoduambdikhnow

How can you delete similar characters at the same positions in 2 strings

I need to figure out a way to delete common characters from two strings if the common characters are in the same position, but it is not working and I am trying to figure this out. This is what I tried so far, it works for some strings, but as soon as the second string is larger than the first, it stops working. EDIT: I also need a way to store the result in a variable before printing it as I need to use it in another function.
Example :
ABCDEF and ABLDKG would result in the "ABD" parts of both strings to be deleted, but the rest of the string would remain the same
CEF and LKG would be the output
def compare(input1,input2):
if len(input1) < len(input2):
for i in input1:
posi = int(input1.find(i))
if input1[num] == input2[num]:
x = input1.replace(i,"" )
y = input2.replace(i,"" )
num = num+1
print(x)
print(y)
else:
for i in input2:
num = 0
posi = int(input2.find(i))
if input2[num] == input1[num]:
input1 = input1[0:num] + input1[num+1:(len(input1)+ 1 )] # input1.replace(i,"" )
input2 = input2[0:num] + input2[num+1:(len(input1) + 1)]
x = input1
y = input2
num = num + 1
print(str(x))
print(str(y))
you could use
from itertools import zip_longest
a,b = "ABCDEF","ABLDKG"
[''.join(k) for k in zip(*[i for i in zip_longest(a, b, fillvalue = "") if i[0]!=i[1]])]
['CEF', 'LKG']
You can wrap this in a function:
def compare(a, b):
s = zip(*[i for i in zip_longest(a, b, fillvalue = "") if i[0]!=i[1]])
return [''.join(k) for k in s]
compare("ABCDEF","ABLDKG")
['CEF', 'LKG']
compare('asdfq', 'aqdexyz')
['sfq', 'qexyz']
strlist = ["ABCDEF","ABLDKG"]
char_dict = dict()
for item in strlist:
for char in item:
char_dict[char] = char_dict.get(char,0) + 1
new_strlist = []
for item in strlist:
new_strlist.append(''.join([char for char in item if char_dict[char] < 2]))
Note that this will convert strings that have only duplicates into empty strings rather than removing them altogether.

Print letters in specific pattern in Python

I have the follwing string and I split it:
>>> st = '%2g%k%3p'
>>> l = filter(None, st.split('%'))
>>> print l
['2g', 'k', '3p']
Now I want to print the g letter two times, the k letter one time and the p letter three times:
ggkppp
How is it possible?
You could use generator with isdigit() to check wheter your first symbol is digit or not and then return following string with appropriate count. Then you could use join to get your output:
''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in l)
Demonstration:
In [70]: [i[1:]*int(i[0]) if i[0].isdigit() else i for i in l ]
Out[70]: ['gg', 'k', 'ppp']
In [71]: ''.join(i[1:]*int(i[0]) if i[0].isdigit() else i for i in l)
Out[71]: 'ggkppp'
EDIT
Using re module when first number is with several digits:
''.join(re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1)) if re.search('(\d+)(\w+)', i) else i for i in l)
Example:
In [144]: l = ['12g', '2kd', 'h', '3p']
In [145]: ''.join(re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1)) if re.search('(\d+)(\w+)', i) else i for i in l)
Out[145]: 'ggggggggggggkdkdhppp'
EDIT2
For your input like:
st = '%2g_%3k%3p'
You could replace _ with empty string and then add _ to the end if the work from list endswith the _ symbol:
st = '%2g_%3k%3p'
l = list(filter(None, st.split('%')))
''.join((re.search('(\d+)(\w+)', i).group(2)*int(re.search('(\d+)(\w+)', i).group(1))).replace("_", "") + '_' * i.endswith('_') if re.search('(\d+)(\w+)', i) else i for i in l)
Output:
'gg_kkkppp'
EDIT3
Solution without re module but with usual loops working for 2 digits. You could define functions:
def add_str(ind, st):
if not st.endswith('_'):
return st[ind:] * int(st[:ind])
else:
return st[ind:-1] * int(st[:ind]) + '_'
def collect(l):
final_str = ''
for i in l:
if i[0].isdigit():
if i[1].isdigit():
final_str += add_str(2, i)
else:
final_str += add_str(1, i)
else:
final_str += i
return final_str
And then use them as:
l = ['12g_', '3k', '3p']
print(collect(l))
gggggggggggg_kkkppp
One-liner Regex way:
>>> import re
>>> st = '%2g%k%3p'
>>> re.sub(r'%|(\d*)(\w+)', lambda m: int(m.group(1))*m.group(2) if m.group(1) else m.group(2), st)
'ggkppp'
%|(\d*)(\w+) regex matches all % and captures zero or moredigit present before any word character into one group and the following word characters into another group. On replacement all the matched chars should be replaced with the value given in the replacement part. So this should loose % character.
or
>>> re.sub(r'%(\d*)(\w+)', lambda m: int(m.group(1))*m.group(2) if m.group(1) else m.group(2), st)
'ggkppp'
Assumes you are always printing single letter, but preceding number may be longer than single digit in base 10.
seq = ['2g', 'k', '3p']
result = ''.join(int(s[:-1] or 1) * s[-1] for s in seq)
assert result == "ggkppp"
LATE FOR THE SHOW BUT READY TO GO
Another way, is to define your function which converts nC into CCCC...C (ntimes), then pass it to a map to apply it on every element of the list l coming from the split over %, the finally join them all, as follows:
>>> def f(s):
x = 0
if s:
if len(s) == 1:
out = s
else:
for i in s:
if i.isdigit():
x = x*10 + int(i)
out = x*s[-1]
else:
out = ''
return out
>>> st
'%4g%10k%p'
>>> ''.join(map(f, st.split('%')))
'ggggkkkkkkkkkkp'
>>> st = '%2g%k%3p'
>>> ''.join(map(f, st.split('%')))
'ggkppp'
Or if you want to put all of these into one single function definition:
>>> def f(s):
out = ''
if s:
l = filter(None, s.split('%'))
for item in l:
x = 0
if len(item) == 1:
repl = item
else:
for c in item:
if c.isdigit():
x = x*10 + int(c)
repl = x*item[-1]
out += repl
return out
>>> st
'%2g%k%3p'
>>> f(st)
'ggkppp'
>>>
>>> st = '%4g%10k%p'
>>>
>>> f(st)
'ggggkkkkkkkkkkp'
>>> st = '%4g%101k%2p'
>>> f(st)
'ggggkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkpp'
>>> len(f(st))
107
EDIT :
In case of the presence of _ where the OP does not want this character to be repeated, then the best way in my opinion is to go with re.sub, it will make things easier, this way:
>>> def f(s):
pat = re.compile(r'%(\d*)([a-zA-Z]+)')
out = pat.sub(lambda m:int(m.group(1))*m.group(2) if m.group(1) else m.group(2), s)
return out
>>> st = '%4g_%12k%p__%m'
>>> f(st)
'gggg_kkkkkkkkkkkkp__m'
Loop the list, check first entry for number, and then append the second digit onwards:
string=''
l = ['2g', 'k', '3p']
for entry in l:
if len(entry) ==1:
string += (entry)
else:
number = int(entry[0])
for i in range(number):
string += (entry[1:])

How to split a string by a list of marks in python?

Suppose we have a sentence like: "ABCDEFG", and a list of marks like: [0,0,1,0,0,0,1]. What I intends to do is split the source string into segments by using the list of marks: if a character has index i in the source string, and by using this index we could get 1 in the list of marks, then this character is the end of a word.
So the source string could be split into ['ABC', 'DEFG']
How to achieve this in Python? I mean, not the simple way like using a temporary buffer.
A simple approach would be:
temp = "ABCDEFG"
t = [0,0,1,0,0,0,1]
f_i, e_i = 0,0
for index,val in enumerate(t):
if val:
e_i = index +1
print temp[f_i: e_i ] #Here you can store these as you wish
f_i = e_i
.index is probably the fastest way to find the 1s. Also allows you to slice the source string directly
s = "ABCDEFG"
L = [0,0,1,0,0,0,1]
pos = 0
res = []
while True:
try:
idx = L.index(1, pos) + 1
except ValueError:
break
res.append(s[pos: idx])
pos = idx
print(res)
We can use simple for loop to acheive this -
>>> s = "ABCDEFG"
>>> l = [0,0,1,0,0,0,1]
>>>
>>> endlist = []
>>> tsh = ''
>>> for i, ch in enumerate(s):
... tsh += ch
... if l[i] == 1:
... endlist.append(tsh)
... tsh = ''
...
>>> endlist
['ABC', 'DEFG']
A simple answer:
l = [0,0,1,0,0,0,1]
s = 'ABCDEFG'
indices = [i for i, x in enumerate(l) if x == 1]
t = 0
a = []
for i in indices:
a.append(s[t:i+1])
t = i+1
print(a)
Another variation on a theme.
marks = [0,0,1,0,0,0,1]
sentence = 'ABCDEFG'
output = []
last_index = 0
for index, mark in enumerate(marks):
if mark:
output.append(sentence[last_index:index+1])
last_index = index+1
print(output)

Finding two or more characters of the same kind in a string?

I am working on a piece of code that requires to find certain characters in a word and then replace those characters in a generated string. The code works fine when the word has only one of each character; however, when I have two or more characters of the same kind, the code only identifies the first one and ignores the following ones. Do you have any suggestions on how to solve this issue?
def write_words (word, al):
newal = (list(al))
n = len(word)
i = 0
x = 0
a = []
b = ["_"]
for i in range(0, n):
a = a + b
while (x <(len(newal))):
z = newal[x]
y = word.find(z)
x = x + 1
print (y)
if y >= 0:
a[y] = z
return(a)
(The Python version I'm working with is 3.2.1)
The problem here is that find() returns the index of the first occurrence of the element.
You can just use the following code to replace the occurrences instead.
>>> word = 'abcdabcd'
>>> ignore = 'ab'
>>> "".join([elem if elem not in ignore else '_' for elem in word])
'__cd__cd'
P.S - Some pointers on your current code.
def write_words (word, al):
newal = (list(al))
n = len(word)
i = 0
x = 0
a = []
b = ["_"]
for i in range(0, n):
a = a + b
while (x <(len(newal))):
z = newal[x]
y = word.find(z)
x = x + 1
print (y)
if y >= 0:
a[y] = z
return(a)
Instead of doing a for loop and appending _ at every element in a, you could have just done a = ['_']*len(word).
You don't need a while loop here or converting your word to a list. Strings are iterable, so you could just do for elem in newal. That way you don't have to keep a seperate x variable to iterate over the string.
So, now your code gets reduced to
>>> def write_words_two(word, al):
a = ['_']*len(word)
for elem in al:
y = word.find(elem)
print(y)
a[y] = z
return a
But, it still has the same problem as before. The problem now seems to be that word.find(elem) only returns the occurrence of the first character and not the indices of occurrences of all of them. So, instead of building up a list first and then replacing the characters, we should build up the list as we go along and test every character for our ignored characters, and if the character needs to be ignored, we just replace that with it's replacement in the list. Then, we come up with the following code
>>> def write_words_three(word, al, ignore):
a = []
for elem in word:
if elem in al:
a.append(ignore)
else:
a.append(elem)
return a
>>> write_words_three('abcdabcd', 'ab', '_')
['_', '_', 'c', 'd', '_', '_', 'c', 'd']
But, it still seems to return the list and not the string and that's what we want, and it seems a little big too. So, why not shorten it with a list comprehension?
>>> def write_words_four(word, al, ignore):
return [elem if elem not in al else ignore for elem in word]
>>> write_words_threefour('abcdabcd', 'ab', '_')
['_', '_', 'c', 'd', '_', '_', 'c', 'd']
We still need a string out of this though and our code just returns a list. We can use the join(...) method for that and join each element of the string.
>>> def write_words_five(word, al, ignore):
return "".join([elem if elem not in al else ignore for elem in word])
>>> write_words_five('abcdabcd', 'ab', '_')
'__cd__cd'
which gives us what we want.
Replace your find function by this one:
def myfind(main, x):
return [i for i,j in enumerate(x) if j==x]
so that in your code:
ys = myfind( word, z )
for y in ys:
a[y] = z
This should do what the OP asked, with minimal change to the original code. Does not work if "_" is an allowed character in al.
def write_words (word, al):
newal = (list(al))
n = len(word)
i = 0
x = 0
a = []
b = ["_"]
for i in range(0, n):
a = a + b
while (x <(len(newal))):
z = newal[x]
y = word.find(z)
while (y >= 0):
print (y)
a[y] = z
word[y] = "_"
y = word.find(z)
x = x + 1
return a

Categories

Resources