-1

I have two list:

list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list2 = [1, 4, 5]

I want to select an element from list1 but it shouldn't belongs to list2.

I have a solution using a while loop but I would like to have a one liner more pythonic and elegant.

mel
  • 2,730
  • 8
  • 35
  • 70
  • Try something like this: https://stackoverflow.com/questions/11328940/check-if-list-item-contains-items-from-another-list – xsami May 29 '17 at 14:27

5 Answers5

8

If your elements are unique you can use the set difference. (Convert list1 to a set and remove the elements from list2). Then draw a random sample.

random.choice(list(set(list1).difference(list2)))
Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
zeehio
  • 4,023
  • 2
  • 34
  • 48
1
[item for item in list1 if not in list2]

To make it a bit faster(because lookup in set faster than in list):

list2_items = set(list2)
[item for item in list1 if not in list2_items]

or with filter function(you will get a generator object in Python3

filter(lambda item: item not in list2, list1)

Converting list2 to set will also speed up filtering here.

To get more information read about list comprehensions.

Update: it seems that I missed a point about one random value. Well, you still can use list comprehensions, but use random.choice as was mentioned before:

import random
random.choice([item for item in list1 if not in list2_items])

It will filter choices and then get one randomly. @zeehio response looks like better solution.

Andrii Rusanov
  • 4,405
  • 2
  • 34
  • 54
0
import random
import itertools

next(item for item in (random.choice(list1) for _ in itertools.count()) if item not in list2)

That's equivalent to:

while True:
    item = random.choice(list1)
    if item not in list2:
        break
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • Not wrong, but something that could require an unbounded number of iterations feels off. – chepner May 29 '17 at 14:32
  • @chepner That's right, but `set` approaches might be wrong because they require hashable values **and** discard duplicates (thereby possibly skewing probilities). Maybe a check that some elements remain would remain in `list1` would be good but otherwise it's hard to have a "one-to-rule-them-all" approach. And in this case it rarely needs more than 2 tries and it's `O(1)` whereas the `set`-approaches are `O(n)` with a quite high constant factor. – MSeifert May 29 '17 at 14:36
  • If the probability of choosing an element that appears in `list2` is high, it is probably worth the effort to filter `list1` *before* calling `random.choice`. – chepner May 29 '17 at 14:40
  • That's right. For long iterables containing hashable values `set` is definetly the better choice. – MSeifert May 29 '17 at 14:46
  • Beware the infinite loop when nothing meets the criteria – Chris_Rands May 29 '17 at 14:59
0

You would probably want to utilize sets like so:

list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list2 = [1, 4, 5]

import random

print(random.choice([x for x in set(list1).difference(list2)]))  # kudos @chepner

That way we randomly draw from set(list1) - set(list2): elements in list1 but not in list2. This approach also scales well as lists 1 & 2 become large.


As @MSeifert noticed, the conversion of list1 to set will remove any duplicate elements that might be present in list1 thus altering the probabilities. If list1 might contain duplicate elements in the general case you might want to do this instead:

print(random.choice([x for x in list1 if x not in list2]))
Ma0
  • 15,057
  • 4
  • 35
  • 65
  • 1
    Use `set(list1).difference(list2)`, and you won't need to construct an explicit second set. `difference` akes any iterable as an argument, not just a set. – chepner May 29 '17 at 14:30
  • @chepner then you probably mean `set(list1).difference(list2)`, right? – Ma0 May 29 '17 at 14:32
  • `set2`, `list2`, whatever :) Thanks. – chepner May 29 '17 at 14:33
  • 1
    Also important to note that this will discard any duplicates in `list1` (not only those that are present in `list2`) and therefore skew the probabilities. – MSeifert May 29 '17 at 14:33
-1

without importing random library:

print((list(set(list1) - set(list2))).pop())

inside pop() you can give the index of the element you want to select,it will pop out that element example : for selecting list of index 1(from new list) ,((list(set(list1) - set(list2))).pop(1))

Here this (list(set(list1) - set(list2)) code will create a new list which contains only list with items from the first list which aren't present in the second one,

S T Mohammed
  • 80
  • 1
  • 2
  • 6