shuffle string in python - python

I am looking for a function or short program that receives a string (up to 10 letters) and shuffles it.

>>> import random
>>> s="abcdef123"
>>> ''.join(random.sample(s,len(s)))
'1f2bde3ac'

There is a function shuffle in the random module. Note that it shuffles in-place so you first have to convert your string to a list of characters, shuffle it, then join the result again.
import random
l = list(s)
random.shuffle(l)
result = ''.join(l)

Python Provides the various solutions to shuffle the string:
1. External library: python-string-utils
first install the python-string-utils library
pip install python_string_utils
use string_utils.shuffle() function to shuffle string
please use the below snippet for it
Code Snippet
import string_utils
print string_utils.shuffle("random_string")
Output:
drorntmi_asng
2. Builtins Method: random.shuffle
Please find the code below to shuffle the string. The code will take the string and convert that string to list. Then shuffle the string contents and will print the string.
import random
str_var = list("shuffle_this_string")
random.shuffle(str_var)
print ''.join(str_var)
Output:
t_suesnhgslfhitrfi_
3. External Library: Numpy
import numpy
str_var = list("shuffle_this_string")
numpy.random.shuffle(str_var)
print ''.join(str_var)
Output:
nfehirgsu_slftt_his

you can use more_itertools.random_permutation
from more_itertools import random_permutation
s = 'ABCDEFGHIJ'
''.join(random_permutation(s))
output:
'DFJIEABGCH'

Here is a helper that uses random to non-destructively shuffle, string, list or tuple:
def shuffle_any(o):
is_l=is_t=False
if isinstance(o, str):
l = list(o)
elif isinstance(o, list):
l = o[:]
is_l = True
elif isinstance(o, tuple):
is_t = True
l = list(o)
else:
raise Exception("o is None!" if o is None \
else f"Unexpected type: {o.__class__.__name__}")
random.shuffle(l)
return l if is_l else tuple(l) if is_t else ''.join(l)
Usage:
print(f"shuffle_any('abcdefg'): {shuffle_any('abcdefg')}")
print(f"shuffle_any([1,2,3,4,5]): {shuffle_any([1,2,3,4,5])}")
print(f"shuffle_any((1,2,3,4,5)): {shuffle_any((1,2,3,4,5))}")
Output:
shuffle_any('abcdefg'): dfceabg
shuffle_any([1,2,3,4,5]): [5, 2, 3, 4, 1]
shuffle_any((1,2,3,4,5)): (4, 3, 2, 5, 1)

Related

How to request a list input from the user in Python?

I have defined a function which requires 3 arguments, one of which must be a list.
One solution that I found works only when the list is made up of integers, which will not necessarily be the case (the types can vary within the same list).
How to request a list type input from the user? e.g.: when something like [1,2,3] is inputted, it is considered as a list?
Here's one way:
$ cat foo.py
import sys
input1 = sys.argv[1]
input2 = sys.argv[2]
print('Before\n-------')
print('input1:{},type_of_input1:{}'.format(input1, type(input1)))
print('input2:{},type_of_input2:{}'.format(input2, type(input2)))
print('After\n-------')
input1 = input1.split(' ')
print('input1:{},type_of_input1:{}'.format(input1, type(input1)))
print('input2:{},type_of_input2:{}'.format(input2, type(input2)))
$
Execution output
$ python foo.py 'foo bar' bat
Before
-------
input1:foo bar,type_of_input1:<type 'str'>
input2:bat,type_of_input2:<type 'str'>
After
-------
input1:['foo', 'bar'],type_of_input1:<type 'list'>
input2:bat,type_of_input2:<type 'str'>
$
Keep it simple and safe and use input and convert the input into a list yourself:
import re
re.sub("[^\w]", " ", input('-->')).split()
-->This is a string of words converted into a list
output:
['This', 'is', 'a', 'string', 'of', 'words', 'converted', 'into', 'a', 'list']
input is a built-in: https://docs.python.org/3/library/functions.html#input
Use ast.literal_eval:
import ast
while True:
s=raw_input("Enter a list: ")
s=ast.literal_eval(s)
if not isinstance(s, list):
print "Nope! {} is a {}".format(s, type(s))
else:
break
print s
If you want the option of the user entering a tuple (by entering 1,2,3 for example) add tuple to isinstance:
import ast
while True:
s=raw_input("Enter a list: ")
s=ast.literal_eval(s)
if not isinstance(s, (list, tuple)):
print "Nope! {} is a {}".format(s, type(s))
else:
break
If you completely trust user input, you can just use eval(). Assuming the user enters the string [1, 2, 3]:
x = input() # Python 3, use raw_input for Python 2
y = eval(x) # Dangerous, don't use with untrusted input
print(y)
# [1, 2, 3]
print(len(y))
# 3
Update:
ast.literal_eval is a better choice here.
import ast
x = input() # Python 3, use raw_input for Python 2
y = ast.literal_eval(x)
print(y)
# [1, 2, 3]
print(len(y))
# 3

