lst_a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
lst_b = [[1, 4, 7], [6, 5, 4], [9, 8, 7]]
My goal is to check all nested lists in lst_a if the first entry == first entry of any element in lst_b. If it's not than copy ONLY THAT sublist. In this example he wouldn't copy lst_a[0] but 1 and 2.
I tried to achieve my goal with list comprehension but it won't work.
zero = [x[0] for x in lst_a]
if zero not in lst_b:
# I don't know what to do here.
Creating a tuple or a dictionary isn't possible because the whole process is in a loop in which every second new data come in and I try to avoid copying duplicates to the list.
EDIT: lst_b should look like that after the whole process:
lst_b = [[1, 4, 7], [6, 5, 4], [9, 8, 7], [4, 5, 6], [7, 8, 9]]
Extract all the first elements from lst_b into a set so you can check membership efficiently. Then use a list comprehension to copy all the sublists in lst_a that match your criteria.
first_elements = {x[0] for x in lst_b}
result = [x for x in lst_a if x[0] not in first_elements]
It's a bit of a mouthful, but not too bad:
lst_b.extend(x for x in lst_a if not any(x[0] == y[0] for y in lst_b)
If you want a new list rather than modifying lst_b in place, then
lst_c = lst_b + [x for x in lst_a if not any(x[0] == y[0] for y in lst_b)]
In either case, we examine each sublist x in lst_a. any(x[0] == y[0] for y in lst_b) is True if the first element of the sublist is equal to the first element of any sublist in lst_b. If that's not true, then we'll include x in our final result.
Using any allows us to avoid checking against every sublist in lst_b when finding a single match is sufficient. (There are cases where this could be more efficient than first creating an entire set of first elements, as in #barmar's answer, but on average that approach is probably more efficient.)
Another way:
exclude=set(next(zip(*lst_b)))
lst_b+=[sl for sl in lst_a if sl[0] not in exclude]
>>> lst_b
[[1, 4, 7], [6, 5, 4], [9, 8, 7], [4, 5, 6], [7, 8, 9]]
Explanation:
zip(*lst_b) is a generator of the inverse of the matrix lst_b, The * expands the sub lists and this creates a generator that yields [(1, 6, 9), (4, 5, 8), (7, 4, 7)] in turn.
next(zip(*lst_b) we only need the first element of that inverse: (1,6,9)
set(next(zip(*lst_b))) only need the uniq elements of that so turn into a set. You get {1, 6, 9} (order does not matter)
[sl for sl in lst_a if sl[0] not in exclude] filter on that condition.
lst_b+= extend lst_b with the filtered elements.
Profit!
There may be more efficient ways of doing this, but this accomplishes the goal.
>>> [a for a in lst_a if a[0] not in [b[0] for b in lst_b]]
[[4, 5, 6], [7, 8, 9]]
Related
I have a 2D list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]], and I want to delete columns in a loop.
For example, columns with index: 0(first) and 2(last) - - the result after deletions should be: [8, 5, 5].
There is a problem, because when I delete the 0th column, the size of the list is decreased to (0,1), and the 2nd index is out of scope.
What is the fastest method to delete columns in a loop without the out-of-scope problem?
For a better picture:
[[1, 8, 3],
[4, 5, 6],
[0, 5, 7]]
There is no such shortcut in python except for iterating over all the list items and removing those index values.
However, you can use pandas which is meant for some other purpose but will do the task.
import pandas as pd
s = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
df = pd.DataFrame(s,columns=['val1','val2','val3'])
li = df.drop('val1',axis=1).values.tolist()
now li will look like this
[[8, 3], [5, 6], [5, 7]]
You can use numpy like this:
import numpy as np
my_list = np.array([[1, 8, 3], [4, 5, 6], [0, 5, 7]])
new_list = my_list[:, 1].copy()
print(new_list)
Output:
>>> [8, 5, 5]
Also numpy.delete(your_list, index, axis) is do the same job:
new_list = np.delete(my_list,(0, 2), axis=1)
(0, 2) is the indices of the columns 0 and 2
axis=1 says numpy that (0, 2) are columns indices not rows.
if you want to delete rows 0 and 2 you can change axis=1 to axis=0
Output is a little different:
>>> array([[8],
[5],
[5]])
For a pure python approach:
my_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
new_list = [value[1] for value in my_list]
print(new_list)
Output:
>>> [8, 5, 5]
L is 2D list:
print(map(lambda x: x[1:], L))
data= [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
index_to_remove=[0,2]
[list(x) for x in zip(*[d for i,d in enumerate(zip(*data)) if i not in index_to_remove])]
If I understood your question correctly, you want to keep the middle element (index 1) of each list,in that case I would suggest creating a new list. There could be other better ways, for sure. But you could try this, if this works for you:
twoD_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
def keep_col( twoD_list ,index_to_keep = 1):
final_list = []
for x in twoD_list:
final_list.append(x[index_to_keep])
return final_list
final_list = keep_col( twoD_list , 1)
Final output:
[8,5,5]
Assuming you always want only the second element and the inner lists always have at least two elements.
Pure python with list comprehension:
lst = [
[1, 8, 3],
[4, 5, 6],
[0, 5, 7],
]
filtered_lst = [
inner_element
for inner_lst in lst
for i, inner_element in enumerate(inner_lst)
if i == 1
]
print(filtered_lst)
# [8, 5, 5]
If you want you can the reassign the new list to the old variable:
lst = filtered_lst
The advantages of this method are:
no need to worry about the list being altered while you iterate it,
no need to import other libraries
list comprehension is built-in
list comprehension is often the fastest way to filter a list (see for example this article)
easier to read and maintain that other solutions (in my opinion).
Via itemgetter to extract the value at index 1.
from operator import itemgetter
my_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
result = list(map(itemgetter(1), my_list))
try this
my_list = [[1, 8, 3], [4, 5, 6], [0, 5, 7]]
filter_col=[0,2]
col_length=3
my_list=[[x[i] for i in range(col_length) if i not in filter_col] for x in my_list]
u do not want to directly mutate the list that you are working on
this performs a list comprehension to create a new list from the existing list
edit:
just saw u wanted only a flat list
assuming u only want one element for the list u can use
my_list=[x[1] for x in my_list]
I am trying to split a list into multiple ones. The original list lst should be split on every element that is present in a second list, split_on.
Example:
lst = [1, 2, 3, 4, 5, 6, 7, 8]
split_on = [3, 4, 7]
should yield:
[[1,2,3],[3,4],[4,5,6,7],[7,8]]
Note that both lst and split_on do not contain duplicate elements and that any item of split_on is also an item of lst. Finally, the order of the elements of split_on can be random.
How about the following:
a = [1,2,3,4,5,6,7,8]
b = [4,3,7]
res = [[]]
for i in a:
res[-1].append(i)
if i in b:
res.append([i])
print(res) # [[1, 2, 3], [3, 4], [4, 5, 6, 7], [7, 8]]
Note that since b is only used for membership tests and the order does not matter, you could consider converting it to a set so that the approach scales better.
Using Python 3.7, I have two lists, one nested and one that is not, and I would like to extract the strings that are ordered in one list, and place each into a corresponding ordered nested list. Once the nested lists have been merged, I plan to unpack them into a table.
I have tried to perform a nested for-loop where I iterate through the nested loop to isolate the nested lists, and then a second for loop to extract each string object from its regular (unnested) list. My attempts to insert the string into the nested list end up either iterating through each character in the string, or it adds the entire list of strings into the nested lists. I've tried some different list comprehension attempts with zip, but being new at Python I've not yet mastered the syntax to traverse the lists.
A very simple attempt that hopefully explains what I am trying to accomplish.
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
b = ['1-10', '10-20', '20-30']
for i in a:
for j in b:
i.insert(0, j)
print(a)
>>> [['1-10', 1, 2, 3], ['10-20', 4, 5, 6], ['20-30', 7, 8, 9]]
Use zip in a list-comprehension:
[[y] + x for x, y in zip(a, b)]
Example:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
b = ['1-10', '10-20', '20-30']
print([[y] + x for x, y in zip(a, b)])
# [['1-10', 1, 2, 3], ['10-20', 4, 5, 6], ['20-30', 7, 8, 9]]
Or use unpacking:
print([[y, *x] for x, y in zip(a, b)])
Output:
[['1-10', 1, 2, 3], ['10-20', 4, 5, 6], ['20-30', 7, 8, 9]]
I have two lists of lists, of equal length, like this:
lstA = [[1,4,5,6],[4,5],[5,6],[],[],[],[],[]]
lstB = [[7,8],[4,5],[],[],[],[2,7,8],[7,8],[6,7]]
And I want to concatenate the sublists at each index position such that they make a single sublist, like this:
newlst = [[1,4,5,6,7,8],[4,5],[5,6],[],[],[2,7,8],[7,8],[6,7]]
Ideally, the new sublists will remove duplicates (like in newlst[1]). I converted the integers to strings, and attempted this:
for i in range(len(lstA)):
c = [item + item for item in strA[i], strB[i]]
but that adds each item from each list to itself before adding to the other list, resulting in something like this:
failedlst = [[["1","4","5","6","1","4","5","6"],["7","8","7","8"]],[["4","5","4","5"],["4","5","4","5"]]...etc]
And this still doesn't actually join the two sublists, just makes a new sublist of the two sublists. Any help would be greatly appeciated!
Making a list by concatenating items in parallel is very simple, using a list comprehension in combination with the zip function.
newlst = [x+y for x,y in zip(lstA, lstB)]
If you want to remove duplicates, you can use a set. If you then want to put the items back in order in a list, you can use sorted.
In combination, this:
newlst = [sorted(set(x+y)) for x,y in zip(lstA, lstB)]
You could use:
lstA = [[1,4,5,6],[4,5],[5,6],[],[],[],[],[]]
lstB = [[7,8],[4,5],[],[],[],[2,7,8],[7,8],[6,7]]
answer = []
for idx in range(len(lstA)):
answer.append(sorted(list(set(lstA[idx]+lstB[idx]))))
print(answer)
Output
[[1, 4, 5, 6, 7, 8], [4, 5], [5, 6], [], [], [2, 7, 8], [7, 8], [6, 7]]
Using zip and chain.from_iterable from the itertools module.
In [94]: from itertools import chain
In [95]: lstA = [[1,4,5,6],[4,5],[5,6],[],[],[],[],[]]
In [96]: lstB = [[7,8],[4,5],[],[],[],[2,7,8],[7,8],[6,7]]
In [97]: [list(set(chain.from_iterable(item))) for item in zip(lstA, lstB)]
Out[97]: [[1, 4, 5, 6, 7, 8], [4, 5], [5, 6], [], [], [8, 2, 7], [8, 7], [6, 7]]
If you want to sort the sublist then:
In [98]: [sorted(set(chain.from_iterable(item))) for item in zip(lstA, lstB)]
Out[98]: [[1, 4, 5, 6, 7, 8], [4, 4, 5, 5], [5, 6], [], [], [2, 7, 8], [7, 8], [6, 7]]
So this is what I have:
lst = [[1,4,5,9], [4,5,7,9], [6,2,9], [4,5,9], [4,5]]
And I want to make a new list where the sublists only have the elements that are shared with ALL the previous sublists.
It should look like this:
new_lst = [[1,4,5,9], [4,5,9], [9], [9], []]
I tried converting the integers to strings and iterating over it, but I can't seem to get the right result. I'm new to python, so any help is much appreciated!
If you don't care about the order of the items, you can use a set based approach:
last = set(lst[0])
res = [lst[0]]
for item in lst[1:]:
last &= set(item)
res.append(list(last))
Here, res contains the resulting list of lists. The important line is last &= set(item) which calculates the intersection between the previous and the current item.
You can use accumulate
from itertools import accumulate
new_lst = map(list,accumulate(map(set,lst), set.intersection))
print(list(new_lst))
which produces
[[1, 4, 5, 9], [4, 5, 9], [9], [9], []]
but it doesn't guarantee the items of the sublists will be in the same order.
In case you do care about the order of the items, you can do it manually
last = set(lst[0])
new_lst = [lst[0]]
for l in lst[1:]:
new_els = [n for n in l if n in last]
new_lst.append(new_els)
last &= set(new_els)
print(new_lst)
Or just ignore sets and use lists instead, but sets are faster, since they are hashed and intersection is a core functionality.
One line answer:
>>> from functools import reduce # for forward compatible.
>>> lst = [[1,4,5,9], [4,5,7,9], [4,5,7,9], [4,5,9], [4,5]]
>>> [list(reduce(lambda x,y: set(x)&set(y), lst[:i+1])) for i in range(len(lst))]
[[1, 4, 5, 9], [9, 4, 5], [9], [9], []]
The explanation:
[1, 4, 5, 9] = list(set([1, 4, 5, 9]))
[9, 4, 5] = list(set([1, 4, 5, 9]) & set([4,5,7,9]))
[9] = list(set([1, 4, 5, 9]) & set([4,5,7,9]) & set([4,5,7,9]))
...
in the i'st loop, it should be union the items in lst[:i+1].