Insert "X" between identical consecutive letters in a string - python

Given a string, how can I break it up such that there are no consecutive identical letters, at n, n+1, where n is even.
Meaning, how can i get "abba" to remain "abba", but take "abbb" into "abbXb".
Thanks

Because everyone loves one-liners:
strings = ['ab', 'abba', 'abbb', 'abbba', 'abbababababbbaaaa', 'abcacbbbddbabbdd']
for s in strings:
r = ''.join('X' + v if (k and k % 2 and v == s[k - 1]) else v for (k,v) in enumerate(s))
print s, '->', r
The code reads like this: look at every character in the string. If it's not the first and if it's index is even and it is the same as the character before, prepend an 'X' to the character.
Output:
ab -> ab
abba -> abba
abbb -> abbXb
abbba -> abbXba
abbababababbbaaaa -> abbababababXbbaaXaa
abcacbbbddbabbdd -> abcacbbXbdXdbabXbdXd

Do your own homework, Kevin.
def foo(text, separator):
if len(text) < 2:
return text
result = ""
for i in range(1, len(text), 2):
if text[i] == text[i - 1]:
result += text[i - 1] + separator + text[i]
else:
result += text[i-1:i+1]
if len(text) % 2 != 0:
result += text[-1]
return result
print(foo("ab", "X"))
print(foo("abba", "X"))
print(foo("abbba", "X"))
print(foo("abbababababbbaaaa", "Z"))
Output:
>> ab
>> abba
>> abbXba
>> abbababababZbbaaZaa

You can use itertools.groupby:
from itertools import islice, groupby
import math
def solve(strs, n):
for k, g in groupby(strs):
lis = list(g)
it = iter(lis)
yield 'X'.join(''.join(islice(it, n)) for _ in xrange(int(math.ceil(len(lis)/float(n)))))
Demo:
>>> ''.join(solve("abba", 2))
'abba'
>>> ''.join(solve("abbb", 2))
'abbXb'
>>> ''.join(list(solve('abbbbyyyyy', 2)))
'abbXbbyyXyyXy'
>>> ''.join(solve('abbbbyyyyy', 4))
'abbbbyyyyXy'

May it be good?
from itertools import izip_longest
def X(s):
s_odd = s[::2]
s_even = s[1::2]
output = ''
for o, e in izip_longest(s_odd, s_even):
output += o or ''
if o == e:
output += 'X'
output += e or ''
return output
strings = ['ab', 'abba', 'abbb', 'abbba', 'abbababababbbaaaa', 'abcacbbbddbabbdd']
for s in strings:
print X(s)
result:
ab
abba
abbXb
abbXba
abbababababXbbaaXaa
abcacbbXbdXdbabXbdXd
EDIT
A simpler version:
def X(s):
output = ''
for i in range(0, len(s), 2):
o = s[i]
e = s[i+1] if i < len(s) - 1 else ''
output += o
if o == e:
output += 'X'
output += e
return output
strings = ['ab', 'abba', 'abbb', 'abbba', 'abbababababbbaaaa', 'abcacbbbddbabbdd']
for s in strings:
print X(s)

Related

How to move element around element in a string

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']

trying to reverse the string with "_" fixed at point

def r(s):
str = []
for i in len(s):
if (s[i]=='_'):
str = s[i] + str
continue
str = s[i] + str
return str
I tried using the above code to convert the following string
Input: ab_cde
Expected Output: ed_cba
s = 'ab_cde'
out = ''
for a, b in zip(s, s[::-1]):
if b != '_' and a != '_':
out += b
else:
out += a
print(out)
Prints:
ed_cba
EDIT: For more fixed points:
s = 'ab_cde_f_ghijk_l'
i, out = iter(ch for ch in s[::-1] if ch != '_'), ''
out = ''.join(ch if ch == '_' else next(i) for ch in s)
print(out)
Prints:
lk_jih_g_fedcb_a
The main idea is to check all the positions of the underscore _, save them and reverse the string without them, to insert them again after reversing.
import re
def r(s):
# check where all the underscore are
underscore_positions = [m.start() for m in re.finditer('_', s)]
# get list of reversed chars without underscores
reversed_chars = [c for c in reversed(s) if c != '_']
# put underscore back where they where
for p in underscore_positions:
reversed_chars.insert(p, '_')
# profit
return "".join(reversed_chars)
The function can be modified to have a different fixed character.
I also uses the package re for the regex function to identify the _, you can do with a simple loop as underscore_positions = [i for i, c in enumerate(s) if c =='_'] if you prefer.
def fixed_reverse(s, ch):
idxs = [-1] + [i for i, x in enumerate(s) if x == ch] + [len(s)]
idxs = [x - i + 1 for i, x in enumerate(idxs)]
chars = "".join(x for x in s if x != ch)[::-1]
return ch.join(chars[a:b] for a, b in zip(idxs[:-1], idxs[1:]))
>>> fixed_reverse("ab_cde_f_ghijk_l", "_")
'lk_jih_g_fedcb_a'
This works by:
Storing the locations of the fixed-point character "_".
Reversing the string with the "_" characters removed.
Inserting the "_" back into the correct locations.

Combine elements that has same pattern in a list to strings

