First common element from two lists

后端 未结 10 1063
执笔经年
执笔经年 2020-12-20 14:07
x = [8,2,3,4,5]
y = [6,3,7,2,1]

How to find out the first common element in two lists (in this case, \"2\") in a concise and elegant way? Any list

相关标签:
10条回答
  • 2020-12-20 14:59

    One liner, using next to take the first item from a generator:

    x = [8,2,3,4,5]
    y = [6,3,7,2,1]
    
    first = next((a for a in x if a in y), None)
    

    Or more efficiently since set.__contains__ is faster than list.__contains__:

    set_y = set(y)
    first = next((a for a in x if a in set_y), None)
    

    Or more efficiently but still in one line (don't do this):

    first = next((lambda set_y: a for a in x if a in set_y)(set(y)), None)
    
    0 讨论(0)
  • 2020-12-20 15:01

    Using a for loops with in will result in a O(N^2) complexity, but you can sort y here and use binary search to improve the time complexity to O(NlogN).

    def binary_search(lis,num):
        low=0
        high=len(lis)-1
        ret=-1  #return -1 if item is not found
        while low<=high:
            mid=(low+high)//2
            if num<lis[mid]:
                high=mid-1
            elif num>lis[mid]:
                low=mid+1
            else:
                ret=mid
                break
    
        return ret
    
    x = [8,2,3,4,5]
    y = [6,3,7,2,1]
    y.sort()
    
    for z in x:
        ind=binary_search(y,z)
        if ind!=-1
            print z
            break
    

    output: 2

    Using the bisect module to perform the same thing as above:

    import bisect
    
    x = [8,2,3,4,5]
    y = [6,3,7,2,1]
    y.sort()
    
    for z in x:
        ind=bisect.bisect(y,z)-1  #or use `ind=min(bisect.bisect_left(y, z), len(y) - 1)`
        if ind!=-1 and y[ind] ==z:
            print z      #prints 2
            break     
    
    0 讨论(0)
  • 2020-12-20 15:05

    I assume you want to teach this person Python, not just programming. Therefore I do not hesitate to use zip instead of ugly loop variables; it's a very useful part of Python and not hard to explain.

    def first_common(x, y):
        common = set(x) & set(y)
        for current_x, current_y in zip(x, y):
            if current_x in common:
                return current_x
            elif current_y in common:
                return current_y
    
    print first_common([8,2,3,4,5], [6,3,7,2,1])
    

    If you really don't want to use zip, here's how to do it without:

    def first_common2(x, y):
        common = set(x) & set(y)
        for i in xrange(min(len(x), len(y))):
            if x[i] in common:
                return x[i]
            elif y[i] in common:
                return y[i]
    

    And for those interested, this is how it extends to any number of sequences:

    def first_common3(*seqs):
        common = set.intersection(*[set(seq) for seq in seqs])
        for current_elements in zip(*seqs):
            for element in current_elements:
                if element in common:
                    return element
    

    Finally, please note that, in contrast to some other solutions, this works as well if the first common element appears first in the second list.

    I just noticed your update, which makes for an even simpler solution:

    def first_common4(x, y):
        ys = set(y) # We don't want this to be recreated for each element in x
        for element in x:
            if element in ys:
                return element
    

    The above is arguably more readable than the generator expression.

    Too bad there is no built-in ordered set. It would have made for a more elegant solution.

    0 讨论(0)
  • 2020-12-20 15:05

    Just for fun (probably not efficient), another version using itertools:

    from itertools import dropwhile, product
    from operator import __ne__
    
    def accept_pair(f):
        "Make a version of f that takes a pair instead of 2 arguments."
        def accepting_pair(pair):
            return f(*pair)
        return accepting_pair
    
    def get_first_common(x, y):
        try:
            # I think this *_ unpacking syntax works only in Python 3
            ((first_common, _), *_) = dropwhile(
                accept_pair(__ne__),
                product(x, y))
        except ValueError:
            return None
        return first_common
    
    x = [8, 2, 3, 4, 5]
    y = [6, 3, 7, 2, 1]
    print(get_first_common(x, y))  # 2
    y = [6, 7, 1]
    print(get_first_common(x, y))  # None
    

    It is simpler, but not as fun, to use lambda pair: pair[0] != pair[1] instead of accept_pair(__ne__).

    0 讨论(0)
提交回复
热议问题