Related
I was curios about the question: Eliminate consecutive duplicates of list elements, and how it should be implemented in Python.
What I came up with is this:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
i = 0
while i < len(list)-1:
if list[i] == list[i+1]:
del list[i]
else:
i = i+1
Output:
[1, 2, 3, 4, 5, 1, 2]
Which I guess is ok.
So I got curious, and wanted to see if I could delete the elements that had consecutive duplicates and get this output:
[2, 3, 5, 1, 2]
For that I did this:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
i = 0
dupe = False
while i < len(list)-1:
if list[i] == list[i+1]:
del list[i]
dupe = True
elif dupe:
del list[i]
dupe = False
else:
i += 1
But it seems sort of clumsy and not pythonic, do you have any smarter / more elegant / more efficient way to implement this?
>>> L = [1,1,1,1,1,1,2,3,4,4,5,1,2]
>>> from itertools import groupby
>>> [key for key, _group in groupby(L)]
[1, 2, 3, 4, 5, 1, 2]
For the second part
>>> [k for k, g in groupby(L) if len(list(g)) < 2]
[2, 3, 5, 1, 2]
If you don't want to create the temporary list just to take the length, you can use sum over a generator expression
>>> [k for k, g in groupby(L) if sum(1 for i in g) < 2]
[2, 3, 5, 1, 2]
Oneliner in pure Python
[v for i, v in enumerate(your_list) if i == 0 or v != your_list[i-1]]
If you use Python 3.8+, you can use assignment expression :=:
list1 = [1, 2, 3, 3, 4, 3, 5, 5]
prev = object()
list1 = [prev:=v for v in list1 if prev!=v]
print(list1)
Prints:
[1, 2, 3, 4, 3, 5]
A "lazy" approach would be to use itertools.groupby.
import itertools
list1 = [1, 2, 3, 3, 4, 3, 5, 5]
list1 = [g for g, _ in itertools.groupby(list1)]
print(list1)
outputs
[1, 2, 3, 4, 3, 5]
You can do this by using zip_longest() + list comprehension.
from itertools import zip_longest
list1 = [1, 2, 3, 3, 4, 3, 5, 5].
# using zip_longest()+ list comprehension
res = [i for i, j in zip_longest(list1, list1[1:])
if i != j]
print ("List after removing consecutive duplicates : " + str(res))
Here is a solution without dependence on outside packages:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
L = list + [999] # append a unique dummy element to properly handle -1 index
[l for i, l in enumerate(L) if l != L[i - 1]][:-1] # drop the dummy element
Then I noted that Ulf Aslak's similar solution is cleaner :)
To Eliminate consecutive duplicates of list elements; as an alternative, you may use itertools.zip_longest() with list comprehension as:
>>> from itertools import zip_longest
>>> my_list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
>>> [i for i, j in zip_longest(my_list, my_list[1:]) if i!=j]
[1, 2, 3, 4, 5, 1, 2]
Plenty of better/more pythonic answers above, however one could also accomplish this task using list.pop():
my_list = [1, 2, 3, 3, 4, 3, 5, 5]
for x in my_list[:-1]:
next_index = my_list.index(x) + 1
if my_list[next_index] == x:
my_list.pop(next_index)
outputs
[1, 2, 3, 4, 3, 5]
Another possible one-liner, using functools.reduce (excluding the import) - with the downside that string and list require slightly different implementations:
>>> from functools import reduce
>>> reduce(lambda a, b: a if a[-1:] == [b] else a + [b], [1,1,2,3,4,4,5,1,2], [])
[1, 2, 3, 4, 5, 1, 2]
>>> reduce(lambda a, b: a if a[-1:] == b else a+b, 'aa bbb cc')
'a b c'
So for my assignment I must find a way in which I can work around this problem of printing 'YES' if a list contains the elements 1,2,3 in the consecutive order. It does not work if the list contains the elements [3,1,2,3] due to the index method. How can I work around this?
n=int(input("Enter the number of elements: "))
A=[]
for i in range(0,n):
print("Entering element", i)
LstEl=int(input("Please enter the element: "))
A.append(LstEl)
print(A)
for i in range(0,len(A)):
if(1 in A and 2 in A and 3 in A):
post1 = A.index(1)
post2 = A.index(2)
post3 = A.index(3)
if(post1 < post2 and post2 < post3):
print("YES")
break
else:
print('NO')
break
else:
print("NO")
break
Thanks!
one option is just:
# find the indices of all `1`s
one_idxs = (i for (i, v) in enumerate(values) if v == 1)
for idx in one_idxs:
if values[i : i + 3] == [1, 2, 3]:
print('YES')
break
else:
print('NO')
a more concise way is
if any(values[i : i + 3] == [1, 2, 3] for (i, v) in enumerate(values) if v == 1):
print('YES')
else:
print('NO')
Once you find a valid post1 value, you can check for the sequence using
if A[post1:post1+3] == [1, 2, 3]:
print('Yes')
break
Use the other parameters of the index() method to find successive occurrences of the element '1'.
The following code uses a generator function to extract sublists from a larger list. It's probably not appropriate for a homework assignment if you don't understand the mechanics behind generator functions, but it might be something to look into if you're interested.
# A generator function that returns the n-length sublists of list lst
def slider(lst, n):
start = 0
while start + n <= len(lst):
yield lst[start:start+n]
start += 1
# A function that will return True if sequence needle exists in
# haystack, False otherwise
def list_contains(haystack, needle):
for sub in slider(haystack, 3): # Loop through the sublists...
if sub == needle: # ... test for equality ...
return True
return False
# Code
big = [2,4,6,8,0,1,2,3,1,5,7] # Hardcoded here, could be created
# in a loop like you show
seq = [1,2,3] # The sequence you're looking for
print(list_contains(big, seq))
You can see the output of the generator function with something like:
big = [2,4,6,8,0,1,2,3,1,5,7]
for sub in slider(big, 3):
print(sub)
Output:
[2, 4, 6]
[4, 6, 8]
[6, 8, 0]
[8, 0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 1]
[3, 1, 5]
[1, 5, 7]
Or maybe more clearly:
# [2, 4, 6, 8, 0, 1, 2, 3, 1, 5, 7]
[2, 4, 6]
[4, 6, 8]
[6, 8, 0]
[8, 0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 1]
[3, 1, 5]
[1, 5, 7]
I would suggest using enumerate with the for loop.
lst = ['a', 'b', 'c', 'd']
for i, value in enumerate(lst):
print(i, value) # 0,a.....3,d
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.
I want to rotate elements in a list, e.g. - shift the list elements to the right so ['a','b','c','d'] would become ['d','a','b','c'], or [1,2,3] becomes [3,1,2].
I tried the following, but it's not working:
def shift(aList):
n = len(aList)
for i in range(len(aList)):
if aList[i] != aList[n-1]:
aList[i] = aList[i+1]
return aList
elif aList[i] == aList[i-1]:
aList[i] = aList[0]
return aList
shift(aList=[1,2,3])
You can use negative indices together with list concatenation:
def shift(seq, n=0):
a = n % len(seq)
return seq[-a:] + seq[:-a]
If you are trying to shift the elements, use collections.deque rotate method:
#! /usr/bin/python3
from collections import deque
a = deque([1, 2, 3, 4])
a.rotate()
print(a)
Result:
[2, 3, 4, 1]
If you are allergic to slice notation: a.insert(0,a.pop())
Usage:
In [15]: z=[1,2,3]
In [16]: z.insert(0,z.pop())
In [17]: z
Out[17]: [3, 1, 2]
In [18]: z.insert(0,z.pop())
In [19]: z
Out[19]: [2, 3, 1]
You can use this:
li=li[-1:]+li[:-1]
You can just slice the last element off the list, then add it to the beginning of a new list:
aList = [aList[-1]] + aList[:-1]
Here is the result:
>>> aList = [1,2,3]
>>> aList = [aList[-1]] + aList[:-1]
>>> aList
[3, 1, 2]
The question seems to imply to me that the list itself should be modified rather than a new list created. A simple in place algorithm is therefore:
lst = [1, 2, 3, 4, 5]
e1 = lst[-1]
for i, e2 in enumerate(lst):
lst[i], e1 = e1, e2
print(lst)
Giving:
[5, 1, 2, 3, 4]
If you actually want to shift the elements, you can use modulo to cycle the list and reassign the elements to their shifted positions:
def shift(lst, shft=0):
ln = len(lst)
for i, ele in enumerate(lst[:]):
lst[(i + shft) % ln] = ele
return lst
In [3]: shift( ['a','b','c','d'] , 1)
Out[3]: ['d', 'a', 'b', 'c']
In [4]: shift( ['a','b','c','d'] , 2)
Out[4]: ['c', 'd', 'a', 'b']
In [5]: shift( ['a','b','c','d'] , 3)
Out[5]: ['b', 'c', 'd', 'a']
If you only want a single shift just shift the last element to the front extending the list:
def shift(lst):
lst[0:1] = [lst.pop(),lst[0]]
return lst
Both of which change the original list.
Simple use of slice syntax:
def shift(seq):
return [seq[-1]] + seq[:-1]
assert shift([1, 2, 3, 4, 5]) == [5, 1, 2, 3, 4]
Generalized version with changeable shift:
def shift(seq, shift=1):
return seq[-shift:] + seq[:-shift]
assert shift([1, 2, 3, 4, 5]) == [5, 1, 2, 3, 4]
assert shift([1, 2, 3, 4, 5], 2) == [4, 5, 1, 2, 3]
Use a function assuming n is the shift that is less than the length of list l like so:
shift = lambda l, n: l[-n:] + l[:-n] # i.e. shift([1, 2, 3, 4], 3)
This could be done simply by using list method: insert,
values = [2, 3, 5, 7, 11, 13]
def shift(list):
new_list = []
for i in list:
new_list.insert(len(new_list)-1, i)
return new_list
print(shift(values))
Output is:
[3, 5, 7, 11, 13, 2]
Solution for your query :
def shift(aList):
l = list()
n = len(aList)
l.append(aList[-1])
temp = aList[:n-1]
for i in temp:
l.append(i)
return l
print(shift(aList=[1,2,3]))
you can use roll function from numpy.
>>> import numpy as np
>>> q = [1, 2, 3, 4, 5]
>>> np.roll(q, 2)
array([4, 5, 1, 2, 3])
Hope it helps!
shift right and shift left functions:
def left_shift(seq, n=0):
a = n % len(seq)
return seq[1:] + [seq[0]]
def right_shift(seq, n=0):
a = n % len(seq)
return seq[-a:] + seq[:-a]
seq=right_shift(seq,1)
You can use list slicing with '+'
For left shift
def left_shift(seq):
return seq[1:]+[seq[0]]
For Right shift
def right_shift(seq):
return seq[-1:]+seq[:-1]
If you have multiple elements in a list you can use this recursive method
def recurcieveShift(seq, n = 0):
if n == 0:
return seq
seq.insert(0,seq.pop())
recurcieveShift(seq, n - 1)
return seq
I don't know what the space and time complexity hope it helps
This question already has answers here:
Check for presence of a sliced list in Python
(11 answers)
Closed 9 years ago.
I am not sure if this question has been asked before but I couldn't find anything similar from the question list. I would like to check if a list has a set of three values in a certain order. For example, I would like to check if an int list has a set of values 1, 2, 3 anywhere within that list. The length of the list is unknown and the list cannot be sorted.
Example:
Values to check: 1, 2, 3 (in this order)
Example of a list = [1, 1, 2, 3, 1]
This is what I have tried so far.
list1 = [1, 1, 2, 3, 1]
list2 = [1, 1, 4, 3, 1, 2, 1]
def checkValue:
for i in range (0, len(nums)):
if (nums[i+2] - nums[i+1] == nums[i+1] - nums[i]) == 1:
return True
return False
list1 --> return True
list2 ---> IndexError: list index out of range
EDIT: Thanks to those who answered and thank you for the list to the sublist question. I never thought that the set of integers can be considered as a sublist and use it to compare to a larger list.
It looks like you're searching a sequence in a list.
You can just compare parts of the list with the sequence.
def find_sequence_in_list(list_to_check, values):
for i in range (len(list_to_check) - len(values) + 1):
#print(list_to_check[i:i + len(values)])
if list_to_check[i:i + len(values)] == values:
return True
return False
values = [1, 2, 3]
data1 = [1, 1, 2, 3, 1]
data2 = [1, 1, 4, 3, 1, 2, 1]
print(find_sequence_in_list(data1, values))
print(find_sequence_in_list(data2, values))
Uncomment the print to see what's happening.
i + 2 is too large in the loop body, nums doesn't have that many elements. Fix it like this:
if i + 2 < len(nums) and (nums[i+2] - nums[i+1] == nums[i+1] - nums[i]) == 1:
...
You can use tuple comparison directly, along with zip iteration (or itertools.izip if you prefer, for general iterables):
>>> def findin(values, nums):
... t = tuple(values)
... return any(T == t for T in zip(*(nums[i:] for i in xrange(values))))
Which gives for your examples:
>>> findin([1,2,3], [1,1,2,3,1])
True
>>> findin([1,2,3], [1, 1, 4, 3, 1, 2, 1])
False
I'm thinking of using deque to the sublist comparison.
from collections import deque
def has_sublist(lst, sub):
tmp_q = deque([], maxlen=len(sub))
sub_q = deque(sub)
for i in nums:
if tmp_q == sub_q:
return True
else:
tmp_q.append(i)
return tmp_q == sub_q
The tmp_q has a max length of len(sub) (which is 3 in your example), it contains a sublist of list to search in.
Let's check if it works well:
>>> lst = [1, 1, 4, 3, 1, 2, 1]
>>> sub = [1, 2, 3]
>>> print has_sublist(lst, sub)
False
>>> lst = [1, 1, 4, 3, 1, 2, 3]
>>> print has_sublist(lst, sub)
True
>>> lst = [1, 2]
>>> print find(lst, sub)
False
>>> lst = [1, 2, 3]
>>> print has_sublist(lst, sub)
True
In this case, you have no need to worry about the IndexError.