How can I solve this the right way?

i would have use Try Except, given then only .index has to loop over the list. Plus, pythons idiom is: its better to ask for forgiveness (except) then permission (if)

I didn’t get what you mean?

You search through the list twice looking for the border.
Finding out whether the value is in the list and where it is, that’s the same action, so you only need one search.

Also, I think it’s odd that you sometimes create a copy and sometimes return the original.

There’s also dropwhile in itertools, which can be used to the same effect as your function.

Where user inputs are concerned. not all of them are going to be valid in terms of what our function is programmed to expect as usable data. The interpreter lets us introduce our own interrupts when we anticipate input errors, knowing what their error type is likely to be. That is where we get the tryexcept mechanism from. We’re not there yet, however, and it is well to keep peering into the validation process further.

Catch the error before except does and you are writing good code.

try...except

is debugging code, for the most part. It should only be applied in production code when it has proved its value in a particular case.

1 Like

Python struggles to represent this task in a clean way, something ugly is going to show up, probably no matter how it’s done.

I argue that one should do the right thing, and then implement that however that has to turn out.

try/except has nothing to do with debugging. exceptions are for handling exceptional situations which do not fit into the regular flow

arguably not finding a result is not exceptional enough to resort to using exceptions, but that’s list.index’s fault, but can you blame it? how else should it behave? return -1? this isn’t C, though C-like things do show up here and there in Python (str.find does do this, which is definitely a wart both in that it doesn’t match list’s naming and behaviour and also in that -1 is a valid index which could be mistakenly used). or wrap the result in a Result type? that would be great! but nothing else behaves that way so it doesn’t fit.

What’s the right thing to do? I think the right thing to do is to find the location of the border, and whether it exists. What python provides for this is list.index. list.index uses an exception to signal missing, so, catch it, and handle that situation, and otherwise use the location.

Alternatively, implement something like list.index, and come up with some scheme for signalling missing, maybe it wouldn’t be a separate function so no signalling would be needed, you’d just handle it where you discover it.

Anyway, searching twice isn’t the most offensive thing ever, especially considering it’s done by C code. There’s room for improvement though, and with what’s available, that improvement involves exceptions.

The additional search using not in makes me wonder whether a new programmer is aware that this is a loop, that probably bothers me more than anything else.

1 Like

I’d argue the specification is wrong, if it’s not found then all values come before the cutting element.

If you call this function, you would expect the first value of the result to either be the cutting value, or nothing… right? >.>
Otherwise the function is operating in two different modes depending on whether the cutting element was found which is incredibly awkward.

…the parameters are in the wrong order as well.

It depends upon the mindset behind the problem definition. If the cutting element is a sort of barrier, and a machine rolls through from left to right, it removes all elements until it hits the barrier. It follows that if there is no barrier, all elements get removed. If, on the other hand, the cutting element is a cleaner that removes everything that came before it, one could argue that if it’s not there, no cleaning gets done. However, with that mindset, we might argue that everything that precedes the final occurrence of the cutting element, rather than the first occurrence, should be removed.

Yeah, there’s beauty in simplicity.

1 Like

Riight but then you would be interested in the last barrier value, not the first one – if nothing may be in front of it.

there’s even example of such an input

print(list(remove_all_before([1, 1, 2, 4, 2, 3, 4], 2)))# == [2, 4, 2, 3, 4]

Yes. It is conceivable to want a function that removes before the first value if it exists.

But it’s oddly specific and probably not what you’d want from a general use function.

We have two edge cases here: (1) if a cutting element cannot be found,
then the list shoudn’t be changed. (2) if the list is empty, then it should remain empty.

Those wouldn’t be edge cases at all if it was defined as:

dropBefore x = dropWhile (/= x)

…the second isn’t an edge case either way, actually.

I don’t think I am using try…except the right way, even though it is giving the right output?
here is 3 answers which one is better and faster you think?

