Advanced list comprehension - python

A list of ints is entered into the program 1 at a time, for example:
[1, 3, 1, 4, 4, 3, 1]
Task:
Print a list that contains exactly the same numbers as the given list,
but rearranged so that every 3 is immediately followed by a 4. The 3's must not move index places, but every other number may move.
The output of the example should read:
[1, 3, 4, 1, 1, 3, 4]
My code so far is only able to complete the rules 1 and 2. How could my code be modified to cater to this?
newList=[]
n=0
numCount= int(input())
while True:
try:
n = int(input())
except:
break
if len(newList) !=(numCount):
if n == 3:
newList.append(3)
newList.append(4)
else:
newList.append(n)
print(newList)

I suggest you to first get all the indexes of 3 and 4 in the input list, then swap each element following a 3 with a 4. It gives the following code, which is quite short and easily readable:
a = [1, 3, 1, 4, 4, 3, 1]
# Get the indexes of 3 and 4 in the list
indexesOf3 = [i for i,elem in enumerate(a) if elem == 3]
indexesOf4 = [i for i,elem in enumerate(a) if elem == 4]
# Swap each element following a 3 with a 4
for i3,i4 in zip(indexesOf3,indexesOf4):
a[i3+1], a[i4] = a[i4], a[i3+1]
print(a)
# [1, 3, 4, 1, 1, 3, 4]
Note: this code example modifies the input list, but obviously it can be easily updated into a function returning a new list and keeping the input list as it is.

Here is a function doing exactly that:
def arrange_list(my_list):
# Copy the whole list
arranged_list=myList.copy()
# Find the all 4s
index_of_4s=[i for i, x in enumerate(arranged_list) if x == 4]
next_4=0
# Loop over the whole list
for i in range(len(arrangedList)):
if(arrangedList[i]==3): # We only care about 3s
# We swap the previously found 4 with a 1
arranged_list[index_of_4s[next_4]]=arranged_list[i+1]
arranged_list[i+1]=4
# We target the next 4
next_4=next_4+1
return arranged_list
If we test it with your example we get:
myList=[1, 3, 1, 4, 4, 3, 1]
arrange_list(myList)
#> [1, 3, 4, 1, 1, 3, 4]

You're question is not really well defined, and a lost of scenario are not taken into account. This code does the work quite simply, the idea is to create a new list.
-Find the position of the 3 in the input
-Place the 3, followed by a 4 in the new list
-Place the remaining elements.
input_list = [1, 3, 1, 4, 4, 3, 1]
# Check the number of 3 and 4
n3 = input_list.count(3)
n4 = input_list.count(4)
if n3 > n4:
for i in range(n3-n4):
input_list.append(4)
elif n4 > n3:
for i in range(n4-n3):
input_list.append(3)
# Now let's look at the order. The 3 should not move and must be followed by a 4.
# Easy way, create a new list.
output_list = [None for i in range(len(input_list))]
# Then I'm using numpy to go faster but the idea is just to extract the ids are which the 3 are placed.
import numpy as np
# Place the 3 and the 4
for elt_3 in np.where(np.asarray(input_list) == 3)[0]:
output_list[elt_3] = 3
output_list[elt_3+1] = 4 # Must be sure that the input_list does not end by a 3 !!!
# Then we don't care of the position for the other numbers.
other_numbers = [x for x in input_list if x != 3 and x != 4]
for i, elt in enumerate(output_list):
if elt is None:
output_list[i] = other_numbers[0]
del other_numbers[0]
print (output_list)
In a more compact version with a single loop, it gives:
input_list = [1, 3, 1, 4, 4, 3, 1]
position_3 = np.where(np.asarray(input_list) == 3)[0]
other_numbers = [x for x in input_list if x != 3 and x != 4] # Not 3 and not 4
output_list = [None for i in range(len(input_list))]
for i, elt in enumerate(output_list):
if elt == 4:
continue
elif i not in position_3:
output_list[i] = other_numbers[0]
del other_numbers[0]
else:
output_list[i] = 3
output_list[i+1] = 4

Related

How to remove specific elements from python list and add those elements into last of the list?

