Robert
Robert

Reputation: 151

List comprehension headaches

I have a nested list like this which:

list = [[1,2,3], [2,5,7,6], [1,-1], [5,7], [6,3,7,4,3], [2, 5, 1, -5]]

What I am trying to do is to remove nested lists, where the value within these lists are both positive and negative. I have tried doing it by list comprehension, but I couldn't figure it out.

def method(list):
    return [obj for obj in list if (x for x in obj if -x not in obj)]

The obtained results should be like:

 list = [[1,2,3], [2,5,7,6], [5,7], [6,3,7,4,3]]

Upvotes: 5

Views: 140

Answers (6)

6502
6502

Reputation: 114461

Assuming you want lists where elements are either all negative or all positive you can use all predefined function to check for both possibilities

result = [L for L in x if all(y>0 for y in L) or all(y<0 for y in L)]

EDIT:

In the comments you clarified what is a valid list (e.g. [-1, 2] is valid)... with this new formulation the test should be

result = [L for L in x if all(-y not in L for y in L)]

where each single test is however now quadratic in the size of the list. Using set this problem can be removed

result = [L for L in x if all(-y not in S for S in (set(L),) for y in L)]

Upvotes: 5

Jojo
Jojo

Reputation: 447

Numpy can be used as well. My solution here is similar to the "all"-operation suggested by others but coded explicitly and only needs one condition. It checks whether the sign of the all the elements equals the sign of the first element (could be any other as well).

from numpy import *    

def f(b): 
   return [a for a in b if sum(sign(array(a)) == sign(a[0])) == len(a)]

For your case...

data = [[1,2,3], [2,5,7,6], [1,-1], [5,7], [6,3,7,4,3], [2, 5, 1, -5]]
print(f(data))

...it will return:

[[1, 2, 3], [2, 5, 7, 6], [5, 7], [6, 3, 7, 4, 3]]

Upvotes: 0

steviestickman
steviestickman

Reputation: 1251

Your generator should yield whether the condition to filter an object applies.
You then feed the generator to an aggregator to determine if obj should be filtered.
the aggregator could be any or all, or something different.

# assuming obj should be filtered if both x and the inverse of x are in obj
def method_with_all(src):
    return [obj for obj in src if all(-x not in obj for x in obj)]

def method_with_any(src):
    return [obj for obj in src if any(-x in obj for x in obj)]

Upvotes: 2

kederrac
kederrac

Reputation: 17322

you can filter out the lists that have both negative and positive elements:

def keep_list(nested_list):

    is_first_positive = nested_list[0] > 0

    for element in nested_list[1:]:
        if (element > 0) != is_first_positive:
            return False

    return True


my_list = [[1,2,3], [2,5,7,6], [1,-1], [5,7], [6,3,7,4,3], [2, 5, 1, -5]]

print(list(filter(keep_list, my_list)))

output:

[[1, 2, 3], [2, 5, 7, 6], [5, 7], [6, 3, 7, 4, 3]]

Upvotes: 0

FabioL
FabioL

Reputation: 999

Using list comprehension you can do something like:

def method2(list):
    return [obj for obj in list if (all(n>0 for n in obj) or all(n<0 for n in obj))]

that, with your example, give as output:

[[1, 2, 3], [2, 5, 7, 6], [5, 7], [6, 3, 7, 4, 3]]

Upvotes: 3

Dani Mesejo
Dani Mesejo

Reputation: 61910

In general is better to split the task by steps:

  1. Given list find the positives (positives function)
  2. Given list find the negatives and multiply them by -1 (negatives function)
  3. If the intersection of both positives and negatives is not empty remove.

So, you could do:

def positives(ls):
    return set(l for l in ls if l > 0)


def negatives(ls):
    return set(-1*l for l in ls if l < 0)


list = [[1, 2, 3], [2, 5, 7, 6], [1, -1], [5, 7], [6, 3, 7, 4, 3], [2, 5, 1, -5]]
result = [l for l in list if not negatives(l) & positives(l)]

print(result)

Output

[[1, 2, 3], [2, 5, 7, 6], [5, 7], [6, 3, 7, 4, 3]]

As a side note you should not use list as a variable name as it shadows the built-int list function.

Upvotes: 2

Related Questions