def remove_all_before(items, border):
    # if border not in items:
    #     return items
    # else:
    #     x = items.index(border)
    #     return items[x:]
    
    # ======================================
                # second Answer:

    # if border in items:
    #     x = items.index(border)
    #     return items[x:]
    # else:
    #     return items

    # =======================================
                # Third Answer:
    try:
        x = items.index(border)
        return items[x:]
    except:
        return items


# These "asserts" are used for self-checking and not for an auto-testing
print(remove_all_before([1, 1, 2, 2, 3, 3], 2))  # == [2, 2, 3, 3]
print(remove_all_before([1, 2, 3, 4, 5], 3))  # == [3, 4, 5]
print(remove_all_before([1, 1, 2, 4, 2, 3, 4], 2))  # == [2, 4, 2, 3, 4]
print(remove_all_before([1, 1, 5, 6, 7], 2))  # == [1, 1, 5, 6, 7]
print(remove_all_before([], 0)) == []
print(
    remove_all_before([7, 7, 7, 7, 7, 7, 7, 7, 7], 7)
)  # == [7, 7, 7, 7, 7, 7, 7, 7, 7]

Could someone please provide a link to this exercise? We need to know whether the instructions ask us to modify the original list or whether they ask us instead to return the result as a new list, with the original list unmodified. Once we know that, or even if we have to decide for ourselves, any solution we develop should be internally consistent regarding that issue.

1 Like

On reading the docs, this is the correct way to use list.index() since the method raises an exception when the value is not found in the list.

As I stand corrected about my earlier comment, this just goes to support that. Exceptions have a place in the grand scheme of things. Still, it can’t hurt to stretch the envelope as you’ve done above. Nice work.

@ionatan raises a question regarding in as a membership test; I must admit to leaning on this keyword a lot in my coding. The membership testing in your second example essentially puts the .index() method call in a safe bubble. If I was a teacher, would I take your view, or would I take ionatan’s view?

Given that the documentation is clear on how the .index() method responds, it might make better sense to go along with that, and skip the membership testing. #3 is on the money, but for one tiny detail.

What error should the reader of our code be expecting will raise the exception? Can we isolate it from the larger group and only catch THAT exception, and no others? Short pause… Yes. It’s a ValueError we’re looking to trap. We may as well let our reader know that, too.

except ValueError:
    return items

This way if there is some other cause, it will raise an exception and alert us to a greater problem than value not in list.

1 Like

@stetim94 pointed out that the action of testing for membership was already being carried out.
The problem is that the same thing was carried out twice.
There’s nothing wrong with that operator, but the situation suggests that it wasn’t fully considered what the actions used do, as one is completely overlapped by the other.

If it’s not in a tight loop then I would favour this:

def remove_all_before(items, border):
    def is_not_border(x):
        return x != border
    return dropWhile(is_not_border, items)

for the reason that it’s really difficult to get it wrong:
drop while it’s not the value sought for? yeah, that’s obviously right.

Haskell: compose dropwhile with not equal (/= is !=)

remove_all_before = dropWhile . (/=)

…which is trivial enough that one wouldn’t write the function in the first place, makes one wonder why it’s so much harder in python

1 Like

I took a stab at writing that in python.

from functools import partial
from inspect import getfullargspec
from itertools import dropwhile
from operator import ne

curry = (
    lambda f, *args: f(*args)
    if len(args) >= len(getfullargspec(f).args)
    else partial(curry, f, *args)
)
compose = curry(lambda f, g, x: f(g(x)))
dropwhile = curry(dropwhile)
ne = curry(ne)

remove_all_before = compose(dropwhile)(ne)

print(list(remove_all_before(5)(range(10))))

it’s a bit silly of course, but in a way it isn’t - I think it’s good to recognize that the task can be solved with a relation between notEquals and dropwhile, and that this relation is compose, which is a trivial function:

def compose(f, g, x):
    return f(g(x))