There are many solution available on internet for the above question but i am building my own logic here and have written below solution so far but my code is not dynamic e.g my code is only working for num = 2.
How can we write code for dynamic user input using below logic. Any suggestion will be appreciated.
My Code:
l = [1,2,3,4,5,6,7]
l1 = []
num = 2
print('original list is:', l)
for i in range(0,num-1):
rem = l.pop(i)
l1.append(rem)
for i in range(0,num-1):
rem = l.pop(i)
l1.append(rem)
print('poup list is',l1)
l.extend(l1)
print('reverse list is',l)
My Output: (when num = 2)
original list is: [1, 2, 3, 4, 5, 6, 7]
poup list is [1, 2]
reverse list is [3, 4, 5, 6, 7, 1, 2]
My Output: (when num = 3)
original list is: [1, 2, 3, 4, 5, 6, 7]
poup list is [1, 3, 2, 5]
reverse list is [4, 6, 7, 1, 3, 2, 5]
Expected Solution:
l = [1,2,3,4,5,6,7]
num = 2 (This number can be dynamic, User can pass any number)
Output: l = [3,4,5,6,7,1,2]
If user passed num = 3 then output should be like below:
Output: l = [4,5,6,7,1,2,3]
Even though the poup list and reverse list doesn't seem to represent the lists with the best names, the following code snippet should solve your problem:
l = [1,2,3,4,5,6,7]
print("Original list: {}".format(l) )
num = input("Enter value of 'num': " )
l1 = []
for i in range(0,int(num)):
rem = l.pop(0)
l1.append(rem)
print('poup list is',l1)
l.extend(l1)
print('reverse list is',l)
Here num is dynamically stored. The only minor correction to your for loop is that l should always pop the first element. This is because once an element is popped, the second element becomes the first element of your list. But due to incrementation in the value of i, it just skips that element.
Here's a sample output:
Original list: [1, 2, 3, 4, 5, 6, 7]
Enter value of 'num': 4
poup list is [1, 2, 3, 4]
reverse list is [5, 6, 7, 1, 2, 3, 4]
​
You could use a while loop. Problem with your code is that index positions changed when popping, in case of num = 2 it was fine, but then it messed up with new positions.
l = [1,2,3,4,5,6,7]
l1 = []
num = 3
print('original list is:', l)
y = 0
while y != num:
rem = l.pop(0)
y +=1
l1.append(rem)
print(l)
print('poup list is',l1)
l.extend(l1)
print('reverse list is',l)

Repeat values in array until specific length [duplicate]

