List shuffling by range - python

I have a list full of strings. I want to take the first 10 values, shuffle them, then replace the first 10 values of the list, then with values 11-20, then 21-30, and so on.
For example:
input_list = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t']
and a function called:
shuffle10(input_list)
>>> ['d','b','c','f','j','i','h','a','e','g','m','n','s','r','k','p','l','q','o','t']
I thought it'd work if I defined an empty list and appended every 10 values randomized:
newlist=[]
for i in range(int(len(input_list) / 10)):
newlist.append(shuffle(input_list[(i*10):(i+1)*10]))
print(newlist)
but all this returns is:
[None]
[None, None]

Use random.sample instead of shuffle
>>> input_list = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t']
>>> sum((random.sample(input_list[n:n+10], 10) for n in range(0,len(input_list),10)), [])
['f', 'i', 'd', 'a', 'g', 'j', 'e', 'c', 'b', 'h', 'p', 'l', 'r', 'q', 'm', 't', 's', 'n', 'o', 'k']

You're creating a temp list in place and shuffling it but not capturing its results. You can pull out the relevant sublist, shuffle, then create a new list:
new_list=[]
for i in range(1, len(input_list), 10):
list_slice = input_list[i:i + 10]
shuffle(list_slice)
new_list.extend(list_slice)
print(new_list)

Related

Use global positions to mutate list, in list of lists

I need to keep my data in a list of lists, but want to edit elements in the lists based on their overall position.
For instance:
mylist = [['h','e','l','l','o'], ['w','o','r','l','d']]
I want to change position 5 as if it was all one list resulting in:
[['h','e','l','l','o'], ['change','o','r','l','d']]
This is for very large lists and lots of mutations so speed is essential!
Here is the solution of your's question
# initializing list
input_list = [['h', 'e', 'l', 'l', 'o'], ['w', 'o', 'r', 'l', 'd']]
print("The initial list is : " + str(input_list))
length: int = 0
# define position(global index) where you want to update items in list
change_index = 5
## global starting index of sublist [0,5]
sub_list_start_index: list = list()
for sub_list in input_list:
sub_list_start_index.append(length)
length += len(sub_list)
# check if index we want to change is <= global list index and
if change_index <= length - 1 and change_index >= max(sub_list_start_index):
sub_list_index = int(change_index - max(sub_list_start_index))
input_list[input_list.index(sub_list)][sub_list_index] = 'change'
print("Updated list : " + str(input_list))
Output:
The initial list is : [['h', 'e', 'l', 'l', 'o'], ['w', 'o', 'r', 'l', 'd']]
Updated list : [['h', 'e', 'l', 'l', 'o'], ['change', 'o', 'r', 'l', 'd']]

Python rearrange list based on another list

I want to rearrange a list based on another list which have common elements between them.
my list = ['q','s','b','f','l','c','x','a']
base_list = ['z','a','b','c']
Above lists have common 'a','b' and 'c' as common elements.the expected outcome for is as below
my_result = ['a','b','c','q','s','f','l','x']
Thanks in Advance
Sky
my_list = ['q','s','b','f','l','c','x','a']
base_list = ['z','a','b','c']
res1=[x for x in base_list if x in my_list] # common elements
res2=[x for x in my_list if x not in res1] #
res3=res1+res2
Output :
['a', 'b', 'c', 'q', 's', 'f', 'l', 'x']
Create a custom key for sorted as shown in this document. Set the value arbitrarily high for the letters that don't appear in the base_list so they end up in the back. Since sorted is considered stable those that aren't in the base_list will remain untouched in terms of original order.
l = ['q','s','b','f','l','c','x','a']
base_list = ['z','a','b','c']
def custom_key(letter):
try:
return base_list.index(letter)
except ValueError:
return 1_000
sorted(l, key=custom_key)
['a', 'b', 'c', 'q', 's', 'f', 'l', 'x']
A (probably non optimal) way:
>>> sorted(my_list, key=lambda x: base_list.index(x) if x in base_list
else len(base_list)+1)
['a', 'b', 'c', 'q', 's', 'f', 'l', 'x']

Combine list elements to create a nested list

Given a python list with alternating values of two types:
list = ['Q', '0.963', 'R', '0.020', 'K', '0.015', 'E', '0.001']
How can I combine every two values to get something like this:
new_list = [['Q', '0.963'], ['R', '0.020'], ['K', '0.015'], ['E', '0.001']]
You can simply use range()'s step parameter and list indexing.
list2 = ['Q', '0.963', 'R', '0.020', 'K', '0.015', 'E', '0.001']
new_list=[list2[i:i+2] for i in range(0,len(list2),2)]
print(new_list)
Output:
[['Q', '0.963'], ['R', '0.020'], ['K', '0.015'], ['E', '0.001']]
Using zip() and list slicing:
result = [[i, j] for i, j in zip(my_list[::2], my_list[1::2])]
Or just:
>>> list(zip(my_list[::2], my_list[1::2]))
[('Q', '0.963'), ('R', '0.020'), ('K', '0.015'), ('E', '0.001')]

