Joining a string in another string - python

I have done this code but the output is not like what I want
def replace(s,p,n):
return "".join("{}".format(p) if not i % n else char for i, char in enumerate(s,1))
print(replace("university","-",3))
the output that I get is un-ve-si-y
I must get it like :
uni-ver-sit-y

This is one approach. using str slicing.
Demo:
def replace(s,p,n):
return p.join([s[i:i+n] for i in range(0, len(s), n)])
print(replace("university","-",3))
Output:
uni-ver-sit-y

If you extend the code out over multiple lines:
chars_to_join = []
for i, char in enumerate(s,1):
if not i % n:
chars_to_join.append("{}".format(p))
else:
chars_to_join.append(char)
You'll see that when the if statement is true it'll just replace the character rather than include the replacement character after the given character, so just modify the format string to include the currently iterated character aswell
"{}{}".format(char, p)

Alternatively you can do it functionally like this:
from itertools import repeat
def take(s, n):
""""take n characters from s"""
return s[:n]
def skip(s, n):
""""skip n characters from s"""
return s[n:]
def replace(s, p, n):
# create intervals at which to prefix
intervals = range(0, len(s), n)
# create the prefix for all chunks
prefix = map(skip, repeat(s), intervals)
# trim prefix for n characters each
chunks = map(take, prefix, repeat(n))
return p.join(chunks)
And now:
replace('university', '-', 3)
Will give you:
'uni-ver-sit-y'
Note: this is sample code, if this is meant to be efficient you probably should use lazy evaluated functions (like islice) which can take a lot less memory for bigger inputs.

For this question, I think the list-comprehension is not a very good idea. It's not clearly understood. Maybe we can make it clearer by following:
def replace(s,p,n):
new_list = []
for i, c in enumerate(s, 1):
new_list.append(c)
if i % n == 0:
new_list.append(p)
return "".join(new_list)
print(replace("university","-",3))

Related

Suggestions for optimization Python