This question already has answers here:
How to replicate array to specific length array
(4 answers)
Closed 2 years ago.
I need some kind of function or little tip for my problem.
So I got a list let's say
[1,2,3,4]
but I need this array to be longer with the same elements repeated so let's say I need an array of length 10 so it becomes:
[1,2,3,4,1,2,3,4,1,2]
So I need to extend the list with the same values as in the list in the same order
returnString = the array or string to return with extended elements
array = the basic array which needs to be extended
length = desired length
EDIT:
returnString = ""
array = list(array)
index = 0
while len(str(array)) != length:
if index <= length:
returnString += array[index]
index += 1
else:
toPut = index % length
returnString.append(array[toPut])
index += 1
return returnString
This is simple with itertools.cycle and itertools.islice:
from itertools import cycle, islice
input = [1, 2, 3, 4]
output = list(islice(cycle(input), 10))
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
You can use itertools.cycle to iterate repeatedly over the list, and take as many values as you want.
from itertools import cycle
lst = [1, 2, 3, 4]
myiter = cycle(lst)
print([next(myiter) for _ in range(10)])
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
You can also use it to extend the list (it doesn't matter if you append to the end while you are iterating over it, although removing items would not work).
from itertools import cycle
lst = [1, 2, 3, 4]
myiter = cycle(lst)
for _ in range(6):
lst.append(next(myiter))
print(lst)
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
One way could be:
Iterate over the desired length - len(x_lst), So you have 10 - 4 = 6 (new elements to be added). Now since the list element should repeat, you can append the x_lst elements on the go by the indices (0,1,2,3,4,5).
x = [1,2,3,4]
length = 10
for i in range(length - len(x)):
x.append(x[i])
print(x)
OUTPUT:
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
Try this:
n = 10
lst =[1,2,3,4]
new_lst = [lst[i%len(lst)] for i in range(n)]
print(new_lst)
Output:
[1, 2, 3, 4, 1, 2, 3, 4, 1, 2]
I will show a tip to you:
If you have this array [1,2,3,4] so you can create a separated newArray that get this value and fill the newArray with this repeated values.
How? Loop! I think for can do this to you, just point the array and newArray to it knows which it will fill.
NumOfValues = int(input("Number of Values: "))
List1 = [1,2,3,4]
List2 = []
Count = 0
while len(List2) < NumOfValues:
List2.append(List1[Count])
Count += 1
if Count > len(List1) - 1:
Count = 0
print(List2)
First multiply the list by the number of times it needs to be repeated. If that's not the desired length, extend it with the appropriate slice of the list.
old_len = len(original)
new_len = 10
result = original * new_len // old_len
if new_len % old_len != 0:
result += original[:new_len % old_len]

Combining array elements in a particular way, and recording it

I am given a 1D array of numbers.
I need to go through the array adding each consecutive element to form a sum. Once this sum reaches a certain value, it forms the first element of a new array. The sum is then reset and the process repeats, thus iterating over the whole array.
For example if given:
[1, 3, 4, 5, 2, 5, 3]
and requiring the minimum sum to be 5,
the new array would be:
[8, 5, 7]
Explicity: [1 + 3 + 4, 5, 2 + 5]
I then also need to keep a record of the way the elements were combined for that particular array: I need to be to take a different array of the same length and combine the elements in the same way as above.
e.g. give the array
[1, 2, 1, 1, 3, 2, 1]
I require the output
[4, 1, 5]
Explicity: [1 + 2 + 1, 1, 3 + 2]
I have accomplished this with i loops and increment counters, but it is very ugly. The array named "record" contains the number of old elements summed to make each element of the new array i.e. [3, 1, 2]
import numpy as np
def bin(array, min_sum):
num_points = len(array)
# Create empty output.
output = list()
record = list()
i = 0
while i < num_points:
sum = 0
j = 0
while sum < min_sum:
# Break out if it reaches end of data whilst in loop.
if i+j == num_points:
break
sum += array[i+j]
j += 1
output.append(sum)
record.append(j)
i += j
# The final data point does not reach the min sum.
del output[-1]
return output
if __name__ == "__main__":
array = [1, 3, 4, 5, 2, 5, 3]
print bin(array, 5)
I would advice you to simply walk through the list. Add it to an accumulator like the_sum (do not use sum, since it is a builtin), and in case the_sum reaches a number higher than the min_sum, you add it, and reset the_sum to zero. Like:
def bin(array, min_sum):
result = []
the_sum = 0
for elem in array:
the_sum += elem
if the_sum >= min_sum:
result.append(the_sum)
the_sum = 0
return result
The lines where the accumulator is involved, are put in boldface.
I leave combining the other array the same way as an exercise, but as a hint: use an additional accumulator and zip to iterate over both arrays concurrently.
Here is a straightforward solution. which computes a list of boolean values where the value is true when accumulated element equals or exceeds the target value and calc computes an accumulation using this list.
def which(l, s):
w, a = [], 0
for e in l:
a += e
c = (a >= s)
w.append(c)
if c:
a = 0
return w
def calc(l, w):
a = 0
for (e, c) in zip(l, w):
a += e
if c:
yield a
a = 0
here is an interactive demonstration
>>> l1 = [1, 3, 4, 5, 2, 5, 3]
>>> w = which(l1, 5)
>>> w
[False, False, True, True, False, True, False]
>>> list(calc(l1, w))
[8, 5, 7]
>>> l2 = [1, 2, 1, 1, 3, 2, 1]
>>> list(calc(l2, w))
[4, 1, 5]
You can use short solutions I found out after a long struggle with flattening arrays.
For getting bounded sums use:
f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])<x]
This outputs:
>>> f = lambda a,x,j,l: 0 if j>=l else [a[i] for i in range(j,l) if sum(a[j:i])< x]
>>> a= [1, 3, 4, 5, 2, 5, 3]
>>> f(a,5,0,7)
[1, 3, 4]
>>> sum(f(a,5,0,7))
8
>>> sum(f(a,5,3,7))
5
>>> sum(f(a,5,4,7))
7
>>>
To get your records use the function:
>>> y = lambda a,x,f,j,l: [] if j>=l else list(np.append(j,np.array(y(a,x,f,j+len(f(a,x,j,l)),l))))
From here, you can get both array of records and sums:
>>> listt=y(a,5,f,0,len(a))
>>> listt
[0.0, 3.0, 4.0, 6.0]
>>> [sum(f(a,5,int(listt[u]),len(a))) for u in range(0,len(listt)-1)]
[8, 5, 7]
>>>
Now, the bit of magic you can even use it as an index-conditional boundary for the second vector:
>>> b=[1, 2, 1, 1, 3, 2, 1]
>>> [sum(f(b,5,int(listt[u]),int(listt[u+1]))) for u in range(0,len(listt)-1)]
[4, 1, 5]
>>>

