-1

I am trying to see if groups of three with consecutive elements have a zigzag pattern. Add to an empty list "1" for a zig, "1" for a zag, or "0" for neither. It seems to satisfy my first "if" condition and my "else" statement but never the middle. I've tried it as two if statement, one if and one elif, and nested. The answer should be [1,1,0] but I can only get [1,0] or no output and sometimes " index out of range". input [1,2,1,3,4] output [1,1,0]

    def solution(numbers):
        arr = []
        for i in range(len(numbers)-2):
            if numbers[i+1] > numbers[i] and numbers[i+2]:
                arr.append(1)
            if numbers[i+1] < numbers[i] and numbers[i+2]:
                arr.append(1)
            else:
                arr.append(0)
                return arr
  • This version of the code might work better if you did `numbers[i] < numbers[i+1] > numbers[i+2]` instead of `numbers[i+1] > numbers[i] and numbers[i+2]`. – Samwise Feb 04 '22 at 22:26
  • Well that worked. My gosh! Such a simple solution. But I will look up zip for the future. Thanks! – Nansamba Ssensalo Feb 05 '22 at 00:34

4 Answers4

2

You can use zip to combine elements 3 by 3 and compare each triplet to check if the middle element is either greater than both its neighbours or smaller than both of them:

numbers = [1,2,1,3,4]

result = [int((a-b)*(b-c)<0) for a,b,c in zip(numbers,numbers[1:],numbers[2:])]

print(result) # [1, 1, 0]

zip will combine items offset by 0, 1 and 2 yielding the following triplets:

numbers        [1,2,1,3,4]
numbers[1:]    [2,1,3,4]
numbers[2:]    [1,3,4] 
zip():          * * *   <-- 3 triplets (extras are ignored)

 a,b,c   a-b    b-c      (a-b)*(b-c)   int(...<0)
------- 
(1,2,1)  -1      1          -1            1
(2,1,3)   1     -2          -2            1 
(1,3,4)  -2     -1           2            0 
Alain T.
  • 40,517
  • 4
  • 31
  • 51
1

Break the solution into two parts:

  1. Build a list of the differences between consecutive numbers (the zigs and zags).
  2. 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]
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • Being a beginner I am unfamiliar with zip and it's inner workings. For the the sake of understanding the fundamentals of loops/indexing/functions I'm trying to understand your first suggestion. I should use boolean to compare i to i +1 to i+2? – Nansamba Ssensalo Feb 04 '22 at 20:20
  • These aren't two different suggestions, they are the two steps of the function that I provided the code for. I'll add a bit more to the answer to break it down further. – Samwise Feb 04 '22 at 20:24
0

You've got a sliding window here.

You're getting IndexError because your code has for i in range(len(numbers)), and then you ask for numbers[i+2]. To prevent this, reduce the range:

for i in range(len(numbers) - 2):
    <do checks on numbers[i], numbers[i+1], numbers[i+2]>

But you may prefer to zip some slices together:

for a, b, c in zip(numbers, numbers[1:], numbers[2:]):
    <do checks on a, b, c>
Jack Deeth
  • 3,062
  • 3
  • 24
  • 39
  • I'll be honest, I'm a beginner . I have not encountered zip yet.For the sake of understanding loops/functions/indexing can you clarify why I keep getting an output with only 2 integers in the list ( [1,0] instead of [1,1,0]. I changed my range to len(numbers)-2 as suggested. And I now understand how "len(numbers)-2", which in this case would be [ 1,2,1, ...no 3....no 4 ] keeps the triplets in range ( 1,2,1-2,1,3-1,3,4...anything thing beyond is out of range). How can I be getting the same output? ( I restarted my kernels/re-run as well ). Is something wrong with my second "if" statement? – Nansamba Ssensalo Feb 04 '22 at 20:07
0

Thanks everyone!

It was suggested I define my end range to avoid index out of range errors ( changed to len(numbers)-2) and that I change my formatting from "numbers[i+1] < numbers[i] and numbers[i+2]" to "numbers[i] > numbers[i+1] < numbers[i+2]". Also suggested I try the zip function which I will learn for next time.