Search code examples
pythonpython-3.xmembershipmap-function

Unexpected result with a membership test on a map object


I am a beginner in Python and am trying to solve the List Overlap problem on Practice Python.

I have written some code for solving this but the code seems to have a mind of its own. It works sometimes and at other times it just returns a blank. Where am I going wrong?

lista= map(int,input('Enter a list of numbers separated by spaces :').split())

listb= map(int,input('Enter another list of numbers separated by spaces :').split())

listc=[]

for item in lista:
    if item in listb:
        listc.append(item)


print(listc)

After running:

Enter a list of numbers separated by spaces :1 3 5
Enter another list of numbers separated by spaces :3 7 9
[] <-- unexpected result- I was hoping to see a new list with [3] in it.

Solution

  • The strange behavior you're experiencing has to do with how the map function works in Python 3. You might be expecting that it returns a list as it does in Python 2 which can be repeatedly queried with the in keyword. In Python 3, however, map returns an iterator, which is an object that implements the __next__ method, enabling the mapped function to be applied lazily (but can only be traversed once). Using the in keyword on an iterator will exhaust it and harm accuracy of future in calls:

    >>> map(int, ["1", "2", "3"])
    <map object at 0x027FF5D0>
    >>> it = map(int, ["1", "2", "3"])
    >>> 1 in it
    True
    >>> 1 in it
    False
    >>> 1 in it
    False
    

    Had your example been something like 1 2 3 and 1 2 3, it'd appear to work because the iterator would find 1 in listb and pause, find 2 and pause, then find 3 and pause, exhausting both iterators together. But 1 3 5, 3 7 9 fails because the listb iterator is exhausted searching for 1 and returns false for any future in operations.

    The solution is to explicitly convert the iterator returned by map into a list:

    >>> list(map(int, ["1", "2", "3"]))
    [1, 2, 3]
    

    or use a list comprehension, which returns a list and effectively works as a map, applying a function to every element of the input list:

    >>> [int(x) for x in ["1", "2", "3"]]
    [1, 2, 3]
    

    With this change, the code should work as expected. Note that making lista a list isn't strictly necessary since it's only traversed once (but the name suggests that you'd like it to be a list):

    lista = list(map(int, input('Enter a list of numbers separated by spaces :').split()))
    listb = list(map(int, input('Enter another list of numbers separated by spaces :').split())) 
    #       ^^^^
    listc = []
    
    for item in lista:
        if item in listb:
            listc.append(item)
    
    print(listc)
    

    Sample run:

    Enter a list of numbers separated by spaces: 1 3 5
    Enter a list of numbers separated by spaces: 3 7 9
    [3]
    

    For further reading on iterables and iterators, see: What exactly are iterator, iterable, and iteration?.

    Having gone through this, note that your code doesn't account for duplicates correctly, so you'll still fail the coding challenge you're attempting. See if you can manipulate your logic to check if the value isn't already in listc before adding. Once you've successfully finished the challenge using lists, try exploring the set data structure, as others have mentioned, which offers a semantic and efficient way of accomplishing intersection.