I have a list of strings that could vary in pattern.
lst = ['ban-eur.kd', 'ban-eur.sd', 'ban-eur.td' ]
When converted this should become ban-eur<kd,sd,td>.
It should combine elements that are next only if they can be combined. (only if it matches the pattern ban-eur)
lst = ['ban-eur.kd', 'kd', 'ban-eur.sd', 'ban-eur.td' ]
This should result in 'ban-eur.kd_kd_ban-eur<sd,td>'.
If it doesn't have any element that could be combined then it should all be just joined with a _
How can I do this, without missing the first element in the array/duplicate with in the string.
Thanks for your time.
You can use itertools.groupby:
import itertools, re
lst = ['ban-eur.kd', 'ban-eur.sd', 'ban-eur.td' ]
def group_result(d:list) -> list:
if len(d) == 1:
return d[0]
new_result = [[a, list(b)] for a, b in itertools.groupby(sorted(d, key=lambda x:x.split('.')[0]), key=lambda x:x.split('.')[0])]
return '_'.join('{}<{}>'.format(a, ','.join(i.split('.')[-1] for i in b)) for a, b in new_result)
new_data = '_'.join(group_result(list(b)) if a else '_'.join(list(b)) for a, b in itertools.groupby(lst, key=lambda x:'.' in x))
Output:
'ban-eur<kd,sd,td>'
When running on ['ban-eur.kd', 'kd', 'ban-eur.sd', 'ban-eur.td']:
'ban-eur.kd_kd_ban-eur<sd,td>'
matches = []
resulting_string
for item in lst:
if item.startsWith('ban-eur'):
matches.append(item)
elif not item.startsWith('ban-eur') and len(matches) >= 1:
if len(matches) == 1:
resulting_string += item
else:
resulting_string += 'ban-eur.<'
for s in matches:
resulting_string += s + ', '
resulting_string += '>'
matches = []
resulting_string += '_' + item + '_'
This should work
pre = ''
result = []
endings = []
for item in lst:
if item.split('.')[0] == pre:
endings.append(item.split('.')[1])
else:
if endings:
result.append(pre+'<'+','.join(endings)+'>')
else:
result.append(item)
pre = item.split('.')[0]
endings = []
print('_'.join(result))

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:])

Convert numbers into corresponding letter using Python

I was wondering if it is possible to convert numbers into their corresponding alphabetical value. So
1 -> a
2 -> b
I was planning to make a program which lists all the alphabetical combinations possible for a length specified by a user.
See I know how to build the rest of the program except this!
Any help would be wonderful.
Big Letter:
chr(ord('#')+number)
1 -> A
2 -> B
...
Small Letter:
chr(ord('`')+number)
1 -> a
2 -> b
...
import string
for x, y in zip(range(1, 27), string.ascii_lowercase):
print(x, y)
or
import string
for x, y in enumerate(string.ascii_lowercase, 1):
print(x, y)
or
for x, y in ((x + 1, chr(ord('a') + x)) for x in range(26)):
print(x, y)
All of the solutions above output lowercase letters from English alphabet along with their position:
1 a
...
26 z
You'd create a dictionary to access letters (values) by their position (keys) easily. For example:
import string
d = dict(enumerate(string.ascii_lowercase, 1))
print(d[3]) # c
You can use chr() to turn numbers into characters, but you need to use a higher starting point as there are several other characters in the ASCII table first.
Use ord('a') - 1 as a starting point:
start = ord('a') - 1
a = chr(start + 1)
Demo:
>>> start = ord('a') - 1
>>> a = chr(start + 1)
>>> a
'a'
Another alternative is to use the string.ascii_lowercase constant as a sequence, but you need to start indexing from zero:
import string
a = string.ascii_lowercase[0]
What about a dictionary?
>>> import string
>>> num2alpha = dict(zip(range(1, 27), string.ascii_lowercase))
>>> num2alpha[2]
b
>>> num2alpha[25]
y
But don't go over 26:
>>> num2alpha[27]
KeyError: 27
But if you are looking for all alphabetical combinations of a given length:
>>> import string
>>> from itertools import combinations_with_replacement as cwr
>>> alphabet = string.ascii_lowercase
>>> length = 2
>>> ["".join(comb) for comb in cwr(alphabet, length)]
['aa', 'ab', ..., 'zz']
Try a dict and some recursion:
def Getletterfromindex(self, num):
#produces a string from numbers so
#1->a
#2->b
#26->z
#27->aa
#28->ab
#52->az
#53->ba
#54->bb
num2alphadict = dict(zip(range(1, 27), string.ascii_lowercase))
outval = ""
numloops = (num-1) //26
if numloops > 0:
outval = outval + self.Getletterfromindex(numloops)
remainder = num % 26
if remainder > 0:
outval = outval + num2alphadict[remainder]
else:
outval = outval + "z"
return outval
Here is a quick solution:
# assumes Python 2.7
OFFSET = ord("a") - 1
def letter(num):
return chr(num + OFFSET)
def letters_sum_to(total):
for i in xrange(1, min(total, 27)):
for rem in letters_sum_to(total - i):
yield [letter(i)] + rem
if total <= 26:
yield [letter(total)]
def main():
for letters in letters_sum_to(8):
print("".join(letters))
if __name__=="__main__":
main()
which produces
aaaaaaaa
aaaaaab
aaaaaba
aaaaac
aaaabaa
aaaabb
aaaaca
aaaad
aaabaaa
# etc
Note that the number of solutions totalling to N is 2**(N-1).
for i in range(0, 100):
mul = 1
n = i
if n >= 26:
n = n-26
mul = 2
print chr(65+n)*mul

Categories

Resources