How to select an integer randomly from a list containing integers as well as other objects?

I have a list
data=['_','_','A','B','C',1,2,3,4,5]
I need to randomly get an integer among 1,2,3,4,5
The list keeps getting modified so i cant just simply choose from the last 5 members
Here's what i tried but it throws an error retrieving the other members:
inp2=int(random.choice(data))
You can filter the non-integer items;
inp2 = ramdom.choice([x for x in data if isinstance(x, int)])
Though it is almost similar to the answer of Neo, you can also try this:
inp2 = random.choice(filter(lambda d: isinstance(d, int), data))
To create a list of last 5 elements.
>>> from random import choice
>>> data=['_','_','A','B','C',1,2,3,4,5]
>>> l = len(data)
>>> data[(l-5):l]
[1, 2, 3, 4, 5]
>>> k = data[(l-5):l]
>>> choice(k)
5
>>> choice(k)
2
>>>
random.choice([i for i in data[-5:] if isinstance(x, int)])
It is more safe for check type of data[-5:] by isinstance().
Try this.
import operator
inp2 = random.choice(filter(operator.isNumberType, data))
For this specific problem selecting last 5 elements also a good solution.
inp2 = random.choice(data[5:])
I think the best solution would be creating another list, where would be just int values you want to pick from.
I don't know your specified assignment, but for example if you have a method for adding to your list, just add there:
def add(self, element):
self.data.append(element)
if type(element) == int:
self.data_int.append(element)
and then just use:
def get_value(self):
return random.choice(self.data_int)

Split string to various data types

