Break the solution into two parts:
- Build a list of the differences between consecutive numbers (the zigs and zags).
- Return a list of the differences between consecutive zigs and zags (the zigzags).
You can use zip
with slices to iterate over each pair of consecutive elements:
def zig_zags(numbers):
zigzags = [(a - b) // abs(a - b) for a, b in zip(numbers, numbers[1:])]
return [int(a and b and a != b) for a, b in zip(zigzags, zigzags[1:])]
print(zig_zags([1, 2, 1, 3, 4])) # [1, 1, 0]
To break this down a little bit more, let's use the REPL to look at how zip
with the slice works:
>>> numbers = [1, 2, 1, 3, 4]
>>> numbers[1:]
[2, 1, 3, 4]
>>> [(a, b) for a, b in zip(numbers, numbers[1:])]
[(1, 2), (2, 1), (1, 3), (3, 4)]
The slice [1:]
takes the list starting with the second element, and zip
takes two lists (the original list and the sliced version that's offset by one) and yields one element from each at a time -- so all together, we're getting pairs of consecutive elements.
Now let's take that same expression but subtract a
and b
rather than turning them into tuples:
>>> [a - b for a, b in zip(numbers, numbers[1:])]
[-1, 1, -2, -1]
Negative numbers show where the original list was decreasing (zigging) and positive numbers show where it was increasing (zagging). (If it was neither zigging nor zagging there'd be a zero.) It'll be easier to compare the zigs and zags if we normalize them:
>>> [(a - b) // abs(a - b) for a, b in zip(numbers, numbers[1:])]
[-1, 1, -1, -1]
Now each "zig" is -1 and each "zag" is +1. So now let's see where the zigs follow the zags:
>>> zigzags = [(a - b) // abs(a - b) for a, b in zip(numbers, numbers[1:])]
>>> [(a, b) for a, b in zip(zigzags, zigzags[1:])]
[(-1, 1), (1, -1), (-1, -1)]
>>> [a != b for a, b in zip(zigzags, zigzags[1:])]
[True, True, False]
Same exact technique with zip
and [1:]
as before, but now we're looking at the zigs and zags that we computed in the first step. Because we normalized them, all we need to do is look at whether they're equal to each other.
We should also specifically exclude cases where there was no zig or zag, i.e. where a
or b
is zero. That just looks like:
>>> [a and b and a != b for a, b in zip(zigzags, zigzags[1:])]
[True, True, False]
Finally, since we wanted our output to be in terms of 1
and 0
instead of True
and False
, we need to convert that. Conveniently, when you convert True
to an int
it becomes 1
and False
becomes 0
, so we can just wrap the whole thing in int
:
>>> [int(a and b and a != b) for a, b in zip(zigzags, zigzags[1:])]
[1, 1, 0]