Scrambling a string in Python without using random.shuffle() - python-3.x

I'm trying to scramble a string, "string", without using random.shuffle(), but my code keeps producing output that has missing and repeating characters, e.g. gtrgtg, gnrtnn, etc. I'm not sure what I'm doing wrong.
import random
s = "string"
new_s=[]
for c in s:
if random.choice(s) not in new_s:
new_s.append(random.choice(s))
print(''.join(new_s))

In its current state, your program checks whether the randomly chosen character is in a string. If it is, it doesn't do anything other than continuing the loop. Also since you don't assign random.choice(s) to a variable, you generate another character after you do the check.
A working version would be:
import random
s = "string"
new_s = []
for c in s:
char = random.choice(s) # assign it to a variable
while char in new_s: # until a new character comes, repeat the procedure
char = random.choice(s)
new_s.append(char)
print(''.join(new_s))
This generates strings like ngtsri, gsrnit, etc. Note that this won't work if you have duplicates in the original string.
The above code is highly inefficient. I only gave the correction assuming this was for learning purposes. Normally, if you want to repeatedly check if something is in a collection, that collection should be a set or a dictionary.

random.choice choses a random character out of string s, but doesn't remove it - so it's possible for the same character to be chosen multiple times, and for some characters to not be chosen at all.
import random
s = 'string'
new_s = []
# rather than choosing a character, chose an index, use it and slice it out
while s:
i = random.randint(0, len(s)-1)
new_s.append(s[i])
s = s[:i] + s[i+1:]
print(''.join(new_s))
# this is more elegant with lists:
s = list(s)
while s:
i = random.randint(0, len(s)-1)
new_s.append(s.pop(i))
print(''.join(new_s))
Neither option is very efficient... but for efficiency, use random.shuffle. :)

Using while, you could loop through s until the length of new_s matches with that of s and the resultant string has non-repeating characters.
import random
s = "string"
new_s = '' # So you will not need ''.join() when you print this result
while len(new_s) != len(s):
char = random.choice(s)
if char not in new_s:
new_s += char
print(new_s)
rntigs
>>>

try this:
from random import randint
def shuffle(sr):
n = len(sr)
s = list(sr)
for i in range(n):
cur, idx = s[i], randint(0, n - 1)
s[i], s[idx] = s[idx], cur
return ''.join(s)
print(shuffle("hello"))

Related

I want to compress each letter in a string with a specific length

I have the following string:
x = 'aaabbbbbaaaaaacccccbbbbbbbbbbbbbbb'. I want to get an output like this: abaacbbb, in which "a" will be compressed with a length of 3 and "b" will be compressed with a length of 5. I used the following function, but it removes all the adjacent duplicates and the output is: abacb :
def remove_dup(x):
if len(x) < 2:
return x
if x[0] != x[1]:
return x[0] + remove_dup(x[1:])
return remove_dup(x[1:])
x = 'aaabbbbbaaaaaacccccbbbbbbbbbbbbbbb'
print(remove_dup(x))
It would be wonderful if somebody could help me with this.
Thank you!
Unless this is a homework question with special constraints, this would be more conveniently and arguably more readably implemented with a regex substitution that replaces desired quantities of specific characters with a single instance of the captured character:
import re
def remove_dup(x):
return re.sub('(a){3}|([bc]){5}', r'\1\2', x)
x = 'aaabbbbbaaaaaacccccbbbbbbbbbbbbbbb'
print(remove_dup(x))
This outputs:
abaacbbb

Replace slice of string python with string of different size, but maintain structure