Joining elements in list of Strings in loop with a condition

Every 2 elements should be joined in a loop till the end of the list
This is what i have been trying to do
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
for i in range(len(items)+1):
items[i]=items[i]+items[i+1]
i=i+2
print(items)
Expected Output: ['ab' , 'cd' , 'ef' , 'gh' , 'ij']
You can supply another argument to range to specify the increment ("step"):
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
res = []
for i in range(0, len(items), 2):
res.append(items[i] + items[i + 1])
print(res)
# ['ab', 'cd', 'ef', 'gh', 'ij']
Or, better yet, use a list comprehension instead:
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
items = [items[i] + items[i + 1] for i in range(0, len(items), 2)]
print(items)
# ['ab', 'cd', 'ef', 'gh', 'ij']
you can do it using list comprehension like this:
items = [items[i] + items[i+1] for i in range(0, len(items), 2)]
A solution with regex:
>>> import re
>>> items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
>>> re.findall('.{1,2}', ''.join(items))
['ab', 'cd', 'ef', 'gh', 'ij']
Your thought process is correct, I would double check your print statement. You are printing items, but are updating items[i] to items[i] + items[i+1]. I believe you want to print the variable you are updating.
First of all, you need to know that len() returns the number of item, not the size of the array (remember that the array index starts at 0). Here, if you want, you can look at the docs for the len() function.
Next, just to inform you, you can use the method append() to insert an object into the final position of the array. Here you can find some info on arrays.
Moreover, I want to add that in python, when using range, you can take advantage of the step value that you can pass to range: range(start, stop, step). You can read more about it here.
Said so, I would do something as follow:
output=[]
for i in range(0, len(items), 2):
output.append(items[i]+items[i+1]);
print(output)

Take every nth block from list

Given a list:
import string
a = list(string.ascii_lowercase)
What is the Pythonic way to return every nth block of m elements? Note that this is different from just returning every nth element.
Desired result of taking every 1st of 3 blocks of 3 elements (take 3, skip 6, take 3, skip 6...):
['a', 'b', 'c', 'j', 'k', 'l', 's', 't', 'u']
I can get to this as:
import itertools
s1 = a[::9]
s2 = a[1::9]
s3 = a[2::9]
res = list(itertools.chain.from_iterable(zip(s1,s2, s3)))
Is there a cleaner way?
For a fixed order of select and skip, you can wrap indices taking the modulo on the total length of the window (9 here) and select only those beneath the given threshold, 3:
lst = [x for i, x in enumerate(a) if i % 9 < 3]
print(lst)
# ['a', 'b', 'c', 'j', 'k', 'l', 's', 't', 'u']
You can make this into a function that makes it more intuitive to use:
def select_skip(iterable, select, skip):
return [x for i, x in enumerate(iterable) if i % (select+skip) < select]
print(select_skip(a, select=3, skip=6))
# ['a', 'b', 'c', 'j', 'k', 'l', 's', 't', 'u']
Perhaps just writing a simple generator is the most readable
def thinger(iterable, take=3, skip=6):
it = iter(iterable)
try:
while True:
for i in range(take):
yield next(it)
for i in range(skip):
next(it)
except StopIteration:
return
This has the advantage of working even if the input is infinite, or not slicable (e.g. data coming in from a socket).
more_itertools is a third-party library that implements itertools recipes and other helpful tools such as more_itertools.windowed.
> pip install more_itertools
Code
import string
from more_itertools import windowed, flatten
m, n = 3, 6
list(flatten(windowed(string.ascii_lowercase, m, step=m+n)))
# ['a', 'b', 'c', 'j', 'k', 'l', 's', 't', 'u']
windowed naturally steps one position per iteration. Given a new step by advancing beyond the overlaps (m), the windows are appropriately determined.
You can do it using some generic "chunks" recipe:
windows = chunks(original_iter, n=3)
Now that you've windowed you're data as you think of it, use islice's second variant for its' 'step' capabilities:
# flattens the list as well using chain
result = chain.from_iterable(islice(windows, 0, None, 2))
You can use a list comprehension and create a function that does this for any skip, take and list values:
import string
import itertools
a = list(string.ascii_lowercase)
def everyNthBlock(a, take, skip):
res = [a[i:i + take] for i in range(0, len(a) ,skip + take)]
return list(itertools.chain(*res))
print(everyNthBlock(a, 3, 6))
#^^^^ => ['a', 'b', 'c', 'j', 'k', 'l', 's', 't', 'u']
print(everyNthBlock(a, 4, 7))
#^^^^ => ['a', 'b', 'c', 'd', 'l', 'm', 'n', 'o', 'w', 'x', 'y', 'z']
Using incomprehensible list comprehension :D
m, n = 3, 3
[elem for blockstart in range(0, len(a), m*n) for elem in a[blockstart:blockstart+n]]
#> ['a', 'b', 'c', 'j', 'k', 'l', 's', 't', 'u']

Categories