I would like to convert the following string:
s = '1|2|a|b'
to
[1, 2, 'a', 'b']
Is it possible to do the conversion in one line?
Is it possible to do the conversion in one line?
YES, It is possible. But how?
Algorithm for the approach
Split the string into its constituent parts using str.split. The output of this is
>>> s = '1|2|a|b'
>>> s.split('|')
['1', '2', 'a', 'b']
Now we have got half the problem. Next we need to loop through the split string and then check if each of them is a string or an int. For this we use
A list comprehension, which is for the looping part
str.isdigit for finding if the element is an int or a str.
The list comprehension can be easily written as [i for i in s.split('|')]. But how do we add an if clause there? This is covered in One-line list comprehension: if-else variants. Now that we know which all elements are int and which are not, we can easily call the builtin int on it.
Hence the final code will look like
[int(i) if i.isdigit() else i for i in s.split('|')]
Now for a small demo,
>>> s = '1|2|a|b'
>>> [int(i) if i.isdigit() else i for i in s.split('|')]
[1, 2, 'a', 'b']
As we can see, the output is as expected.
Note that this approach is not suitable if there are many types to be converted.
You cannot do it for negative numbers or lots of mixed types in one line but you could use a function that would work for multiple types using ast.literal_eval:
from ast import literal_eval
def f(s, delim):
for ele in s.split(delim):
try:
yield literal_eval(ele)
except ValueError:
yield ele
s = '1|-2|a|b|3.4'
print(list(f(s,"|")))
[1, -2, 'a', 'b', 3.4]
Another way, is using map built-in method:
>>> s='1|2|a|b'
>>> l = map(lambda x: int(x) if x.isdigit() else x, s.split('|'))
>>> l
[1, 2, 'a', 'b']
If Python3, then:
>>> s='1|2|a|b'
>>> l = list(map(lambda x: int(x) if x.isdigit() else x, s.split('|')))
>>> l
[1, 2, 'a', 'b']
Since map in Python3 would give a generator, so you must convert it to list
It is possible to do arbitrarily many or complex conversions "in a single line" if you're allowed a helper function. Python does not natively have a "convert this string to the type that it should represent" function, because what it "should" represent is vague and may change from application to application.
def convert(input):
converters = [int, float, json.loads]
for converter in converters:
try:
return converter(input)
except (TypeError, ValueError):
pass
# here we assume if all converters failed, it's just a string
return input
s = "1|2.3|a|[4,5]"
result = [convert(x) for x in s.split("|")]
If you have all kinds of data types(more than str and int), I believe this does the job.
s = '1|2|a|b|[1, 2, 3]|(1, 2, 3)'
print [eval(x) if not x.isalpha() else x for x in s.split("|")]
# [1, 2, 'a', 'b', [1, 2, 3], (1, 2, 3)]
This fails if there exists elements such as "b1"

Selecting a random list element in python

I'm trying to create a function that takes two lists and selects an element at random from each of them. Is there any way to do this using the random.seed function?
You can use random.choice to pick a random element from a sequence (like a list).
If your two lists are list1 and list2, that would be:
a = random.choice(list1)
b = random.choice(list2)
Are you sure you want to use random.seed? This will initialize the random number generator in a consistent way each time, which can be very useful if you want subsequent runs to be identical but in general that is not desired. For example, the following function will always return 8, even though it looks like it should randomly choose a number between 0 and 10.
>>> def not_very_random():
... random.seed(0)
... return random.choice(range(10))
...
>>> not_very_random()
8
>>> not_very_random()
8
>>> not_very_random()
8
>>> not_very_random()
8
Note: #F.J's solution is much less complicated and better.
Use random.randint to pick a pseudo-random index from the list. Then use that index to select the element:
>>> import random as r
>>> r.seed(14) # used random number generator of ... my head ... to get 14
>>> mylist = [1,2,3,4,5]
>>> mylist[r.randint(0, len(mylist) - 1)]
You can easily extend this to work on two lists.
Why do you want to use random.seed?
Example (using Python2.7):
>>> import collections as c
>>> c.Counter([mylist[r.randint(0, len(mylist) - 1)] for x in range(200)])
Counter({1: 44, 5: 43, 2: 40, 3: 39, 4: 34})
Is that random enough?
I totally redid my previous answer. Here is a class which wraps a random-number generator (with optional seed) with the list. This is a minor improvement over F.J.'s, because it gives deterministic behavior for testing. Calling choice() on the first list should not affect the second list, and vice versa:
class rlist ():
def __init__(self, lst, rg=None, rseed=None):
self.lst = lst
if rg is not None:
self.rg = rg
else:
self.rg = random.Random()
if rseed is not None:
self.rg.seed(rseed)
def choice(self):
return self.rg.choice(self.lst)
if __name__ == '__main__':
rl1 = rlist([1,2,3,4,5], rseed=1234)
rl2 = rlist(['a','b','c','d','e'], rseed=1234)
print 'First call:'
print rl1.choice(),rl2.choice()
print 'Second call:'
print rl1.choice(),rl2.choice()

How do I do what strtok() does in C, in Python?