so today I was working on a function that removes any quoted strings from a chunk of data, and replaces them with format areas instead ({0}, {1}, etc...).
I ran into a problem, because the output was becoming completely scrambled, as in a {1} was going in a seemingly random place.
I later found out that this was a problem because the replacement of slices in the list changed the list so that it's length was different, and so the previous re matches would not line up (it only worked for the first iteration).
the gathering of the strings worked perfectly, as expected, as this is most certainly not a problem with re.
I've read about mutable sequences, and a bunch of other things as well, but was not able to find anything on this.
what I think i need is something like str.replace but can take slices, instead of a substring.
here is my code:
import re
def rm_strings_from_data(data):
regex = re.compile(r'"(.*?)"')
s = regex.finditer(data)
list_data = list(data)
val = 0
strings = []
for i in s:
string = i.group()
start, end = i.span()
strings.append(string)
list_data[start:end] = '{%d}' % val
val += 1
print(strings, ''.join(list_data), sep='\n\n')
if __name__ == '__main__':
rm_strings_from_data('[hi="hello!" thing="a thing!" other="other thing"]')
i get:
['"hello!"', '"a thing!"', '"other thing"']
[hi={0} thing="a th{1}r="other thing{2}
I would like the output:
['"hello!"', '"a thing!"', '"other thing"']
[hi={0} thing={1} other={2}]
any help would be appreciated. thanks for your time :)
Why not match both key=value parts using regex capture groups like this: (\w+?)=(".*?")
Then it becomes very easy to assemble the lists as needed.
Sample Code:
import re
def rm_strings_from_data(data):
regex = re.compile(r'(\w+?)=(".*?")')
matches = regex.finditer(data)
strings = []
list_data = []
for matchNum, match in enumerate(matches):
matchNum = matchNum + 1
strings.append(match.group(2))
list_data.append((match.group(1) + '={' + str(matchNum) + '} '))
print(strings, '[' + ''.join(list_data) + ']', sep='\n\n')
if __name__ == '__main__':
rm_strings_from_data('[hi="hello!" thing="a thing!" other="other thing"]')

Select part of a string and change it in lowercase or uppercase python 3.x

