3

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)
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Brisco
  • 99
  • 9
  • @Tomerikoo I had added the `codewars` tag, what's wrong with it? – no comment Sep 19 '21 at 09:42
  • It doesn't add any important information about the question. Stack Overflow is not a reference source for coding sites. It is a Q & A site for programming problems. Where those problems came from doesn't really matter... – Tomerikoo Sep 19 '21 at 09:44
  • @Tomerikoo But it helps people interested in codewars find codewars things, just like any other tag. – no comment Sep 19 '21 at 09:46

4 Answers4

6

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.

no comment
  • 6,381
  • 4
  • 12
  • 30
  • Nice solution, upvoted :) But maybe some explanation would be useful to the user? – Riccardo Bucco Sep 14 '21 at 14:00
  • @RiccardoBucco I love doing membership tests on iterators :-). Even just did a [question](https://stackoverflow.com/q/69092874/16759116) about that. Added some explanation now. – no comment Sep 14 '21 at 14:08
  • 1
    I'd _really_ suggest doing `if item in it: return next(it, None)`, though, otherwise that `item in it` line is bound to be "optimized" away by the next dev. Also much more self-explanatory then. – tobias_k Sep 14 '21 at 14:11
  • 1
    @tobias_k Hmm, I disagree. That next dev should get fired instead :-). I mean, among other things it means they didn't re-test after their deletion. I also don't think it's much more self-explanatory, in both cases you need to understand that the `in` consumes the iterator until that point. If you do, you understand both, and if you don't, you understand neither. Finally, someone like Dani will come along and request an explicit `return None` in addition. Ugh, no thanks :-) – no comment Sep 14 '21 at 14:18
5

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
Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
1

Try this:

def next_item(seq, item):
    seq = iter(seq)
    return next(next((seq for it in seq if item == it), seq), None)
Riccardo Bucco
  • 13,980
  • 4
  • 22
  • 50
1

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]