List comprehension with for loop

How can I double the first n odd numbers in a list using list comprehension?
Here is my solution:
>>> n = 2
>>> lst = [1, 2, 3, 4, 5, 6]
>>> lst = [num for num in lst if num % 2 == 1] + [num for num in lst if num % 2 == 0]
>>> lst = [num * 2 for num in lst[:n]] + lst[n:]
>>> print(lst)
[2, 6, 5, 2, 4, 6]
You can see that I can't keep the same order of lst anymore...
More example:
n = 2
lst = [2, 2, 2, 2, 1, 2, 3]
output: lst = [2, 2, 2, 2, 2, 2, 6]
Solution for the original requirement to *“double the first n numbers in a list if it’s odd”:
Since you do not want to remove any items from your original list, you cannot use the filter of the list comprehension syntax (the if after the for). So what you need to do instead is simply transform the item you are putting into the target list.
Your logic is something like this for an element x at index i:
def transform(x, i, n):
if i < n:
if x % 2 == 1:
return x * 2
return x
So you can use that exact function and use it in your list comprehension:
>>> n = 2
>>> lst = [1, 2, 3, 4, 5, 6]
>>> [transform(x, i, n) for i, x in enumerate(lst)]
[2, 2, 3, 4, 5, 6]
And of course, you can put this also inline into the list comprehension:
>>> [x * 2 if i < n and x % 2 == 1 else x for i, x in enumerate(lst)]
[2, 2, 3, 4, 5, 6]
First n odd numbers:
If you want to find the first n odd numbers, you cannot solve this like this. In order to solve this, you need to actually remember how many odd numbers you encountered before while going through the list. This means that you need to have some kind of “memory”. Such a thing is not a good fit for a list comprehension since list comprehensions are supposed to transform one item at a time without having side effects.
So instead, you would simply do this the straightforward way:
n = 2
lst = [2, 2, 2, 2, 1, 2, 3]
result = []
for x in lst:
if x % 2 == 1 and n > 0:
result.append(x * 2)
n -= 1
else:
result.append(x)
print(result) # [2, 2, 2, 2, 2, 2, 6]
For this to work, you'll need to keep count of odd numbers that you've already seen. For example, you could instantiate the itertools.count generator and advance it each time the odd number is encountered:
from itertools import count
def f(l, n):
odd = count()
return [x * 2 if x % 2 and next(odd) < n else x for x in l]
>>> f([1, 2, 3, 4, 5, 6], 2)
[2, 2, 6, 4, 5, 6]
>>> f([2, 2, 2, 2, 1, 2, 3], 2)
[2, 2, 2, 2, 2, 2, 6]
Use the ternary operator.
lst = [1, 2, 3, 4, 5, 6]
lst = [x * 2 if x % 2 == 1 and i <= n else x for i, x in enumerate(lst)]
or
lst[:n] = [x * 2 if x % 2 == 1 else x for x in lst[:n]]
Update: Under the new requirement of doubling first n odd integers:
lst = [1, 2, 3, 4, 5, 6]
class Doubler:
def __init__(self, n):
self.n = n
def proc(self, x):
if self.n > 0 and x % 2:
self.n -= 1
return 2 * x
return x
# Double first 2 odd elements
d = Doubler(n=2)
res = [d.proc(x) for x in lst]
print(res)
# [2, 2, 6, 4, 5, 6]
Name things with specificity, and the logic is exposed.
How can I double the first n odd numbers in a list using list comprehension?
We have odd numbers: v for v in l if n%2. This is a filter.
We can take the first n of them using islice(odds, n). We call this a slice, other languages might call it "take". And doubling them is a per item operation, so a map. Join these operations and we arrive at one answer to your question:
[v*2 for v in islice((v for v in l if n%2), n)]
However, that isn't what you wanted. The issue is specificity; your question doesn't say what to do with other items than the first n odd ones, so I have just ignored them.
So what do do if we want a replication of all the items your question did not mention? This means we have three groups: early odds, late odds, and evens, all processed distinctly. The latter may be mixed in arbitrarily, while we know late odds come after early odds. It's impractical to split them in individual streams, as you've shown, since that doesn't preserve their relative order.
I'll apply a few more itertools functions to solve this problem.
from itertools import repeat, chain
oddfactors = chain(repeat(2, n), repeat(1))
outlist = [v*next(oddfactors) if v%2 else v
for v in inlist]
Note that the iterator oddfactors is read for each odd item, not even items, because the if-else expression doesn't evaluate the expression if it's not being used. The iterator is consumed and you need to create another to perform the work again.
It is possible to place the oddfactors iterator's creation (and entire scope) within the list comprehension, but the first way I can think of is incredibly ugly:
from itertools import repeat, chain
outlist = [v*next(oddfactors) if v%2 else v
for v,oddfactors in zip(
inlist,
repeat(chain(repeat(2, n), repeat(1)))
)]
The trick here is to ensure we create the chained iterator only once, then feed it into each mapping operation. This exercise sure didn't help readability or performance. Using a nested comprehension would make it a bit cleaner but there's still only the one iterator, so it's a misleading hack.
outlist = [v*next(oddfactors) if v%2 else v
for oddfactors in [chain(repeat(2, n), repeat(1))]
for v in inlist]
How about this?
n = 2
lst = [1, 2, 3, 4, 5, 6]
for i in range(n):
lst[i]= lst[i]*2
[num if num%2 else 2*num for num in list]. num if a if b else c will return a if b is true, otherwise c.

