# How can I solve this the right way?

Not all of the elements are important. What you need to do here is to remove from the list all of the elements before the given one.

example
For the illustration we have a list [3, 4, 5] and we need to remove
all elements that go before 3 - which is 1 and 2.
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.

Input: List and the border element.
Output: Iterable (tuple, list, iterator …).

``````def remove_all_before(items, border):
new_items = []
if border == 0:
return items
elif items == []:
return items
else:
for i in items:
if items[i] == border:
new_items = [items[i]: ]

return new_items

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

depends, are you allowed to use built-in function and methods? If so, one the simpler solutions is to use `list.index()` method, this tells you if the element is in the array. If it is, great, you have the index you need for slicing, if not, you can just return the list

1 Like

What about the last example where all the element of the list is the same number?

owww, finally, I think i got it.

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

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 `try``except` 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:]

# ======================================

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

# =======================================
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:

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