I want to convert a string so that the pair positions will be in upper case characters and the impair positions will be in lower case characters.
Here is what I've tried so far:
def foldingo(chaine):
chaineuh=chaine[0::2].upper()
chaine=chaineuh[1::2].lower()
return chaine
your code takes every other character in chaine, uppercases them, and assigns those characters to chaineuh.
Then it takes every other character in chaineuh, lowercases them, and assigns those characters to chaine again. In other words:
abcdefg -> ACEG -> cg
You'll notice it's not keeping the characters that you're not trying to target.
You could try building all the uppercases and lowercases separately, then iterate with zip to get them together.
def fold(s):
uppers = s[0::2].upper()
lowers = s[1::2].lower()
return zip(uppers, lowers)
But this doesn't quit work either, since zip gives you tuples, not strings, and will drop the last character in odd-lengthed strings
abcdefg -> ACEG, bdf -> ('A', 'b'), ('C', 'd'), ('E', 'f')
We could fix that by using a couple calls to str.join and using itertools.zip_longest with a fillvalue='', but it's kind of like using a wrench to hammer in a nail. It's not really the right tool for the job. For the record: it would look like:
''.join([''.join(pair) for pair in itertools.zip_longest(uppers, lowers, fillvalue='')])
yuck.
Let's instead just iterate over the string and uppercase every other letter. We can use an alternating boolean to track whether we're upper'ing or lower'ing this time around.
def fold(s):
time_to_upper = True
result = ""
for ch in s:
if time_to_upper:
result += ch.upper()
else:
result += ch.lower()
time_to_upper = not time_to_upper
return result
You could also use enumerate and a modulo to keep track:
def fold(s):
result = ""
for i, ch in enumerate(s):
ch = ch.lower() if i % 2 else ch.upper()
result += ch
return result
Or by using itertools.cycle, str.join, and list comprehensions, we can make this a lot shorter (possibly at the cost of readability!)
import itertools
def fold(s):
return ''.join([op(ch) for op, ch in zip(itertools.cycle([str.upper, str.lower]), s)]

Delete specific characters from a string (Python)

I understand that str = str.replace('x', '') will eliminate all the x's.
But let's say I have a string jxjrxxtzxz and I only want to delete the first and last x making the string jjrxxtzz. This is not string specific. I want to be able to handle all strings, and not just that specific example.
edit: assume that x is the only letter I want to remove. Thank you!
One fairly straight forward way is to just use find and rfind to locate the characters to remove;
s = 'jxjrxxtzxz'
# Remove first occurrence of 'x'
ix = s.find('x')
if ix > -1:
s = s[:ix]+s[ix+1:]
# Remove last occurrence of 'x'
ix = s.rfind('x')
if ix > -1:
s = s[:ix]+s[ix+1:]
Not pretty but this will work:
def remove_first_last(c, s):
return s.replace(c,'', 1)[::-1].replace(c,'',1)[::-1]
Usage:
In [1]: remove_first_last('x', 'jxjrxxtzxz')
Out[1]: 'jjrxxtzz'

Is there a pythonic way to insert space characters at random positions of an existing string?

is there a pythonic way to implement this:
Insert /spaces_1/ U+0020 SPACE
characters into /key_1/ at random
positions other than the start or end
of the string.
?
There /spaces_1/ is integer and /key_1/ is arbitrary existing string.
Thanks.
strings in python are immutable, so you can't change them in place. However:
import random
def insert_space(s):
r = random.randint(1, len(s)-1)
return s[:r] + ' ' + s[r:]
def insert_spaces(s):
for i in xrange(random.randrange(len(s))):
s = insert_space(s)
return s
Here's a list based solution:
import random
def insert_spaces(s):
s = list(s)
for i in xrange(len(s)-1):
while random.randrange(2):
s[i] = s[i] + ' '
return ''.join(s)
I'm going to arbitrarily decide you never want two spaces inserted adjacently - each insertion point used only once - and that "insert" excludes "append" and "prepend".
First, construct a list of insertion points...
insert_points = range (1, len (mystring))
Pick out a random selection from that list, and sort it...
import random
selected = random.sample (insert_points, 5)
selected.sort ()
Make a list of slices of your string...
selected.append (len (mystring)) # include the last slice
temp = 0 # start with first slice
result = []
for i in selected :
result.append (mystring [temp:i])
temp = i
Now, built the new string...
" ".join (result)
Just because no one used map yet:
import random
''.join(map(lambda x:x+' '*random.randint(0,1), s)).strip()
This method inserts a given number of spaces to a random position in a string and takes care that there are no double spaces after each other:
import random
def add_spaces(s, num_spaces):
assert(num_spaces <= len(s) - 1)
space_idx = []
space_idx.append(random.randint(0, len(s) - 2))
num_spaces -= 1
while (num_spaces > 0):
idx = random.randint(0, len(s) - 2)
if (not idx in space_idx):
space_idx.append(idx)
num_spaces -= 1
result_with_spaces = ''
for i in range(len(s)):
result_with_spaces += s[i]
if i in space_idx:
result_with_spaces += ' '
return result_with_spaces
If you want to add more than one space, then go
s[:r] + ' '*n + s[r:]
Here it comes...
def thePythonWay(s,n):
n = max(0,min(n,25))
where = random.sample(xrange(1,len(s)),n)
return ''.join("%2s" if i in where else "%s" for i in xrange(len(s))) % tuple(s)
We will randomly choose the locations where spaces will be added - after char 0, 1, ... n-2 of the string (n-1 is the last character, and we will not place a space after that); and then insert the spaces by replacing the characters in the specified locations with (the original character) + ' '. This is along the lines of Steve314's solution (i.e. keeping the assumption that you don't want consecutive spaces - which limits the total spaces you can have), but without using lists.
Thus:
import random
def insert_random_spaces(original, amount):
assert amount > 0 and amount < len(original)
insert_positions = sorted(random.sample(xrange(len(original) - 1), amount))
return ''.join(
x + (' ' if i in insert_positions else '')
for (i, x) in enumerate(original)
)

Resources