Related
So i have these 2 lists:
list1 = [81, 68, 53, 28, 19, 7, 2, 0]
list1 is fine and nothing would need to happen as there's no numbers (1 up, or 1 below) from any of the nums in list.
list2 = [68, 67, 53, 21, 20, 19, 9, 7, 1, 0]
Whereas in list2 i have (68,67) (21,20,19) & (1,0)
How can i have a new list be filled with the "extra"(starting from high) next-in-line numbers?
It may or may not be important but just to point out that the list2 will always be sorted from high to low before reaching the below code.
Here's what ive got so far:
####################################################
list2 = [68, 67, 53, 21, 20, 19, 9, 7, 1, 0]
numbs_list= []
complete = False
i = 0
# start = 0
# end = len(list2) -1
while complete is False:
if len(list2) > 1:
# if index 1 is next-in-line to index 0
# if 67 == 67(68 -1)
if list2[i +1] == list2[i] -1:
# add 68 to numbs list
numbs_list.append(list2[i])
# remove 68 from list2
list2.pop(list2.index(list2[i]))
else:
list2.pop(list2.index(list2[i]))
else:
complete = True
# start += 1
# if start == end:
# complete = True
# from list2 this is what i need numbs_list to have stored once the while loop is done:
numbs_list = [68, 21, 20, 1]
# whats left in list2 does not matter after the numbs_list is finalised as list2 will eventually get cleared and repopulated.
####################################################
"next-in-line" may be the wrong wording but i think you get the idea of what i mean. if not here's some examples:
1,0
11,10,9
23,22
58,57
91,90,89,88
notice how theres no room between any & all of those nums? because theyre all next-in-line.
Try iterating through each index and comparing it to the index - 1. and append the index to the extra list if they are only 1 apart
list2 = [68, 67, 53, 21, 20, 19, 9, 7, 1, 0]
extra = []
for i in range(1,len(list2)):
if list2[i-1] == list2[i] + 1:
extra.append(list2[i-1])
print(extra)
Using list comprehension:
list2 = [68, 67, 53, 21, 20, 19, 9, 7, 1, 0]
extra = [list2[i-1] for i in range(1,len(list2)) if list2[i-1] == list2[i]+ 1]
print(extra)
Output
[68, 21, 20, 1]
You have made this far more complicated than it needs to be. Just remember what the last number was, and if the new number is one below it, then add it to your lists.
list2 = [68, 67, 53, 21, 20, 19, 9, 7, 1, 0]
maybe = []
last = -99
curr = []
for n in list2:
if n == last-1:
maybe[-1].append(n)
else:
maybe.append( [n] )
last = n
numbs_list = [k for k in maybe if len(k) > 1]
print(numbs_list)
This was fun with functools.reduce. Pun definitely intended.
from functools import reduce
lst = [68, 67, 53, 21, 20, 19, 9, 7, 1, 0]
lst2 = reduce(
lambda acc, x:
[[x]] if len(acc) == 0 else
acc[:-1]+[acc[-1]+[x]] if len(acc[-1]) != 0 and acc[-1][-1] == x+1 else
acc+[[x]],
lst,
[]
)
# [[68, 67], [53], [21, 20, 19], [9], [7], [1, 0]]
Once we have this, everything else you might want to figure out is trivial.
lst3 = [y for x in lst2 for y in x if len(x) == 1]
# [53, 9, 7]
lst4 = [x[:-1] for x in lst2 if len(x) > 1]
# [[68], [21, 20], [1]]
lst5 = [y for x in lst2 for y in x[:-1] if len(x) > 1]
# [68, 21, 20, 1]
We might also implement a general purpose split_when function and use that rather than functools.reduce.
def split_when(lst, pred):
if len(lst) == 0: return
last = lst[0]
lst2 = [last]
for x in lst[1:]:
if pred(last, x):
yield lst2
lst2 = [x]
last = x
else:
lst2.append(x)
last = x
if len(lst2) > 0:
yield lst2
lst2 = list(split_when(lst, lambda a, b: a - b > 1))
# [[68, 67], [53], [21, 20, 19], [9], [7], [1, 0]]
I am asked to replace some elements in a 2d list with elements from a 1D list. The rule is to replace the first row of list1 with list2. The elements in the next row will be replaced by 3 times each element from the previous row. So the second row will contain 33, 39, 45,and 51, and the third row 99, 117, 135, and 153... all the way to the 10th row.
Here is my code:
list1 = [[0]*4]*10
list2 = [11,13,15,17]
list1[0] = list2
i = 1
for i in range(9):
j = 0
for j in range(4):
list1[i][j] = list2[j]*(3**i)
j+=1
i+=1
The result I got from this code basically only contains the correct first row, but the rest of the rows after that are all 72171, 85293, 98415, and 111537 (which is 3 to the 8th). I am not sure which part is giving me the error.
Here are some comments on your code and examples of how to make it do what the question describes:
(1.) References vs copies: list1 = [[0]*4]*10 creates a single 4-element list and populates list1 with 10 references to it (NOT 10 copies of it).
For an example of what this implies, watch this:
list1 = [[0]*4]*10
list1[1][0] = 33
list1[1][1] = 39
list1[1][2] = 45
list1[1][3] = 51
print(list1)
... gives this:
[[33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51], [33, 39, 45, 51]]
In other words, updating one list element within list1 updates them all, since each element of list1 is just a reference to the same list.
If this is not what you want, you can use list1 = [[0]*4 for _ in range(10)] instead to give you a distinct list (10 in total) for each index in list1:
list1 = [[0]*4 for _ in range(10)]
list1[1][0] = 33
list1[1][1] = 39
list1[1][2] = 45
list1[1][3] = 51
print(list1)
... gives:
[[0, 0, 0, 0], [33, 39, 45, 51], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Your code as written would tend to imply that the second approach above is what is needed.
(2.) Code fix assuming nested lists cannot be replaced: It's unclear from the question whether whether you are allowed to replace each list in list1 to get the desired values, or if you are expected to leave the nested lists in place and simply modify their numerical contents.
If list replacement is not allowed, then your code can be rewritten like this:
list1 = [[0]*4 for _ in range(10)]
list2 = [11,13,15,17]
list1[0][:] = list2
for i in range(9):
for j in range(4):
list1[i + 1][j] = list2[j]*(3**(i + 1))
print(list1)
... giving:
[[11, 13, 15, 17], [33, 39, 45, 51], [99, 117, 135, 153], [297, 351, 405, 459], [891, 1053, 1215, 1377], [2673, 3159, 3645, 4131], [8019, 9477, 10935, 12393], [24057, 28431, 32805, 37179], [72171, 85293, 98415, 111537], [216513, 255879, 295245, 334611]]
Note that these changes were made to your code:
Changed the initialization of list1 to allocate 10 distinct nested lists.
Eliminated i = 1 (not needed because the for loop takes care of initializing i), j = 0 (similar reason), j+=1 (not needed because the for loop takes care of updating j) and i+=1 (similar reason).
Changed list1[i][j] to list1[i + 1][j] to fix the indexing.
Changed 3**i to 3**(i + 1) to calculate the correct multiplier.
(3.) Code fix assuming nested lists can be replaced: In this case, your looping logic can be simplified and you don't need to use nested lists when initializing list1.
Here is a long-hand way to do what you ask which will overwrite the contents of list1 with new nested lists that have the desired values:
list1 = [None] * 10
list2 = [11,13,15,17]
list1[0] = list2
mul = 3
for i in range(1, len(list1)):
temp = [0] * len(list2)
for j in range(len(list2)):
temp[j] = mul * list2[j]
list1[i] = temp
mul *= 3
print(list1)
Here is a way that uses a list comprehension inside a loop:
list1 = [None] * 10
list2 = [11,13,15,17]
list1[0] = list2
mul = 3
for i in range(1, len(list1)):
list1[i] = [mul * v for v in list2]
mul *= 3
print(list1)
And finally, here is a very compact nested list comprehension approach:
list1 = [None] * 10
list2 = [11,13,15,17]
list1 = [[(3 ** i) * v for v in list2] for i in range(len(list1))]
the reason that happened is that you are working on a copy of 0 elements of list1 , so whenever you modify further everything will be replaced with the new value, it's not a copy its the same object in reference.
for i in range(9):
tmp = []
for j in range(4):
tmp.append(list2[j]*(3**i))
list1[i] = tmp
this is all you need
You can make this much easier using numpy arrays:
import numpy as np
list1 = np.zeros((10,4), dtype=int)
list1[0] = [11,13,15,17]
for i in range(1,10):
list1[i] = 3*list1[i-1]
I have a random list which is;
newList = [2, 44, 28, 32, 46, 31]
I have a random value which has to be in this way;
{1:8, 2:7, 3:6, 4:5, 5:4, 6:3, 7:2, 8:1}
So if a value in the list is 4, it needs to replace with 5 If a value in the list is 2, it needs to replace with 7 for each one.
when I try this code:
newList1 = list(map(int, newList))
nnList = []
for i in newList1:
i = str(i).split(',')
for y in list(map(str, i)):
for n in y:
print(n)
if n == '1':
n = 8
elif n == '2':
n = 7
elif n == '6':
n = 3
elif n == '3':
n = 6
elif n == '4':
n = 5
elif n == '5':
n = 4
elif n == '7':
n = 2
elif n == '8':
n = 1
nnList.append(n)
print(nnList)
When I run this code, I have this output: [7, 5, 5, 7, 1, 6, 7, 5, 3, 6, 8]
But I need to get in this way: [7, 55, 71, 67, 53, 68]
How can I do it?
Here is what you can do:
newList = [2, 44, 28, 32, 46, 31]
d = {1:8, 2:7, 3:6, 4:5, 5:4, 6:3, 7:2, 8:1}
l = [int(''.join([str(d[int(g)]) for g in str(n)])) for n in newList]
print(l)
Output:
[7, 55, 71, 67, 53, 68]
The short answer is: you need to combine the new digits for a number into a single value before adding it to your list.
The longer answer is that you are doing far too many conversions. You don't need to convert the entire list of int values to a single string, and newList is already a list of int values; you don't need to build newList1.
nnList = []
for i in newList:
newNum = int(''.join(str(9-int(x)) for x in str(i)))
nnList.append(newNum)
A more basic approach using if else and looping can be,
l1= [2, 44, 28, 32, 46, 31]
dict1={1:8, 2:7, 3:6, 4:5, 5:4, 6:3, 7:2, 8:1}
l2=[]
for n,i in enumerate(l1):
str1=str(i)
if len(str1)>1:
str2=""
for j in str1:
if int(j) in dict1:
str2+=str(dict1[int(j)])
l1[n]=int(str2)
else:
if i in dict1:
l1[n]=dict1[i]
print(l1)
output:
[7, 55, 71, 67, 53, 68]
newlist = [2, 44, 28, 32, 46, 31]
repl = {1:8, 2:7, 3:6, 4:5, 5:4, 6:3, 7:2, 8:1}
for i, el in enumerate(newlist):
newlist[i] = repl.get(el, newlist[i])
print(newlist)
repl.get(el, newlist[1]) means: try to find el in the repl dictionary, if it is not in the dictionary, use newlist[i] (the original value) instead, thus replacing the value by itself.
I have a list that needs to be split into 4 separate lists with maximum size of 21 depending on the amount of items in the original list.
The master list can have from 1 to 84 items.
I want the items to start in a and fill up to a maximum of 21 in a, b, c, d
I have the following code that can split the items up no problem but I want to know if there is a shorter way to do this. I am repeating code a lot except the range.
codes = [x for x in range(80)] # range anywhere between 1-84
print(len(codes))
a = []
b = []
c = []
d = []
for i in range(0, 21):
try:
a.append(codes[i])
except IndexError:
pass
for i in range(21, 42):
try:
b.append(codes[i])
except IndexError:
pass
for i in range(42, 63):
try:
c.append(codes[i])
except IndexError:
pass
for i in range(63, 84):
try:
d.append(codes[i])
except IndexError:
pass
print(len(a), len(b), len(c), len(d))
print(a)
print(b)
print(c)
print(d)
Before that I had this code that works great for the whole 84 items as the order is not important..
a = []
b = []
c = []
d = []
for a1, b1, c1, d1 in zip(*[iter(codes)]*4):
a.append(a1)
b.append(b1)
c.append(c1)
d.append(d1)
However if i have say 4 items, it will add 1 to each
a = [0]
b = [1]
c = [2]
d = [3]
What I would like to obtain is
a = [0, 1, 2, 3]
b = []
c = []
d = []
You can simply use sublist
a = codes[0: 21]
b = codes[21:42]
c = codes[42:63]
d = codes[63:84]
This will be fine for your requirement
#SajalPreetSinghs answer is correct for a simple use case like OP's but it also has some disadvantages when it comes to scalability.
For example:
When you need 20 sublists instead of the actual 4. You would have to add 16 more lines and specify 2 different numbers per line each!
Now imagine you already extended the code to 20 sublists but now you want the maximum item count per sublist to be 37 instead of 21 - you would have to change 2*20 = 40 numbers!
Improved scalability with generators
So if you want something with a better scalability you could use the following code which makes usage of generators:
Code
def sublist(orig_list, list_of_subs, max_items_per_list):
def sublist_generator():
for sublist in list_of_subs:
yield sublist
sublist = sublist_generator()
current_sublist = next(sublist)
for element in orig_list:
current_sublist.append(element)
if len(current_sublist) == max_items_per_list: # current list is full
current_sublist = next(sublist) # so let current point to the next list
Setup and usage
import random
start = 1
stop = random.randint(2, 85) # generate random int inclusively 2 and 85
codes = [x for x in range(start, stop)] # stop is exclusive: range(1, 85) == [1, 2, ..., 84]
a, b, c, d = [], [], [], []
sublists = [a, b, c, d] # *1
sublist(codes, sublists, 21)
for sublist in sublists:
print(sublist)
Better scalability because
If you want to change the number of items per sublist you only have to pass in the new maximum number.
If you want to increase the number of sublists you only have to add more of them to the sublists variable which you pass to the function (see *1).
If you need this code more often it's no problem because you can comfortably call the function.
I hope this helps someone!
Cheers
winklerrr
You could use the zip_longest function from itertools
from itertools import zip_longest
master_list = range(0, 84)
iterables = [iter(master_list)] * 21
slices = zip_longest(*iterables, fillvalue=None)
for slice in slices:
print("slice", slice)
# slice (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
# slice (21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41)
# slice (42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62)
# slice (63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83)
You can do it with using a list comprehension:
MAX_SIZE = 21
l = list(range(80))
l1,l2,l3,l4 = [l[i*MAX_SIZE: (i+1)*MAX_SIZE] for i in range(4)]
#l1=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,20],
# ....
#l4=[63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
What I would do would be just put it in one loop.
if index is 0 to 83
for i in range(0,84):
if i>=0 and i<=20:
a.append(codes[i])
elif i>20 and i<=41:
b.append(codes[i])
elif i>41 and i<=62:
c.append(codes[i])
elif i>62 and i<=83:
d.append(codes[i])
A solution using a list comprehension:
i_list = range(0,84)
r = 21
res = [i_list[i:i+r] for i in range(0,len(i_list),r)]
I have a set of lists in Python and I want to shuffle both of them but switching elements in same positions in both lists like
a=[11 22 33 44] b = [66 77 88 99]
*do some shuffeling like [1 3 0 2]*
a=[22 44 11 33] b = [77 99 66 88]
Is this possible?
Here's a solution that uses list comprehensions:
>>> a = [11, 22, 33, 44]
>>> b = [66, 77, 88, 99]
>>> p = [1, 3, 0, 2]
>>>
>>> [a[i] for i in p]
[22, 44, 11, 33]
>>>
>>> [b[i] for i in p]
[77, 99, 66, 88]
>>>
You can use zip in concert with the random.shuffle operator:
a = [1,2,3,4] # list 1
b = ['a','b','c','d'] # list 2
c = zip(a,b) # zip them together
random.shuffle(c) # shuffle in place
c = zip(*c) # 'unzip' them
a = c[0]
b = c[1]
print a # (3, 4, 2, 1)
print b # ('c', 'd', 'b', 'a')
If you want to retain a,b as lists, then just use a=list(c[0]). If you don't want them to overwrite the original a/b then rename like a1=c[0].
Expanding upon Tom's answer, you can make the p list easily and randomize it like this:
import random
p = [x for x in range(len(a))]
random.shuffle(p)
This works for any size lists, but I'm assuming from your example that they're all equal in size.
Tom's answer:
Here's a solution that uses list comprehensions:
a = [11, 22, 33, 44]
b = [66, 77, 88, 99]
p = [1, 3, 0, 2]
[a[i] for i in p]
[22, 44, 11, 33]
[b[i] for i in p]
[77, 99, 66, 88]
a=[11,22,33,44]
order = [1,0,3,2] #give the order
new_a = [a[k] for k in order] #list comprehension that's it
You just give the order and then do list comprehension to get new list