I have two lists
a = [1,2,3]
b = []
I want to move an element from list a, if it meets a certain condition.
a = [1,3]
b = [2]
The below code shows an example, however, I would like to do this inside of a single loop. How do I do this more efficiently?
a = [1,2,3]
b = []
pop_list = []
for i in range(len(a)):
if a[i] == 2:
print("pop:", a[i])
pop_list.append(i)
for i in range(len(pop_list)):
b.append(a.pop(pop_list[i]))
# Reset pop_list
pop_list=[]
Ideally, I would not generate a new list b.
A pair of list comprehensions would do the job: one to select the desired elements for b, the other to remove them from a
b = [i for i in a if i == 2]
a = [i for i in a if i != 2]
You can use filter and itertools.filterfalse and use the same filtering function for both:
from itertools import filterfalse
a = [1,2,3]
b = []
list(filterfalse(lambda x: x == 2, a))
list(filter (lambda x: x == 2, a))
[1, 3]
[2]
Here is the itertools.filterfalse docs.
If the element x exists you could just remove it from b and append it to a.
a = [1, 2, 3]
b = []
x = 2
def remove_append(a, b, x):
if x in a:
a.remove(x)
b.append(x)
remove_append(a, b, x)
print(a)
print(b)
Output:
[1, 3]
[2]
We must pass through all elements, however, you can apply this trick to add to the appropriate list in one loop:
(Appending to a loop is more efficient than deleting an element at arbitrary position)
a = [1,2,3]
condition_false, condition_true = [], []
for v in a:
# Add to the right list
(condition_false, condition_true)[v == 2].append(v)
# [1, 3]
print(condition_false)
# [2]
print(condition_true)
Here is a single loop way that's similar to your initial method:
length = len(a)
popped = 0
for i in range(length):
if i == length - popped:
break
if a[i] == 2:
b.append(a.pop(i))
popped += 1
If we keep track of how many elements we pop from a, we can just stop our loop that many elements early since there are fewer elements left in a.
Related
I have a few lists that I want filtered. Consider the following example:
a = [1,2]
b = [2,3]
l = [a,b]
for i in l:
i = [elem for elem in l if elem != 2]
I want the output to be a==[1], b==[3], but it looks like the update of i does not change the elements a or b. In C++, I'd use a pointer but I can't seem to find the corresponding concept in Python.
By assigning to i a new list in your for loop, it loses its reference to a and b so they do not get updated. Instead, you should update i in-place. You should also iterate through i instead of l to filter each sub-list:
for i in l:
i[:] = (elem for elem in i if elem != 2)
Demo: https://replit.com/#blhsing/BlissfulWhichGravity
Are you certain you don't just want something like the following?
a = [1, 2]
b = [2, 3]
l = [[i for i in a if i != 2], [i for i in b if i != 2]]
Or perhaps more flexibly:
exclude = (2,)
a = [1, 2]
b = [2, 3]
l = [a, b]
l = [[i for i in x if not any(i == e for e in exclude)] for x in l]
This latter approach makes it each to add additional sublists to l, and to specify more numbers to exclude from them.
Try this:
a = [1,2]
b = [2,3]
l = [a,b]
for i in l:
i.remove(2)
print(a)
print(b)
print(l)
If you've more than one occurrence of 2, you can try:
for i in l:
try:
while True:
i.remove(2)
except ValueError:
pass
Output:
[1]
[3]
[[1], [3]]
I have a list that contains duplicate elements. For all duplicate elements, I would like to obtain a list of their indices. The final output should be a list of lists of duplicate indices.
I have already come up with a working solution, but I have the feeling, that there might be a more computationally efficient and/or sparse way (using less code) for this problem:
# set up a list that contains duplicate elements
a = ['bar','foo','bar','foo','foobar','barfoo']
# get list of elements that appear more than one time in the list
seen = {}
dupes = []
for x in a:
if x not in seen:
seen[x] = 1
else:
if seen[x] == 1:
dupes.append(x)
seen[x] += 1
# for each of those elements, return list of indices of matching elements
# in original list
dupes_indices = []
for dupe in dupes:
indices = [i for i, x in enumerate(a) if x == dupe]
dupes_indices.append(indices)
where dupes_indices is [[0, 2], [1, 3]] ('foo' appears at indices 0 and 2 and 'bar' appears at indices 1 and 3)
I used the code from this and from this answer on stackoverflow.
You could try this nested list comprehension one-liner:
a = ['bar','foo','bar','foo','foobar','barfoo']
print([y for y in [[i for i, v in enumerate(a) if v == x] for x in set(a)] if len(y) > 1])
Output:
[[0, 2], [1, 3]]
pandas selection is great for such solution:
df = pd.DataFrame(['bar','foo','bar','foo','foobar','barfoo'])
df.columns = ["elements"]
elements = set(df.elements.tolist())
for e in elements:
x = df.loc[df.elements == e]
print(e, x.index.tolist())
output
bar [0, 2]
foobar [4]
foo [1, 3]
barfoo [5]
I've been practising a problem on CodeWarriors in which i basically have to do a difference of sets but retain the repeating elements which have not been excluded.
Eg: ([1,2,2,2,3] and [1]) should give --> [2,2,2,3]
So I thought of maintaining the duplicate elements in a list and adding them at the end.
Here's my code with List comprehension: [I have removed the excess debug printing commands I had written]
def array_diff(a, b):
duplicate = []
duplicate = [i for i in a if a.count(i)>1 and (i not in b) and (duplicate.count(i) < a.count(i)-1) ]
a = set(a)
b = set(b)
return list(a.difference(b)) + duplicate
For the inputs: a = [1,2,2,2,3] ; b = [1]
Gives the Output: [2, 3, 2, 2, 2]
Correct Output should be: [2,2,2,3]
But the same program without list comprehension, the program gives right output:
def array_diff(a, b):
print(a)
duplicate = []
for i in a:
if a.count(i) >1 and i not in b and duplicate.count(i) < a.count(i) -1 :
duplicate.append(i)
a = set(a)
b = set(b)
return list(a.difference(b)) + duplicate
For the same inpu, give the output: [2, 3, 2, 2]
Why does this happen?
In the first one, you are checking duplicate.count inside your list comprehension. When your list comprehension is finished, it will be assigned to the duplicate variable. Until then, duplicate is whatever you assigned to it before, which is an empty list. So duplicate.count(i) is always zero.
In the second version, you are appending to duplicate as you go, so duplicate.count(i) will sometimes be nonzero.
I have 2 list
a=[2,4,6]
b=[1,3,5,-1,-1,-1]
I have to replace the -1 in b with elements from list a and then print b as a sorted list
Expected
output= [1,2,3,4,5,6]
You can do with a list-comprehension and calling sorted() on it:
a = [2,4,6]
b = [1,3,5,-1,-1,-1]
a = iter(a)
output = sorted([next(a) if x == -1 else x for x in b])
print(output)
# [1, 2, 3, 4, 5, 6]
Try this:
a=[2,4,6]
b=[1,3,5,-1,-1,-1]
a.reverse()
for i,v in enumerate(b):
if v == -1:
b[i] = a.pop()
Then you can sort b as you want.
for i in a:
b[b.index(-1)]=i
b.sort()
The above is all it needs for your requirement
You can utilise the list.pop() method to get the next item from b whilst also removing it and then wrap the whole list comprehension in sorted():
a = [2,4,6]
b = [1,3,5,-1,-1,-1]
a = sorted([b.pop(0) if i == -1 else i
for i in a]
However, if you want to keep list b, you’re better off using next() on iter(a) as written in another answer.
This verbose option works also if -1's are more than len(a):
a=[2,4,6]
b=[1,3,5,-1,-1,-1,-1]
def replace(iterable, replacements):
i = 0
for e in iterable:
if e == -1 and i < len(replacements):
yield replacements[i]
i += 1
else:
yield e
res = sorted(replace(b, a))
print(res) #=> [-1, 1, 2, 3, 4, 5, 6]
I need to make a formula that, given two lists a and b, it returns the common elements in a and b. If the same element appears more than once in both, let's say xa times in a and xb times in b, then x should appear min(xa,xb) times in the results. If it's possible, don't use "import" in the code, please.
For example:
(Supposing my function is called common(a,b))
common([1,3,3,3],[1,3,3,3,3,4])
=> [1,3,3,3]
Thank you for your help!
A simple way is sort the two list first and compare the first element one by one. The code is like this:
def common(a, b):
sorted_a, sorted_b = sorted(a), sorted(b)
numa, numb = len(a), len(b)
rv = []
i, j = 0, 0
while i < numa and j < numb:
if sorted_a[i] == sorted_b[j]:
rv.append(sorted_a[i])
i += 1
j += 1
elif sorted_a[i] < sorted_b[j]:
i += 1
else:
j += 1
return rv
def common(lista=None,listb=None):
result_list=list()
inter_dict=dict()
lista_count=dict()
listb_count=dict()
for i in lista:
lista_count[i]=lista_count.get(i,0)+1 #convert lista to dict with frequency
for i in listb:
listb_count[i]=lista_count.get(i,0)+1 #convert listb to dict with frequency
for key in set(lista_count).intersection(set(listb_count)):
inter_dict[key]=min(lista_count[key],listb_count[key]) # get intersection of two dicts
for k,v in inter_dict.items():
result_list.extend([k]*v) #extend to the output list
return result_list
The output will give you when invoke the function common([1,3,3,3],[1,3,3,3,3,4]):
[1, 3, 3, 3]
def common(l1,l2):
totalElements = l1 + l2
resultList = []
for num in totalElements:
if num in l1 and num in l2:
resultList.append(num)
l1.remove(num)
l2.remove(num)
return resultList
l1 = [1,3,3,3,5]
l2 = [1,3,3,3,5,3,4]
result = common(l1 , l2)
print(result)
[1, 3, 3, 3, 5]
Three steps to resolve this problem:
Find out those elements should be in the final list, which is the intersection of two lists
For each element found in step #1, find out how many times it should be in final list
Generate final list based on the info found by previous two steps
And then translate these 3 steps to three lines of code:
def common(l1, l2):
intersection = [e for e in l1 if e in l2]
elemnts_counters = {e: min(l1.count(e), l2.count(e)) for e in intersection}
return sum([[e] * c for e, c in elemnts_counters.items()], [])
Then
print common([1,3,3,3], [1,3,3,3,3,4])
will give you:
[1, 3, 3, 3]