I am learning Python and trying to figure out an efficient way to tokenize a string of numbers separated by commas into a list. Well formed cases work as I expect, but less well formed cases not so much.
If I have this:
A = '1,2,3,4'
B = [int(x) for x in A.split(',')]
B results in [1, 2, 3, 4]
which is what I expect, but if the string is something more like
A = '1,,2,3,4,'
if I'm using the same list comprehension expression for B as above, I get an exception. I think I understand why (because some of the "x" string values are not integers), but I'm thinking that there would be a way to parse this still quite elegantly such that tokenization of the string a works a bit more directly like strtok(A,",\n\t") would have done when called iteratively in C.
To be clear what I am asking; I am looking for an elegant/efficient/typical way in Python to have all of the following example cases of strings:
A='1,,2,3,\n,4,\n'
A='1,2,3,4'
A=',1,2,3,4,\t\n'
A='\n\t,1,2,3,,4\n'
return with the same list of:
B=[1,2,3,4]
via some sort of compact expression.
How about this:
A = '1, 2,,3,4 '
B = [int(x) for x in A.split(',') if x.strip()]
x.strip() trims whitespace from the string, which will make it empty if the string is all whitespace. An empty string is "false" in a boolean context, so it's filtered by the if part of the list comprehension.
Generally, I try to avoid regular expressions, but if you want to split on a bunch of different things, they work. Try this:
import re
result = [int(x) for x in filter(None, re.split('[,\n,\t]', A))]
Mmm, functional goodness (with a bit of generator expression thrown in):
a = "1,2,,3,4,"
print map(int, filter(None, (i.strip() for i in a.split(','))))
For full functional joy:
import string
a = "1,2,,3,4,"
print map(int, filter(None, map(string.strip, a.split(','))))
For the sake of completeness, I will answer this seven year old question:
The C program that uses strtok:
int main()
{
char myLine[]="This is;a-line,with pieces";
char *p;
for(p=strtok(myLine, " ;-,"); p != NULL; p=strtok(NULL, " ;-,"))
{
printf("piece=%s\n", p);
}
}
can be accomplished in python with re.split as:
import re
myLine="This is;a-line,with pieces"
for p in re.split("[ ;\-,]",myLine):
print("piece="+p)
This will work, and never raise an exception, if all the numbers are ints. The isdigit() call is false if there's a decimal point in the string.
>>> nums = ['1,,2,3,\n,4\n', '1,2,3,4', ',1,2,3,4,\t\n', '\n\t,1,2,3,,4\n']
>>> for n in nums:
... [ int(i.strip()) for i in n if i.strip() and i.strip().isdigit() ]
...
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 4]
How about this?
>>> a = "1,2,,3,4,"
>>> map(int,filter(None,a.split(",")))
[1, 2, 3, 4]
filter will remove all false values (i.e. empty strings), which are then mapped to int.
EDIT: Just tested this against the above posted versions, and it seems to be significantly faster, 15% or so compared to the strip() one and more than twice as fast as the isdigit() one
Why accept inferior substitutes that cannot segfault your interpreter? With ctypes you can just call the real thing! :-)
# strtok in Python
from ctypes import c_char_p, cdll
try: libc = cdll.LoadLibrary('libc.so.6')
except WindowsError:
libc = cdll.LoadLibrary('msvcrt.dll')
libc.strtok.restype = c_char_p
dat = c_char_p("1,,2,3,4")
sep = c_char_p(",\n\t")
result = [libc.strtok(dat, sep)] + list(iter(lambda: libc.strtok(None, sep), None))
print(result)
Why not just wrap in a try except block which catches anything not an integer?
I was desperately in need of strtok equivalent in Python. So I developed a simple one by my own
def strtok(val,delim):
token_list=[]
token_list.append(val)
for key in delim:
nList=[]
for token in token_list:
subTokens = [ x for x in token.split(key) if x.strip()]
nList= nList + subTokens
token_list = nList
return token_list
I'd guess regular expressions are the way to go: http://docs.python.org/library/re.html

Categories