The only problem is that python evaluates everything immediately, so partially applied expressions have to be explicitly delayed by creating new functions which still require the remaining arguments

1 Like

Here is a link to the exercise:

Thanks, @kasem007, for the link.

The instructions do not specify whether the user should alter the original list or work with an entirely new list, so it is up to the developer to decide.

Let’s look at your solution in post 4, which contains two return statements.

The first of those return statements is as follows, which gives us a reference to the original list, rather than a copy of that list:

         return items

The second return statement is this one, which gives us an entirely new list, rather than a reference to the original list in altered form:

         return items[x:]

For consistency, I would favor having the first return statement give as a copy of the unaltered list, as follows:

         return items[:]

Then in both cases, we get back a reference to a new list, and the original list that got passed to the function is left unaltered.

Edited on May 18, 2020 to add the following:

You could do the above with a for loop that iterates through the indexes until it either finds the first index where the border value occurs or stops when it reaches the end of the list. You may need to switch the order of the return statements.

1 Like

Perhaps I’m being too picky, but every solution to this problem feels at least a little uncomfortable in some regard. A try and except approach has the aura of setting off an alarm when nothing really has gone wrong. Using the in operator followed by a call to the list.index() method checks twice for the presence of the border value. Some solutions that avoid the aforementioned approaches seem overly detailed, as if to go overboard just to avoid some minor transgression. Then there’s the following, hinted at in my previous post, but which seems spoiled by the final line in its rather abrupt reversion to copying the entire list after the loop doesn’t find the border value:

def remove_all_before(items, border):
  for i in range(len(items)):
    if items[i] == border:
      return items[i:]
  return items[:]

I’d rather the loop built toward something useful that could be salvaged even when the border value was absent from the list. Guess the dour mood of our current time in history must be getting to me. Maybe morning will bring a brighter day …

1 Like

Piggy-backing on @appylpye’s suggestion, we also have the built-in, enumerate()

>>> def remove_all_before(items, border):
	for i, x in enumerate(items):
		if x == border: return items[i:]
	return items[:]

>>> remove_all_before([], 0)
[]
>>> remove_all_before([7, 7, 7, 7, 7, 7, 7, 7, 7], 7)
[7, 7, 7, 7, 7, 7, 7, 7, 7]
>>> remove_all_before([range(7)] + [7], 7)
[7]
>>> 

Edited May 19 to fix errors

2 Likes

Thanks, @mtf, and all.

On a bright note, this challenge has provided us with an impetus to delve into various issues and features concerning algorithms, Python, and even Haskell. I think it’s because the problem throws us a curve ball in that when the border is not found, we need to return the full list rather than an empty one. @ionatan alluded to this previously.

3 Likes

You’re giving it too much credit.
We create verbs and nouns to combine together into increasingly more complicated things, but this is several such units combined together in a way that is too specific to be widely useful where the two individual parts would be more useful on their own, like a button that both calls a taxi and prepares a bath. As two separate buttons those would be very useful.

Implementation-wise it’s a trivial special case for the sake of it, something that would be more interesting if you could find a way to get both “cases” to converge to the same code path.

And, I especially dislike that because combining universal things is what makes programming fun and powerful, while having specific stuff like this is a dead end.

Implementation-wise, the “right thing” is achieved by asking python’s built-ins to search for that thing, and that turns out to have an ugly api. Doing anything other than that will be doing something slightly worse because python code can’t compete with builtins… Which I suppose is a common problem when comparing approaches in python, it’s not about how to solve it it’s about how to get a builtin to solve it. So maybe that should be ignored, and instead express it in whatever way is nice using python. This is a big part of why C and go and … things like that, are probably nicer educational languages, they make all things equal.

1 Like

We’ll need to invoke a different sports analogy …

curve ball --> deflated football ?

This post was edited on May 19, 2020 to remove what might be a copyrighted image related to Wikipedia: Deflategate.

3 Likes