I have Python lists with various strings in them such as:
List1 = ["a","b","c","d"]
List2 = ["b","d","e","f"]
List3 = []
List4 = ["d","f","g"]
I need to iterate through these lists, provided they are not blank, and finds items that are in all non-blank lists. In the above example, the exact matches list would be ["d"], since that is the only item that appears in all non-blank lists. List3 is blank, so it would not matter that it is not in that list.
Here's some functional programming beauty:
from operator import and_
from functools import reduce
Lists = List1, List2, List3, List4
result = reduce(and_, map(set, filter(None, Lists)))
I can't test this right now, but something like the following should work:
intersection(set(l) for l in [List1, List2, List3, List4] if l)
It uses Python's built-in set datatype to do the intersection operation.
for thing in list1: # iterate each item, you can check before hand if its not empty
if len(list2) > 0: # if not empty
if thing in list2: # in the list
# do the same thing for the other lists
something like that
Related
Here is some example code.
list1 = [['one','a'],['two','a'],['three','a'],['four','a']]
list2 = [['three','b'],['four','a'],['five','b']]
for l in list1:
if l not in list2:
print(l[0])
and the output from this code.
one
two
three
because ['four','a'] does indeed appear in both lists.
What I am trying to do is check if just the first item of each entry within the first list appears in the second list, I have tried variations of the following
list1 = [['one','a'],['two','a'],['three','a'],['four','a']]
list2 = [['three','b'],['four','a'],['five','b']]
for l in list1:
if l[0] not in list2:
print(l[0])
however, that code returns
one
two
three
four
though both 'three' and 'four' do appear in the second list.
I have used different methods before now to find the values that appear in only one of a pair of lists, then used that to make a master list that contains all possible values with no duplicates and I believe the same should be possible using this method but the syntax is a mystery to me. Where I am going wrong here?
You could use not any() and then you check specific requirements in the comprehension:
list1 = [['one','a'],['two','a'],['three','a'],['four','a']]
list2 = [['three','b'],['four','a'],['five','b']]
for l in list1:
if not any(l[0] == l2[0] for l2 in list2):
print(l[0])
# one
# two
You could also use sets if order doesn't matter:
list1 = [['one','a'],['two','a'],['three','a'],['four','a']]
list2 = [['three','b'],['four','a'],['five','b']]
set(l[0] for l in list1) - set(l2[0] for l2 in list2)
# {'one', 'two'}
you can use set operations
list1 = [['one','a'],['two','a'],['three','a'],['four','a']]
list2 = [['three','b'],['four','a'],['five','b']]
result = set(i[0] for i in list1) - set(i[0] for i in list2)
print(result)
# output {'one', 'two'}
I have a list of lists
list1 = [['a','b','c'],['m','n','o'],['x','y','z']]
Now, I want to check if all elements of any one of these 'sub-lists' are present in an incoming list called list2.
For example, if
list2 = ['a','c','e','b'] #Code would return True as a,b,c all from list1[0] match
list2 = ['a','c','m','x','e'] # False as all elements from any one sub-list do not match
list2 = ['g','h','i','x','y','z'] # True as all elements from list1[2] matched
I know that this can be done using a nested all within any python function. If list1 was not a nested list, I would have used
result = any(elem in list2 for elem in list1) # To check for any one
or
result = all(elem in list2 for elem in list1) # To check for all
However, I am at loss to do a nested any/all type of list comprehension. Any guidance would be appreciated.
You can do a combination of any and all:
list1 = [['a','b','c'],['m','n','o'],['x','y','z']]
def test(l1, l2):
return any(all(x in l2 for x in sub) for sub in l1)
print(test(list1, ['a','c','e','b']))
print(test(list1, ['a','c','m','x','e']))
print(test(list1, ['g','h','i','x','y','z']))
Output:
True
False
True
The test means: if all x's of any of the sublists are in list2.
Why not make list2 a set and use set.issuperset to see if all elements are in list2
list2 = {'g','h','i','x','y','z'}
print(any(list2.issuperset(elem) for elem in list1))
#True
You can even make this more simple using map although the comprehension is more readable
any(map(list2.issuperset, list1))
If you go this route I suggest not using list2 as the name to avoid confusion
I am trying to use List Comprehension to perform the following. I want to make a new list (unique) that only has the common numbers from both lists.
unique = []
for listcomp in range(len(list1)):
if list1[listcomp] in list2 and list1[listcomp] not in unique:
unique.append(list1[listcomp])
else:
continue
Above works fine but when I create the List comprehension below I get duplicates if list1 has duplicate numbers. i.e. list1 = [1, 1, 2], list2 = [1, 5]. I created my list comprehension as
unique = [list1[listcomp] for listcomp in range(len(list1)) if list1[listcomp] in list2 and list1[listcomp] not in unique]
If I'm getting duplicates I assume the "and" statement isn't being applied? I have read other queries about moving the if statement further up the comprehension statement but this didn't work. Can you use AND to extend your conditions?
Many thanks
My full code is:-
import random as rnd
# Randomly generate the size of your list
list1size = rnd.randint(1,20)
list2size = rnd.randint(1,20)
# Declare your list variables
list1 = []
list2 = []
# Fill your lists with randomly generated numbers upto the listsize generated above
for x in range(list1size):
list1.append(rnd.randint(1,15))
for y in range(list2size):
list2.append(rnd.randint(1,15))
# Not required but easier to read lists once sorted
list1.sort()
list2.sort()
print(list1)
print(list2)
# Now to compare old school
unique = []
# for listcomp in range(len(list1)):
# if list1[listcomp] in list2 and list1[listcomp] not in unique:
# unique.append(list1[listcomp])
# else:
# continue
# Now to compare with list comprehension
unique = [list1[listcomp] for listcomp in range(len(list1)) if list1[listcomp] in list2 and list1[listcomp] not in unique]
# Above doesn't stop duplicates if they are in List1 so I assume you can't use AND
print(f"The common numbers in both lists are {unique}")
You can't access elements produced by a list comprehension as you go along. Your condition list1[listcomp] not in unique will always return True since at that moment in time unique is defined as the empty list intialised in unique = [].
So the and statement is being applied, but not the in way you want.
Instead, you can create a "seen" set holding items you have already found and omit them. The standard implementation is found in the itertools unique_everseen recipe.
If you have the 3rd party toolz library, you can use the identical toolz.unique and feed a generator expression. More Pythonic, you can iterate elements directly rather than using indices:
from toolz import unique
unique = list(unique(i for i in list1 if i in list2))
Say I have a list2 of randomly chosen elements from a large list1. Is there a clever way of choosing an element from list1 that's NOT already in list2?
For example:
list1 = range(20,100)
list2 = [37,49,22,35,72] # could be much longer
while True:
n = random.choice(list1)
if n not in list2:
break
# now n is an element of list1 that's not in list2
I feel like there must be a more efficient way of doing this than a guess-and-check while-loop.
You can subtract list2 of list1:
list3 = list(set(list1)-set(list2))
and choose from it randomly:
random.choice(list3)
Note: you need to reconvert the set to a list.
You could use:
import random
list1 = range(20,100)
list2 = [37,49,22,35,72]
not_in_list2 = [item for item in list1 if item not in list2]
n = random.choice(not_in_list2)
This uses a list comprehension to create a list of all elements in list1 that aren't inlist2. It then selects randomly from this list. Unlike when working with sets, this technique does not change the probability of items being selected, because it does not remove duplicate elements from list1.
In case that there are no repeating elements in list1, this is a pythonic way, working with set and -:
import random
list1 = range(20,100)
list2 = [37,49,22,35,72] # could be much longer
n = random.choice(tuple(set(list1)-set(list2)))
# now n is an element of list1 that's not in list2
The tuple call is needed to avoid a NotIndexable exception.
If you want to randomly select more than one item from a list, or select an item from a set, it's better to use random.sample instead of choice
import random
diff = set(list1)-set(list2)
num_to_select = 1 # set the number to select here.
list_of_random_items = random.sample(diff, num_to_select)
If you do not want the overhead of creating a new list (or a new list and two sets) which can become quite costly if list1 is very large, there is another option.
import random
list1 = range(20,100)
list2 = [37,49,22,35,72]
for i in list2:
while i in list1:
list1.remove(i)
random.choice(list1)
Just iterate through the items in list2 and remove them from list1. Since list.remove() only removes the first occurrence of an item, I added a while-loop to ensure that all occurences are removed.
I have 3 lists:
['1','2']
['a','b','c']
['X','Y']
and the result I am looking to get:
['1aX','1bX','1cX','2aX','2bX','2cX','1aY','1bY','1cY','2aY','2bY','2cY']
is there a way to set this up quickly?
You can use itertools.product():
map("".join, itertools.product(list1, list2, list3))
>>> x,y,z=['1','2'],['a','b','c'],['x','y']
>>> s=[a+b+c for c in z for a in x for b in y]
>>> s
['1ax', '1bx', '1cx', '2ax', '2bx', '2cx', '1ay', '1by', '1cy', '2ay', '2by', '2cy']
This way you can choose the order you want
To get the exact result that you specified, you can use the following:
import operator
import itertools
list1 = ['1','2']
list2 = ['a','b','c']
list3 = ['X','Y']
getter = operator.itemgetter(1, 2, 0)
result = [''.join(getter(seq)) for seq in itertools.product(list3, list1, list2)]
The final argument to itertools.product() will be the first to change, so based on your example output we want list2 to be last, then list1, and then list3, since first the middle element advances, then the first element, then the last. The operator.itemgetter() call is used to reorder the elements so that the element from list1 comes first, etc.
I find list comprehensions easier to read here, but here is a one-line alternative that uses map():
map(''.join, map(operator.itemgetter(1, 2, 0), itertools.product(list3, list1, list2)))
(You could do the list comprehension in one line as well, but you shouldn't because then the operator.itemgetter() call would be executed on each iteration)