1

Possible Duplicate:
Finding first and last index of some value in a list in Python

Hi I was wondering if someone could help me with Python. I am trying to create a code that returns the last index of the last occurrence of an item in a list in a recursive way. So in a list [1,2,3,4,5,2] the last it should return 4. It only takes in 2 variables which are the list and the item that it is searching for. If it does not find any matching variable then it returns -1.

So far I have this:

def lstIndex(lst, item):
    if len(lst) == 0:
        return -1
    place = lst[0]
    if place == item:
        print(place)
        #just return the index
        return lst.index(place)
    else:
        return lstIndex(lst[1:],item)
Community
  • 1
  • 1
brian Chiem
  • 235
  • 1
  • 6
  • 13
  • Does it need to be recursive? – mgilson Oct 20 '12 at 03:51
  • You might want to see this question: http://stackoverflow.com/questions/522372/finding-first-and-last-index-of-some-value-in-a-list-in-python – GreenMatt Oct 20 '12 at 03:51
  • You can traveerse the list in reverse, and return the first index.. – Rohit Jain Oct 20 '12 at 03:52
  • Why do you request a recursive solution and then select a non-recursive answer as the best one? (Granted, it's a better approach, but if that's acceptable it would be better for you to avoid stating that you want a recursive solution.) – GreenMatt Oct 20 '12 at 14:15
  • @brian Chiem: While I appreciate having the selected answer switched to mine, that wasn't the point (or desire) of my previous comment. I was just saying that I think it best if you stay consistent. If you realize that another approach is better, I suggest you edit your answer or leave a comment to explain that to be the case. I don't mean to be critical here (sorry if this seems so), I just want selected answers to be consistent with their questions - or have an explanation for the inconsistency; otherwise, I fear future viewers will be confused by the inconsistency. – GreenMatt Oct 22 '12 at 14:19

5 Answers5

3

If recursion isn't necessary, you can use this:

def find_last(lst,item):
    try:
       return len(lst) - next(i for i,elem in enumerate(reversed(lst),1) if elem == item)
    except StopIteration:
       return -1


a = [1,2,3,4,5,4,3]
idx = find_last(a,4)
print a[idx]
print find_last(a,6)
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • +1 for using generators. Yours is better. :) – Rohit Jain Oct 20 '12 at 04:07
  • @RohitJain -- For some reason any time I want the first element of a sequence subject to some condition, I reach for `next(item in seq if condition)` ... It's just become habit. – mgilson Oct 20 '12 at 04:14
  • @mgilson.. And that is far better I think. Well, I also need to make that a habbit. makes code so clean :) – Rohit Jain Oct 20 '12 at 04:15
  • @RohitJain -- A quick `timeit` shows that mine is the slowest one here. I'm not actually convinced that it's always better. – mgilson Oct 20 '12 at 04:22
  • @mgilson.. Aww!! So, generators might work slower in some cases? – Rohit Jain Oct 20 '12 at 04:23
  • @RohitJain -- frequently. They're actually somewhat slow. The benefit you gain is that frequently you can short-circuit them and not calculate the whole sequence (which is the *only* reason they're faster than list-comps for some things) – mgilson Oct 20 '12 at 04:24
  • Similar to a loop. And yeah `list-comprehension` is absolutely not a choice when it comes to these kinds of problems. thanks:) I got to know today that generators might be slow. – Rohit Jain Oct 20 '12 at 04:26
  • 1
    @mgilson: Actually [nneonneo's answer](http://stackoverflow.com/a/12985164/548696) is better in my opinion, but you are correct the recursion is not required. I did not want to upvote this answer, but yours may be more memory efficient, plus you gave us some self-criticism in the comments ;) +1 – Tadeck Oct 20 '12 at 04:43
3

Short iterative solution:

try:
    return (len(lst)-1) - lst[::-1].index(item)
except ValueError:
    return -1

But, since you are explicitly looking for a recursive solution, I'll show you how it can be done recursively. However, it will not be efficient; if you want a nice, efficient, Pythonic solution you should use an iterative solution like the others have shown (or the one above).

There's actually a few ways you can do this. You can use a helper function, which takes an extra argument specifying the last index at which the value was found:

def list_rfind(lst, item):
    def list_rfind_helper(i, item, last=-1):
        if i >= len(lst): return last
        if lst[i] == item: last = i
        return list_rfind_helper(i+1, item, last)

    return list_rfind_helper(0, item)

You can do it without a helper function:

def list_rfind(lst, item):
    if not lst:
        return -1

    res = list_rfind(lst[1:], item)
    if res >= 0:
        return res+1
    elif lst[0] == item:
        return 0
    else:
        return -1
nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • Don't you want `len(lst) - (lst[::-1].index(item) + 1)` instead? – mgilson Oct 20 '12 at 04:13
  • yes, my bad. Thanks for catching that. – nneonneo Oct 20 '12 at 04:14
  • No problem. Happy to help :). I wonder how our various answers compare in terms of efficiency ... – mgilson Oct 20 '12 at 04:16
  • As I expected, yours is fastest in a very simplified benchmark I set up. – mgilson Oct 20 '12 at 04:30
  • This is the best solution (the first one, without recursion), but since recursion is required by OP and you have given also such option, then this answer is in my opinion the best. – Tadeck Oct 20 '12 at 04:39
  • I only tested sequences up to length 60 or so. So, there might be some wiggle room for *really* big sequences. My experience says that Genexp's are pretty costly to make and list slicing is quite fast (as long as the list isn't super big). – mgilson Oct 20 '12 at 04:39
  • @Tadeck -- I agree with you. If I could, I'd transfer the check-mark. – mgilson Oct 20 '12 at 04:40
  • My benchmark on 1000-length sequences actually shows that the iterative solution in this answer is the fastest, and that the other generator solution I posted is the slowest... – nneonneo Oct 20 '12 at 04:41
  • @mgilson: Don't worry. You have stated clearly in the comments what are advantages and disadvantages of your answer. The rest is the responsibility of OP ;) Both answers are very good (although different, suitable for different cases). – Tadeck Oct 20 '12 at 04:46
