oOinsaneOo
oOinsaneOo

Reputation: 79

List comprehension in Python under specific circumstances

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]]

Upvotes: 1

Views: 76

Answers (4)

dawg
dawg

Reputation: 103844

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:

  1. 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.
  2. next(zip(*lst_b) we only need the first element of that inverse: (1,6,9)
  3. 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)
  4. [sl for sl in lst_a if sl[0] not in exclude] filter on that condition.
  5. lst_b+= extend lst_b with the filtered elements.

Profit!

Upvotes: 2

chepner
chepner

Reputation: 531165

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.)

Upvotes: 2

Barmar
Barmar

Reputation: 780994

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]

Upvotes: 2

ApplePie
ApplePie

Reputation: 8942

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]]

Upvotes: 1

Related Questions