Rotating a list without using collection.deque

I want to make a script. The program should get some list L with values, and natural number N.
If N>0, the list's objects move N steps to left.
If N<0, the list's objects move abs(N) to the right.
If N=0 the list remain the same...
For example: For N=1 and L=[1,2,3,4,5], the output is [2,3,4,5,1].
For same list and N=-1 the output is [5,1,2,3,4]
I actually did it using collection.deque, but I want to do this with nothing but lists, for loop and 'if'.
I have problem to understand how to make the objects move.
l = input("enter list:")
N = input("enter number of rotations:")
import collections
d = collections.deque(l)
d.rotate(-N)
print d
You can use list slicing:
def rotate(L, N):
if not L or N % len(L) == 0:
return L
return L[N % len(L):] + L[:N % len(L)]
L = [1, 2, 3, 4, 5]
for N in range(-3, 4):
print(rotate(L, N))
Output:
[3, 4, 5, 1, 2]
[4, 5, 1, 2, 3]
[5, 1, 2, 3, 4]
[1, 2, 3, 4, 5]
[2, 3, 4, 5, 1]
[3, 4, 5, 1, 2]
[4, 5, 1, 2, 3]
Note that if you use a list, the time complexity of a rotation is linear to the number of elements in the list in general, since you have to move all existing elements. deque.rotate(), on the other hand, is O(k), with k being the number of steps. Therefore, if you need to rotate more than once deque.rotate() is the way to go.
This works:
result = l[N % len(l):]+l[:N % len(l)]
If using numpy is an option for you, there is a method called roll, which you might want to have a look at:
import numpy as np
array = np.arange(1, 6) # 1 to 5
print array
print np.roll(array, 0)
print np.roll(array, 2)
print np.roll(array, -2)
print np.roll(array, 17)
The result is as expected:
[1 2 3 4 5]
[1 2 3 4 5]
[4 5 1 2 3]
[3 4 5 1 2]
[4 5 1 2 3]
If you're after simple lists here's how you can do it in Python 3 (based on your P2 example).
L = list(map(int, input("Enter numbers: ").split(" ")))
N = int(input("Enter how many rotations to perform: "))
print(L[N:] + L[:N])
If you don't want to create new list, you can try in-place reverse,
def reverse(nums, start, end):
i = start
j = end - 1
while i < j:
tmp = nums[i]
nums[i] = nums[j]
nums[j] = tmp
i += 1
j -= 1
def rotate(nums, n):
if n == 0:
return nums
length = len(nums)
if n < 0:
n = length + n
reverse(nums, 0, n)
reverse(nums, n, length)
reverse(nums, 0, length)
return nums
>>> rotate([1, 2, 3, 4, 5], 1)
[2, 3, 4, 5, 1]
>>> rotate([1, 2, 3, 4, 5], -1)
[5, 1, 2, 3, 4]
thank you all ! :)
your answers are great and very useful for me.
here is my solution for this question (given that I do these scripts for a basic python course for biologists):
L = input('Enter list of numbers, please: ')
N = input('Enter how many places to rotate: ')
L1 = L[N:]
L2 = L[:N]
L = L1 + L2
print L

Categories

Resources