I am new to python and trying to simply replace the values of a list when they meet a condition with values from a shorter list.
For example:
list1 = [1,0,1,0,1,0]
list2 = [1,2,3]
The output I want is:
list1 = [1,1,1,2,1,3]
I can use a loop with a counter:
counter = 0
for i, j in enumerate(list1):
if j == 1:
list1[i] = list2[counter]
counter += 1
But this seems inefficient for something so simple, so I'm guessing there might be a way to do this with a list comprehension, something like:
[list2[i] if j == 0 else j for i,j in enumerate(list1)]
(although this fails due the lists being different lengths).
Is there any other concise way of doing this in base python, perhaps using map or filter?
You can use an iterator made from the short list and just call next on it in the comprehension:
list1 = [1, 0, 1, 0, 1, 0]
list2 = [1, 2, 3]
it2 = iter(list2)
[x if x != 0 else next(it2) for x in list1]
# [1, 1, 1, 2, 1, 3]
Note that you can provide a default value to next if there are not enough filler elements:
Try something like:
[x if x else list2.pop(0) for x in list1]
Note this removes items from list2.
If you want to use map(), you could try this:
from collections import deque
list1 = [1,0,1,0,1,0]
list2 = [1,2,3]
queue = deque(list2)
result = list(map(lambda x : x if x else queue.popleft(), list1))
print(result)
Which outputs:
[1, 1, 1, 2, 1, 3]
Note: I used a stack/queue data structure, collections.deque to allow a O(1) popleft() from the front, instead of using pop(0), which is O(n). If you don't wish to use this library, you can just reverse list2 beforehand, and call pop(), which is also O(1).
You can try this:
list1 = [1,0,1,0,1,0]
list2 = [1,2,3]
new_list = [list2[list1[:i].count(a)] if a != 1 else a for i, a in enumerate(list1)]
Output:
[1, 1, 1, 2, 1, 3]
Related
Given list1 = [1,2,2,3], list2 = [1,2], what's the simplest way to subtract all element of list2 from list1 to get list list3 = [2,3]
It seems sum work well for two lists but subtraction doesn't.
To clarify: Order doesn't matter. L2 is a subset of L1. Duplicates need to be kept. Therefore can't use set.
>>> [1,2,2,3]+[1,2,3]
[1, 2, 2, 3, 1, 2, 3]
>>> [1,2,2,3]-[1,2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'list' and 'list'
You can try using remove:
list1 = [1,2,2,2,3]
list2 = [1,2,2]
[list1.remove(i) for i in list2]
list1
Output:
[2, 3]
Update without list comprehension, using standard for loops.
for i in list2:
list1.remove(i)
list1
Output:
[2, 3]
You could use collections.Counter and a list comprehension:
from collections import Counter
list1 = [1, 2, 2, 2, 3]
list2 = [1, 2, 2]
counts = Counter(list2)
result = [l for l in list1 if counts.get(l, 0) == 0 or counts.subtract((l,))]
print(result)
Output
[2, 3]
The list comprehension is equivalent to:
result = []
for l in list1:
if counts.get(l, 0) == 0 or counts.subtract((l,)):
result.append(l)
The tricky part here is the statement counts.get(l, 0) == 0 or counts.subtract((l,)). The counts.subtract((l,)) means subtract 1 from the count of l and the return value of the expression is None, the fact that None is a boolean-like value (that evals to False) allows to use it a single or expression. So the above or will only be True when counts.get(l, 0) == 0.
This is how I would do it:
def remove_elems(l1, l2):
removals = set(l2)
result = []
for elem in l1:
if elem in removals:
removals.remove(elem)
else:
result.append(elem)
return result
l1 = [1,2,2,3]
l2 = [1,2]
print(remove_elems(l1, l2)) # -> [2, 3]
If I have two lists, such as:
list1 = [1,2,3]
list2 = [1,2]
And I want to get an output that looks like this:
new_list = [(1,0),(2,0),(2,1),(3,0),(3,1)]
This is the Cartesian product minus the tuple that has the same element in both spots (1,1). I could generate this by using itertools to get the Cartesian product and then looping back through to remove any tuples that do not have distinct elements, but that seems really inefficient.
Imho list comprehensions are similarly fast as the itertools.product approach.
list1 = [1, 2, 3, 4]
list2 = [1, 2, 5]
x = [(i, j) for i in list1 for j in list2 if i != j]
print(x)
Though you didn't specify, if list1 or list2 elements are unique. If this is not the case, you should rather use
list1 = [1, 2, 3, 1]
list2 = [1, 2, 5, 2]
x = [(i, j) for i in set(list1) for j in set(list2) if i != j]
to prevent repetition of generated tuples.
I'm trying to solve the Google's Python Basic Exercises and I tried solving this particular one about lists with list comprehension:
# D. Given a list of numbers, return a list where
# all adjacent == elements have been reduced to a single element,
# so [1, 2, 2, 3] returns [1, 2, 3]. You may create a new list or
# modify the passed in list.
def remove_adjacent(nums):
newList = []
newList = [i for i in nums if len(newList) == 0 or nums[i] != newList[-1]]
return newList
Obviously, output is not what I expected, and the author-made test function underlines this:
got: [2, 2, 3, 3, 3] expected [2, 3]
got: [1, 2, 2, 3] expected [1, 2, 3]
What's wrong with my function?
The problem with your code is that the newList you are referring to inside the list comprehension expression always stays the same empty list [] as you assigned it initially. The expression [i for i in nums if len(newList) == 0 or nums[i] != newList[-1]] is calculated first using the existing variable, and only then the result is assigned to newList.
In other words, your code is equivalent to
def remove_adjacent(nums):
newList = []
otherList = [i for i in nums if len(newList) == 0 or nums[i] != newList[-1]]
return otherList
You don't have to use list comprehension to solve this problem (and personally I wouldn't because it gets tricky in this case).
def adj(l):
if len(l) in {0,1}: # check for empty or list with 1 element
return l
return [ele for ind, ele in enumerate(l[:-1]) if ele != l[ind+1]] + [l[-1]]
if ele != l[ind+1]]checks the current element against the element at the next index in the list, we go to l[:-1] so l[ind+1] does not give an index error, because of this we need to add l[-1] the last element to the result.
In [44]: l = [1, 2, 2, 3]
In [45]: adj(l)
Out[45]: [1, 2, 3]
In [46]: l = [1, 2, 2, 3,2]
In [47]: adj(l)
Out[47]: [1, 2, 3, 2]
In [48]: l = [2,2,2,2,2]
In [49]: adj(l)
Out[49]: [2]
Using your own code you would need a for loop as newList is assigned to the list comprehension, you are not updating your original assignment of newList you have reassigned the name to the list comprehension which is a completely new object:
def remove_adjacent(nums):
if len(l) in {0,1}: # catch empty and single element list
return l
newList = [nums[0]] # add first element to avoid index error with `newList[-1]`
for i in nums[1:]: # start at second element and iterate over the element
if i != newList[-1]:
newList.append(i)
return newList
In [1]: l = [] # assign l to empty list
In [2]: id(l)
Out[2]: 140592635860536 # object id
In [3]: l = [x for x in range(2)] # reassign
In [4]: id(l)
Out[4]: 140592635862264 # new id new object
I want to create what I thought was a fairly straightforward function. The function just runs through a list of lists and returns any list that does not have a 1 in all of the list elements following the second element ([2: ]). So given the list of lists [[1, 2, 1, 1, 1, 1], [4, 5, 1, 2, 0.3, 1, 1, 1]] the function would return [4, 5, 1, 2, 0.3, 1, 1, 1]. What I have so far is:
def discover(A):
"""Looks for list that has an element not equal to one.
"""
for i in range(len(A)):
for j in range(len(A[i])):
if A[i][j+2] != 1:
print A[i]
But when I run the function it finds one list but then prints that list over and over again before giving me an IndexError saying the list index is out of range. This seems to be a fairly easy problem but for some reason I'm not getting it. Any help would be really appreciated.
The problem is these two lines:
for j in range(len(A[i])):
if A[i][j+2] != 1:
What'll happen is that you'll eventually get to a point where j is the length of your list, minus 1. But then you're calling j+2 in the below code, and that's guaranteed to create a number longer than your list, giving you the IndexError. You can fix that with:
for j in range(2,len(A[i])):
if A[i][j] != 1:
As for the endless printing, you're nearly there, but you'll want to stop the loop if you find the non-1 element.
if A[i][j] != 1:
print A[i]
break
Alternately, the other answers will give you the same result more easily. But that's where your current errors are coming from.
for list in A:
if 1 not in list[3:]:
print list
even another solution:
lst = [
[1,2,3],
[1,1,1],
[3,4,5],
[3,5,6],
] # +++
def has1(subLst):
return subLst.count(1) == 0
print filter(has1, lst)
This avoids out of range issues.
def discover(A):
results = []
for lst in A:
for i in lst[3:]:
if i != 1:
results.append(lst)
break
return results
In addition to the other answers here, one could also make use of a generator. The yield statement will allow you to skirt establishing a default list to place your results into; you can just specify the condition you're looking for and yield the result.
>>> def discover(lists):
... for l in lists:
... if not [x for x in l[2:] if x != 1]:
... yield l
>>> stuff = [[2, 3, 4, 5, 1, 2], [2, 5, 1, 1, 1, 1, 1]]
>>> results = discover(stuff) #returns <generator object discover at 0x105e3eb90>
>>> results.next()
[2, 5, 1, 1, 1, 1, 1]
>>>
The magic line here being, if not [x for x in l[2:] if x !=1]. It builds a list from l[2:] and checks that any variable in there does not equal 1; if the list has no length, it means there are no non-1 entries in l[2:] and so it yields l.
A query to check if any element (after the second) != 1 would be:
any(x != 1 for x in mylist[3:])
so
def discover(A):
for mylist in A:
if any(x != 1 for x in mylist[3:]):
print mylist
I have a list as below:
list = [ [1,2,3,4,5],
[1,2,3,3,5],
[1,2,3,2,5],
[1,2,3,4,6] ]
I would like to parse through this list and remove the entry if it satisfy below conditions:
if list[i][0] is the same as list[i+1][0] AND
if list[i][4] is the same as list[i+1][4]
which will result in below list:
list = [ [1,2,3,4,5],
[1,2,3,4,6]]
Any help is much appreciated. Thanks.
Edit: Using Python 2.5.4
Use a list comprehension to keep everything not matching the condition:
[sublist for i, sublist in enumerate(yourlist)
if i + 1 == len(yourlist) or (sublist[0], sublist[4]) != (yourlist[i+1][0], yourlist[i + 1][4])]
So, any row that is either the last one, or one where the first and last element do not match the same columns in the next row is allowed.
Result:
>>> [sublist for i, sublist in enumerate(yourlist)
... if i + 1 == len(yourlist) or (sublist[0], sublist[4]) != (yourlist[i+1][0], yourlist[i + 1][4])]
[[1, 2, 3, 2, 5], [1, 2, 3, 4, 6]]
Less concise non-list comprehension version.
list = [ [1,2,3,4,5],
[1,2,3,3,5],
[1,2,3,2,5],
[1,2,3,4,6] ]
output = []
for i, v in enumerate(list):
if i +1 < len(list):
if not (list[i][0] == list[i+1][0] and list[i][4] == list[i+1][4]):
output.append(v)
else:
output.append(v)
print output
Just to put some itertools on the table :-)
from itertools import izip_longest
l = [ [1,2,3,4,5],
[1,2,3,3,5],
[1,2,3,2,5],
[1,2,3,4,6] ]
def foo(items):
for c, n in izip_longest(items, items[1:]):
if not n or c[0] != n[0] or c[4] != n[4]:
yield c
print list(foo(l))
Output:
[[1, 2, 3, 2, 5], [1, 2, 3, 4, 6]]
If you don't mind that this don't work in place put rather creates a new list.
Edit:
Since you told us you are using 2.5.4, you can use a method like the following instead of izip_longest:
# warning! items must not be empty :-)
def zip_longest(items):
g = iter(items)
next(g)
for item in items:
yield item, next(g, None)
I think list comprehension is best for this code.
res = [value for index, value in enumerate(a) if not (index < len(a)-1 and value[0]==a[index+1][0] and value[4]==a[index+1][4])]