This is a fairly straight forward programming problem in Python and I am looking for suggestions for further optimization. I am successfully processing in time except for very large strings. I am not looking for code rather areas that I should research for optimization improvements. I have already identified that I can skip even numbers reducing the loop operation and given the nature of the operations the pattern eventually repeats which is why I track when repeat occurs. This allows me break out if n > repeat. I am not positive if converting the string to a list is the most effective.
Problem:
We have a string s and we have a number n that indicates the number of times to run the function. Here is a function that takes your string, concatenates the even-indexed chars to the front, odd-indexed chars to the back. You perform this operation n times.
Example:
example where s = "qwertyuio" and n = 2:
after 1 iteration s = "qetuowryi"
after 2 iterations s = "qtorieuwy"
return "qtorieuwy"
def jumbled_string(s, n):
sl = list(s)
repeat = 0
for y in range(0,n):
for i in range(1, (len(sl)//2)+1):
sl.append(sl.pop(i))
if repeat == 0 and ''.join(sl) == s:
repeat = y+1
break
if repeat != 0:
afterrepeat = n%repeat
for y in range(0,afterrepeat):
for i in range(1, (len(sl)//2)+1):
sl.append(sl.pop(i))
return ''.join(sl)
I don't know what you mean by "pattern repeats". But if we stick to the problem statement, it's a one liner in Python:
s='abecidofug'
from itertools import chain
s2 = ''.join(chain([s[c] for c in range(0, len(s), 2)],[s[c] for c in range(1, len(s), 2)]))
s2
'aeioubcdfg'
In python 3.8+ (due to := operator) you could do it like this:
import collections
def jumbled_string(s: str, n: int) -> str:
generator = (s:=s[::2]+s[1::2] for _ in range(n))
collections.deque(generator, maxlen=0)
return s
Using collections.deque as this is the Fastest (most Pythonic) way to consume an iterator.
Though, for small n I'm finding it faster to use:
def jumbled_string(s: str, n: int) -> str:
for _ in (s:=s[::2]+s[1::2] for _ in range(n)):
pass
return s
Test:
jumbled_string("qwertyuio", 2)
Output:
'qtorieuwy'
You don't explain what n does. The statement is this:
def jumbled_string(s: str) -> str:
even = s[::2]
odd = s[1::2]
return even+odd
print(jumbled_string("0123456789"))
>>>0246813579

Python recursive list comprehension to iterative approach

I'm trying to understand how to think a recursive method iteratively. For example, I have the following backtracking method:
def bitStr(n, s):
if n == 1:
return s
return [digit + bits for digit in bitStr(1, s) for bits in bitStr(n - 1, s)]
I'm practicing how to do accomplish a similar iteratively or explicitly using double for-loop.
I started something like this which I understand is incorrect; however, unable to fix it:
def bitStr2(n, s):
if n == 1:
return [c for c in s]
for bits in bitStr2(n - 1, s):
for digit in bitStr2(1, s):
return digit + bits
Thank You
There are two issues in your code.
First, as pointed out by #MisterMiyagi, you switched the loops. In a list comprehension, loops are read from left to right. You should write the regular loops like this:
for digit in bitStr2(1, s):
for bits in bitStr2(n - 1, s):
...
Second, a list comprehension produces... a list. You have to store the elements in a list:
...
result = []
for digit in bitStr2(1, s):
for bits in bitStr2(n - 1, s):
result.append(digit + bits)
return result
(Conversely: never use a list comprehension if you don't want to produce a list.) And you don't have to handle differently the n = 1 case. Full code:
def bitStr2(n, s):
if n == 1:
return s
result = []
for digit in bitStr2(1, s):
for bits in bitStr2(n - 1, s):
result.append(digit + bits)
return result
Note that for digit in bitStr(1, s) is equivalent to for digit in s. I don't see why you call the method bitStr in this case, since you already know the result.

How to generate all strings with d-mismatches, python

I have a following string - "AACCGGTTT" (alphabet is ["A","G","C","T"]). I would like to generate all strings that differ from the original in any two positions i.e.
GAGCGGTTT
^ ^
TATCGGTTT
^ ^
How can I do it in Python?
I have only brute force solution (it is working):
generate all strings on a given alphabet with the same length
append strings that have 2 mismatches with a given string
However, could you suggest more efficient way to do so?
I'd probably go with using itertools. Maybe something like
from itertools import combinations, product
def generate(s, d=2):
N = len(s)
letters = 'ACGT'
pool = list(s)
for indices in combinations(range(N), d):
for replacements in product(letters, repeat=d):
skip = False
for i, a in zip(indices, replacements):
if pool[i] == a: skip = True
if skip: continue
keys = dict(zip(indices, replacements))
yield ''.join([pool[i] if i not in indices else keys[i]
for i in range(N)])
Then just
list(generate("AACCGGTTT"))

Reverse a string without using reversed() or [::-1]?

I came across a strange Codecademy exercise that required a function that would take a string as input and return it in reverse order. The only problem was you could not use the reversed method or the common answer here on stackoverflow, [::-1].
Obviously in the real world of programming, one would most likely go with the extended slice method, or even using the reversed function but perhaps there is some case where this would not work?
I present a solution below in Q&A style, in case it is helpful for people in the future.
You can also do it with recursion:
def reverse(text):
if len(text) <= 1:
return text
return reverse(text[1:]) + text[0]
And a simple example for the string hello:
reverse(hello)
= reverse(ello) + h # The recursive step
= reverse(llo) + e + h
= reverse(lo) + l + e + h
= reverse(o) + l + l + e + h # Base case
= o + l + l + e + h
= olleh
Just another option:
from collections import deque
def reverse(iterable):
d = deque()
d.extendleft(iterable)
return ''.join(d)
Use reversed range:
def reverse(strs):
for i in xrange(len(strs)-1, -1, -1):
yield strs[i]
...
>>> ''.join(reverse('hello'))
'olleh'
xrange or range with -1 step would return items in reversed order, so we need to iterate from len(string)-1 to -1(exclusive) and fetch items from the string one by one.
>>> list(xrange(len(strs) -1, -1 , -1))
[4, 3, 2, 1, 0] #iterate over these indexes and fetch the items from the string
One-liner:
def reverse(strs):
return ''.join([strs[i] for i in xrange(len(strs)-1, -1, -1)])
...
>>> reverse('hello')
'olleh'
EDIT
Recent activity on this question caused me to look back and change my solution to a quick one-liner using a generator:
rev = ''.join([text[len(text) - count] for count in xrange(1,len(text)+1)])
Although obviously there are some better answers here like a negative step in the range or xrange function. The following is my original solution:
Here is my solution, I'll explain it step by step
def reverse(text):
lst = []
count = 1
for i in range(0,len(text)):
lst.append(text[len(text)-count])
count += 1
lst = ''.join(lst)
return lst
print reverse('hello')
First, we have to pass a parameter to the function, in this case text.
Next, I set an empty list, named lst to use later. (I actually didn't know I'd need the list until I got to the for loop, you'll see why it's necessary in a second.)
The count variable will make sense once I get into the for loop
So let's take a look at a basic version of what we are trying to accomplish:
It makes sense that appending the last character to the list would start the reverse order. For example:
>>lst = []
>>word = 'foo'
>>lst.append(word[2])
>>print lst
['o']
But in order to continue reversing the order, we need to then append word[1] and then word[0]:
>>lst.append(word[2])
>>lst.append(word[1])
>>lst.append(word[0])
>>print lst
['o','o','f']
This is great, we now have a list that has our original word in reverse order and it can be converted back into a string by using .join(). But there's a problem. This works for the word foo, it even works for any word that has a length of 3 characters. But what about a word with 5 characters? Or 10 characters? Now it won't work. What if there was a way we could dynamically change the index we append so that any word will be returned in reverse order?
Enter for loop.
for i in range(0,len(text)):
lst.append(text[len(text)-count])
count += 1
First off, it is necessary to use in range() rather than just in, because we need to iterate through the characters in the word, but we also need to pull the index value of the word so that we change the order.
The first part of the body of our for loop should look familiar. Its very similar to
>>lst.append(word[..index..])
In fact, the base concept of it is exactly the same:
>>lst.append(text[..index..])
So what's all the stuff in the middle doing?
Well, we need to first append the index of the last letter to our list, which is the length of the word, text, -1. From now on we'll refer to it as l(t) -1
>>lst.append(text[len(text)-1])
That alone will always get the last letter of our word, and append it to lst, regardless of the length of the word. But now that we have the last letter, which is l(t) - 1, we need the second to last letter, which is l(t) - 2, and so on, until there are no more characters to append to the list. Remember our count variable from above? That will come in handy. By using a for loop, we can increment the value of count by 1 through each iteration, so that the value we subtract by increases, until the for loop has iterated through the entire word:
>>for i in range(0,len(text)):
..
.. lst.append(text[len(text)-count])
.. count += 1
Now that we have the heart of our function, let's look at what we have so far:
def reverse(text):
lst = []
count = 1
for i in range(0,len(text)):
lst.append(text[len(text)-count])
count += 1
We're almost done! Right now, if we were to call our function with the word 'hello', we would get a list that looks like:
['o','l','l','e','h']
We don't want a list, we want a string. We can use .join for that:
def reverse(text):
lst = []
count = 1
for i in range(0,len(text)):
lst.append(text[len(text)-count])
count += 1
lst = ''.join(lst) # join the letters together without a space
return lst
And that's it. If we call the word 'hello' on reverse(), we'd get this:
>>print reverse('hello')
olleh
Obviously, this is way more code than is necessary in a real life situation. Using the reversed function or extended slice would be the optimal way to accomplish this task, but maybe there is some instance when it would not work, and you would need this. Either way, I figured I'd share it for anyone who would be interested.
If you guys have any other ideas, I'd love to hear them!
Only been coding Python for a few days, but I feel like this was a fairly clean solution. Create an empty list, loop through each letter in the string and append it to the front of the list, return the joined list as a string.
def reverse(text):
backwardstext = []
for letter in text:
backwardstext.insert(0, letter)
return ''.join(backwardstext)
I used this:
def reverse(text):
s=""
l=len(text)
for i in range(l):
s+=text[l-1-i]
return s
Inspired by Jon's answer, how about this one
word = 'hello'
q = deque(word)
''.join(q.pop() for _ in range(len(word)))
This is a very interesting question, I will like to offer a simple one
liner answer:
>>> S='abcdefg'
>>> ''.join(item[1] for item in sorted(enumerate(S), reverse=True))
'gfedcba'
Brief explanation:
enumerate() returns [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e'), (5, 'f'), (6, 'g')]. The indices and the values.
To reverse the values, just reverse sort it by sorted().
Finally, just put it together back to a str
I created different versions of how to reverse a string in python in my repo:
https://github.com/fedmich/Python-Codes/tree/master/Reverse%20a%20String
You can do it by using list-comprehension or lambda technique:
# Reverse a string without using reverse() function
s = 'Federico';
li = list( s ) #convert string to list
ret = [ li[i-1] for i in xrange(len(li),0,-1) ] #1 liner lambda
print ( "".join( ret ) )
or by doing a backward for loop
# Reverse a string without using reverse() function
s = 'Federico';
r = []
length = len(s)
for i in xrange(length,0,-1):
r.append( s[ i - 1] )
print ( "".join(r) )
reduce(lambda x, y : y + x, "hello world")
A golfed version: r=lambda x:"".join(x[i] for i in range(len(x-1),-1,-1)).
i just solved this in code academy and was checking my answers and ran across this list. so with a very limited understanding of python i just did this and it seamed to work.
def reverse(s):
i = len(s) - 1
sNew = ''
while i >= 0:
sNew = sNew + str(s[i])
i = i -1
return sNew
def reverse(s):
return "".join(s[i] for i in range(len(s)-1, -1, -1))
Blender's answer is lovely, but for a very long string, it will result in a whopping RuntimeError: maximum recursion depth exceeded. One might refactor the same code into a while loop, as one frequently must do with recursion in python. Obviously still bad due to time and memory issues, but at least will not error.
def reverse(text):
answer = ""
while text:
answer = text[0] + answer
text = text[1:]
return answer
Today I was asked this same exercise on pen&paper, so I come up with this function for lists:
def rev(s):
l = len(s)
for i,j in zip(range(l-1, 0, -1), range(l//2)):
s[i], s[j] = s[j], s[i]
return s
which can be used with strings with "".join(rev(list("hello")))
This is a way to do it with a while loop:
def reverse(s):
t = -1
s2 = ''
while abs(t) < len(s) + 1:
s2 = s2 + s[t]
t = t - 1
return s2
I have also just solved the coresponding exercise on codeacademy and wanted to compare my approach to others. I have not found the solution I used so far, so I thought that I sign up here and provide my solution to others. And maybe I get a suggestion or a helpful comment on how to improve the code.
Ok here it goes, I did not use any list to store the string, instead I have just accessed the string index. It took me a bit at first to deal with the len() and index number, but in the end it worked :).
def reverse(x):
reversestring = ""
for n in range(len(str(x))-1,-1, -1):
reversestring += x[n]
return reversestring
I am still wondering if the reversestring = "" could be solved in a more elegant way, or if it is "bad style" even, but i couldn't find an answer so far.
def reverse(text):
a=""
l=len(text)
while(l>=1):
a+=text[l-1]
l-=1
return a
i just concatenated the string a with highest indexes of text (which keeps on decrementing by 1 each loop).
All I did to achieve a reverse string is use the xrange function with the length of the string in a for loop and step back per the following:
myString = "ABC"
for index in xrange(len(myString),-1):
print index
My output is "CBA"
You can simply reverse iterate your string starting from the last character. With python you can use list comprehension to construct the list of characters in reverse order and then join them to get the reversed string in a one-liner:
def reverse(s):
return "".join([s[-i-1] for i in xrange(len(s))])
if you are not allowed to even use negative indexing you should replace s[-i-1] with s[len(s)-i-1]
You've received a lot of alternative answers, but just to add another simple solution -- the first thing that came to mind something like this:
def reverse(text):
reversed_text = ""
for n in range(len(text)):
reversed_text += text[-1 - n]
return reversed_text
It's not as fast as some of the other options people have mentioned(or built in methods), but easy to follow as we're simply using the length of the text string to concatenate one character at a time by slicing from the end toward the front.
def reverseThatString(theString):
reversedString = ""
lenOfString = len(theString)
for i,j in enumerate(theString):
lenOfString -= 1
reversedString += theString[lenOfString]
return reversedString
This is my solution using the for i in range loop:
def reverse(string):
tmp = ""
for i in range(1,len(string)+1):
tmp += string[len(string)-i]
return tmp
It's pretty easy to understand. I start from 1 to avoid index out of bound.
Here's my contribution:
def rev(test):
test = list(test)
i = len(test)-1
result = []
print test
while i >= 0:
result.append(test.pop(i))
i -= 1
return "".join(result)
You can do simply like this
def rev(str):
rev = ""
for i in range(0,len(str)):
rev = rev + str[(len(str)-1)-i]
return rev
Here is one using a list as a stack:
def reverse(s):
rev = [_t for _t in s]
t = ''
while len(rev) != 0:
t+=rev.pop()
return t
Try this simple and elegant code.
my_string= "sentence"
new_str = ""
for i in my_string:
new_str = i + new_str
print(new_str)
you have got enough answer.
Just want to share another way.
you can write a two small function for reverse and compare the function output with the given string
var = ''
def reverse(data):
for i in data:
var = i + var
return var
if not var == data :
print "No palindrome"
else :
print "Palindrome"
Not very clever, but tricky solution
def reverse(t):
for j in range(len(t) // 2):
t = t[:j] + t[- j - 1] + t[j + 1:- j - 1] + t[j] + t[len(t) - j:]
return t
Pointfree:
from functools import partial
from operator import add
flip = lambda f: lambda x, y: f(y, x)
rev = partial(reduce, flip(add))
Test:
>>> rev('hello')
'olleh'

Determine prefix from a set of (similar) strings

I have a set of strings, e.g.
my_prefix_what_ever
my_prefix_what_so_ever
my_prefix_doesnt_matter
I simply want to find the longest common portion of these strings, here the prefix. In the above the result should be
my_prefix_
The strings
my_prefix_what_ever
my_prefix_what_so_ever
my_doesnt_matter
should result in the prefix
my_
Is there a relatively painless way in Python to determine the prefix (without having to iterate over each character manually)?
PS: I'm using Python 2.6.3.
Never rewrite what is provided to you: os.path.commonprefix does exactly this:
Return the longest path prefix (taken
character-by-character) that is a prefix of all paths in list. If list
is empty, return the empty string (''). Note that this may return
invalid paths because it works a character at a time.
For comparison to the other answers, here's the code:
# Return the longest prefix of all list elements.
def commonprefix(m):
"Given a list of pathnames, returns the longest common leading component"
if not m: return ''
s1 = min(m)
s2 = max(m)
for i, c in enumerate(s1):
if c != s2[i]:
return s1[:i]
return s1
Ned Batchelder is probably right. But for the fun of it, here's a more efficient version of phimuemue's answer using itertools.
import itertools
strings = ['my_prefix_what_ever',
'my_prefix_what_so_ever',
'my_prefix_doesnt_matter']
def all_same(x):
return all(x[0] == y for y in x)
char_tuples = itertools.izip(*strings)
prefix_tuples = itertools.takewhile(all_same, char_tuples)
''.join(x[0] for x in prefix_tuples)
As an affront to readability, here's a one-line version :)
>>> from itertools import takewhile, izip
>>> ''.join(c[0] for c in takewhile(lambda x: all(x[0] == y for y in x), izip(*strings)))
'my_prefix_'
Here's my solution:
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]
prefix_len = len(a[0])
for x in a[1 : ]:
prefix_len = min(prefix_len, len(x))
while not x.startswith(a[0][ : prefix_len]):
prefix_len -= 1
prefix = a[0][ : prefix_len]
The following is an working, but probably quite inefficient solution.
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]
b = zip(*a)
c = [x[0] for x in b if x==(x[0],)*len(x)]
result = "".join(c)
For small sets of strings, the above is no problem at all. But for larger sets, I personally would code another, manual solution that checks each character one after another and stops when there are differences.
Algorithmically, this yields the same procedure, however, one might be able to avoid constructing the list c.
Just out of curiosity I figured out yet another way to do this:
def common_prefix(strings):
if len(strings) == 1:#rule out trivial case
return strings[0]
prefix = strings[0]
for string in strings[1:]:
while string[:len(prefix)] != prefix and prefix:
prefix = prefix[:len(prefix)-1]
if not prefix:
break
return prefix
strings = ["my_prefix_what_ever","my_prefix_what_so_ever","my_prefix_doesnt_matter"]
print common_prefix(strings)
#Prints "my_prefix_"
As Ned pointed out it's probably better to use os.path.commonprefix, which is a pretty elegant function.
The second line of this employs the reduce function on each character in the input strings. It returns a list of N+1 elements where N is length of the shortest input string.
Each element in lot is either (a) the input character, if all input strings match at that position, or (b) None. lot.index(None) is the position of the first None in lot: the length of the common prefix. out is that common prefix.
val = ["axc", "abc", "abc"]
lot = [reduce(lambda a, b: a if a == b else None, x) for x in zip(*val)] + [None]
out = val[0][:lot.index(None)]
Here's a simple clean solution. The idea is to use zip() function to line up all the characters by putting them in a list of 1st characters, list of 2nd characters,...list of nth characters. Then iterate each list to check if they contain only 1 value.
a = ["my_prefix_what_ever", "my_prefix_what_so_ever", "my_prefix_doesnt_matter"]
list = [all(x[i] == x[i+1] for i in range(len(x)-1)) for x in zip(*a)]
print a[0][:list.index(0) if list.count(0) > 0 else len(list)]
output: my_prefix_
Here is another way of doing this using OrderedDict with minimal code.
import collections
import itertools
def commonprefix(instrings):
""" Common prefix of a list of input strings using OrderedDict """
d = collections.OrderedDict()
for instring in instrings:
for idx,char in enumerate(instring):
# Make sure index is added into key
d[(char, idx)] = d.get((char,idx), 0) + 1
# Return prefix of keys while value == length(instrings)
return ''.join([k[0] for k in itertools.takewhile(lambda x: d[x] == len(instrings), d)])
I had a slight variation of the problem and google sends me here, so I think it will be useful to document:
I have a list like:
my_prefix_what_ever
my_prefix_what_so_ever
my_prefix_doesnt_matter
some_noise
some_other_noise
So I would expect my_prefix to be returned. That can be done with:
from collections import Counter
def get_longest_common_prefix(values, min_length):
substrings = [value[0: i-1] for value in values for i in range(min_length, len(value))]
counter = Counter(substrings)
# remove count of 1
counter -= Counter(set(substrings))
return max(counter, key=len)
In one line without using itertools, for no particular reason, although it does iterate through each character:
''.join([z[0] for z in zip(*(list(s) for s in strings)) if all(x==z[0] for x in z)])
Find the common prefix in all words from the given input string, if there is no common prefix print -1
stringList = ['my_prefix_what_ever', 'my_prefix_what_so_ever', 'my_prefix_doesnt_matter']
len2 = len( stringList )
if len2 != 0:
# let shortest word is prefix
prefix = min( stringList )
for i in range( len2 ):
word = stringList[ i ]
len1 = len( prefix )
# slicing each word as lenght of prefix
word = word[ 0:len1 ]
for j in range( len1 ):
# comparing each letter of word and prefix
if word[ j ] != prefix[ j ]:
# if letter does not match slice the prefix
prefix = prefix[ :j ]
break # after getting comman prefix move to next word
if len( prefix ) != 0:
print("common prefix: ",prefix)
else:
print("-1")
else:
print("string List is empty")

Categories