1
lst = [1, 2, 3, 4, 3, 4]

findLast(lst, 4)

def findLast(lst, item):
    for i, val in enumerate(reversed(lst)):
        if val == item:
            return len(lst) - (i + 1)  # Return index of matched item

    return -1
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
1

Just for completeness:

def list_rfind(lst, item):
    return (len(lst)-1) - sum(1 for _ in iter(reversed(lst).next, item))
nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • @nneonneo.. So you posted an incomplete code in your last answer?? lol ;) – Rohit Jain Oct 20 '12 at 04:27
  • last one wasn't incomplete, this is just my code-golf entry. :) – nneonneo Oct 20 '12 at 04:28
  • Hmm. Yeah I see this one is better and cleaner.. +1 :) – Rohit Jain Oct 20 '12 at 04:28
  • Ahh, here it is. All is right with the world again. – mgilson Oct 20 '12 at 04:31
  • I should point out that I don't really like this solution. It is too "clever", in that it won't be very readable in a month's time (plus, it happens to be the slowest among the four iterative solutions posted so far). I guess that's also a good rationale for relegating it to a separate answer. – nneonneo Oct 20 '12 at 04:44
0

I'm not quite 100% sure I know what you want. Your statement that "... in a list of [1,2,3,4,5,2] the last it should return 4 ..." has me a bit confused; I think you want to return the index of the last appearance of your specified item. So, for 4 to be the result in the list specified, item must be 5.

As noted elsewhere, a recursive function would not be the most efficient or Pythonic solution here. I'd prefer a solution like the first one in nneonneo's answer.

However, if it must be recursive, I believe the code below gets what you want. Instead of stepping through the list from the front (by using [1:]), you need to step backwards by using [:-1] as the index range when passing the list in the recursive call:

def lstIndex(lst, item):
    if len(lst) == 0:
        return -1
    elif lst[-1] == item:
        return len(lst) - 1
    else:
        return lstIndex(lst[0:-1], item)

I tested with the following:

the_list = [1,2,3,4,5,2]
print lstIndex(the_list, 2)
print lstIndex(the_list, 1)
print lstIndex(the_list, 3)
print lstIndex(the_list, 4)
print lstIndex(the_list, 5)
print lstIndex(the_list, 6)
print lstIndex(the_list, 0)

With the following output:

5
0
2
3
4
-1
-1

Community
  • 1
  • 1
GreenMatt
  • 18,244
  • 7
  • 53
  • 79