Brisco
Brisco

Reputation: 109

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

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)

Upvotes: 3

Views: 165

Answers (4)

Deera Wijesundara
Deera Wijesundara

Reputation: 761

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]

Upvotes: 1

no comment
no comment

Reputation: 10203

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.

Upvotes: 6

Riccardo Bucco
Riccardo Bucco

Reputation: 15364

Try this:

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

Upvotes: 1

Dani Mesejo
Dani Mesejo

Reputation: 61910

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

Upvotes: 5

Related Questions