How to find a specific item in a list/string/range following an item specified?

Viewed 127

Given an input sequence of any type (list/string/range) how do I find the next item in the input following the specified item?

Also, if the item isn't present or nothing follows it, the function should return None.

I tried converting input type to a list and then finding the position from a list, then getting the next item but this does not work for all input types. I have written something but I know it is not pythonic and it also times out. (codewars challenge: https://www.codewars.com/kata/542ebbdb494db239f8000046/train/python)

My attempt:

def next_item(xs, item):
    xs_list = list(xs)
    if item in xs_list:
        position = xs_list.index(item)
        try:
            return xs_list[position+1]
        except IndexError:
            return None
    else:
        return None

Desired results:

next_item([1, 2, 3, 4, 5, 6, 7, 8], 5)
# 6)
next_item(['a', 'b', 'c'], 'd')
# None)
next_item(['a', 'b', 'c'], 'c')
# None)
next_item('testing', 't')
# # 'e')
next_item(iter(range(1, 3000)), 12)
# , 13)
4 Answers

Simple solution:

def next_item(xs, item):
    it = iter(xs)
    item in it
    return next(it, None)

The item in it tries to find the item and thereby consumes that iterator until it finds the item or until it reaches the end.

You could use next to return the element that comes after the specified one:

def next_item(seq, e):
    iterable = iter(seq)
    for i in iterable:
        if i == e:
            return next(iterable, None)
    return None


print(next_item([1, 2, 3, 4, 5, 6, 7, 8], 5))
print(next_item(['a', 'b', 'c'], 'd'))
print(next_item(['a', 'b', 'c'], 'c'))
print(next_item('testing', 't'))
print(next_item(iter(range(1, 3000)), 12))

Output

6
None
None
e
13

Try this:

def next_item(seq, item):
    seq = iter(seq)
    return next(next((seq for it in seq if item == it), seq), None)

Fastest Solution after testing,

def next_item(ls,item):
    try:
        return ls[ls.index(item)+1]
    except:
        None

It took 0.886 to run this.

Here's are some other solutions,

  1. Using a for loop.
def next_item(ls,item):
    for i in range(len(ls)):
        if ls[i] == item:
            return ls[i+1]
    return None
next_item(range(100000000),9999999)

OUTPUT 1.862

[10000000]
  1. Using list.index()
def next_item(ls,item):
    try:
        return ls[ls.index(item)+1]
    except:
        None
next_item(range(100000000),9999999)

OUTPUT 0.886

[10000000]
  1. Shortest but, None is outputed as [] and Any answer is inside a list, ex: [1]
def next_item(ls,item):
    return [ls[i+1] for i in range(len(ls)) if ls[i] == item]
next_item(range(100000000),9999999)

OUTPUT 16.481

[10000000]
  1. (3) with the problem solved
def next_item(ls,item):
    a = [ls[i+1] for i in range(len(ls)) if ls[i] == item]
    if a == []:
        return None
    else:
        return a[0]
next_item(range(100000000),9999999)

OUTPUT 16.389

[10000000]
  1. What everyone else suggested,
def next_item(ls, item):
    ls = iter(ls)
    return next(next((ls for i in ls if item == i), ls), None)
next_item(range(100000000),9999999)

OUTPUT 0.994

[10000000]
Related