21
\$\begingroup\$

For our purposes, a trend is a contiguous subsequence of an array that falls into one of three categories:

  • increasing, e.g. [1,2,3,10,239]
  • decreasing, e.g. [8,5,0,-3,-50]
  • stable, e.g. [7,7,7,7]

Note that [1,1,2,3] is not a valid trend. It can be broken up into a stable trend [1,1] and an increasing trend [1,2,3]. In the event an array has more than one trend, its trends will always overlap by one number.

Task

Given an array of at least two integers in any reasonable format, output the trends of the array.

Rules

  • You must output the smallest possible number of trends. [[1,1], [1,2], [2,3]] is not valid output for [1,1,2,3].
  • You must output trends in the order they occur in the input.
  • You may output in any format you like as long as we can tell the trends apart.
  • This is , so the code with the fewest bytes (in each language) wins.
  • Standard loopholes are forbidden.

Test cases

Input Output
[1,2]
[1,2,1]
[1,1,2,3]
[1,-1,2,-2,3,-3]
[0,0,0,4,10,96,54,32,11,0,0,0]
[-55,-67,-80,5,5,5,9,9,9,9,14,20,25]
[[1,2]]
[[1,2], [2,1]]
[[1,1], [1,2,3]]
[[1,-1], [-1,2], [2,-2], [-2,3], [3,-3]]
[[0,0,0], [0,4,10,96], [96,54,32,11,0], [0,0,0]]
[[-55,-67,-80], [-80,5], [5,5,5], [5,9], [9,9,9,9], [9,14,20,25]]
\$\endgroup\$
2
  • \$\begingroup\$ Will teh array always have at least two elements? \$\endgroup\$
    – Steffan
    Apr 20 at 19:03
  • \$\begingroup\$ @Steffan Given an array of at least two integers \$\endgroup\$
    – pajonk
    Apr 20 at 19:04

16 Answers 16

9
\$\begingroup\$

BQN, 30 bytesSBCS

{(𝕩⊏˜+`)⌾∾0∾¨=˜⊔+`»≠⟜«×1↓-⟜»𝕩}

Run online!

Code Explanation Example
x Argument ⟨ 1 1 2 4 ⟩
1↓-⟜» Differences of adjacent arguments ⟨ 0 1 2 ⟩
× Sign of each value ⟨ 0 1 1 ⟩
»≠⟜« Mark starts of runs of equal adjacent values with a 1,
except the first.
⟨ 0 1 0 ⟩
+` Cumulative sum ⟨ 0 1 1 ⟩
Group indices by their value ⟨ ⟨ 0 ⟩ ⟨ 1 2 ⟩ ⟩
Self-equal; replace all values by 1's ⟨ ⟨ 1 ⟩ ⟨ 1 1 ⟩ ⟩
0∾¨ Prepend a 0 to each group ⟨ ⟨ 0 1 ⟩ ⟨ 0 1 1 ⟩ ⟩
+`⌾∾ Cumulative sum under flattening the list ⟨ ⟨ 0 1 ⟩ ⟨ 1 2 3 ⟩ ⟩
𝕩⊏˜ Index into the argument ⟨ ⟨ 1 1 ⟩ ⟨ 1 2 4 ⟩ ⟩
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Love the explanation format. \$\endgroup\$
    – Jonah
    Apr 21 at 13:59
5
\$\begingroup\$

Jelly, 10 bytes

IṠŒɠ1;ÄrƝị

Try it online!

 Ṡ            Take the signs of the
I             differences between adjacent elements.
  Œɠ          Count the lengths of consecutive runs of equal signs,
    1;        prepend a 1,
      Ä       take the cumulative sums,
       rƝ     get the inclusive ranges between each pair of adjacent values,
         ị    and index each number in each range back into the input.
\$\endgroup\$
5
\$\begingroup\$

R, 83 74 73 bytes

Edit: -9 bytes thanks to @Giuseppe.

\(x,`?`=diff)Map(\(i,j)x[i:j],c(1,w<-which(!!?sign(?x))+1),c(w,sum(x|1)))

Attempt This Online!

Pretty straightforward:

  1. Get signs of differences (identify trends).
  2. Find starting positions of the trends.
  3. Those (shifted by one) are also the ends of the trends.
  4. Map over trends and extract them using start:end.

Alternative inspired by Unrelated String's Jelly answer:

R, 73 71 bytes

Edit: -2 bytes thanks to @Giuseppe.

\(x)Map(\(i,j)x[i:j],head(w<-diffinv(rle(sign(diff(x)))$l)+1,-1),w[-1])

Attempt This Online!

\$\endgroup\$
6
  • \$\begingroup\$ 74 bytes \$\endgroup\$
    – Giuseppe
    Apr 21 at 0:20
  • \$\begingroup\$ @Giuseppe, very nice - thanks! \$\endgroup\$
    – pajonk
    Apr 21 at 4:41
  • \$\begingroup\$ head(w<-cumsum 😏😳 \$\endgroup\$ Apr 21 at 10:19
  • \$\begingroup\$ 71 bytes \$\endgroup\$
    – Giuseppe
    Apr 21 at 10:36
  • \$\begingroup\$ @Giuseppe, thanks. I don't know how could I forget about diffinv again... \$\endgroup\$
    – pajonk
    Apr 21 at 11:00
3
\$\begingroup\$

Vyxal, 14 bytes

¯±ĠvL0p¦2lvƒṡİ

Try it Online!

Port of Unrelated String's Jelly answer.

  ĠvL          # Get the lengths of the runs of identical...'
 ±             # Signs
¯              # In the forward differences of the input
     0p¦       # Prepend a zero and get cumulative sums
         lvƒ   # Over runs of length...
        2      # 2
            ṡ  # Inclusive range
             İ # Index those into the original array
\$\endgroup\$
3
\$\begingroup\$

Ruby, 60 bytes

->a,*w{a.each_cons(2){|x,y|(a!=a=x<=>y)&&w<<[x];w[-1]<<y};w}

Try it online!

How it works

->a,*w{

Initialize w as an empty array, we will store the result there

a.each_cons(2)

Iterate on all pairs of consecutive elements of a

{|x,y|(a!=a=x<=>y)

Use a as a temporary variable to store the result of the last comparison. At the beginning its value will be different from anything else. On every iteration, check if the result of the comparison of the next 2 values is different from the previous result.

&&w<<[x]

If it's different, put the first of the two value in a new array inside of w.

;w[-1]<<y

Append the second value to the last array (continuation of sequence if the comparison result is the same, new array if it is different.)

};w}

Return w after the last iteration.

\$\endgroup\$
2
\$\begingroup\$

Perl 5, 107 bytes

sub{my@r;!@r?push@r,$l=[$_]:@$l<2||($_<=>$L)==($L<=>$$l[-2])?push@$l,$_:push@r,$l=[$L,$_]and$L=$_ for@_;@r}

Try it online!

\$\endgroup\$
2
\$\begingroup\$

JavaScript, 84 bytes

-2 bytes from Arnauld, thanks!

f=([n,...a],d,s=[g=[]])=>1/n?f(a,D=Math.sign(a[0]-n,g.push(n)),d-D?[...s,g=[n]]:s):s

Try it online!

Explained

f = (
  [n, ...a],     // n: first item of array, a: remaining items
  d,              // d: previous difference
  s = [g = []]  // s: partial output, g: current group
) =>
  1 / n ?        // if n is a number, recurse, else return s
    f(           // call f() again
      a,         // remaining items
      D = Math.sign(a[0] - n, g.push(n)),  // assign new d to D
      d - D ?    // check if direction changes (sneakily push n to g)
        [...s, g = [n]] :  // if new direction, start a new group
        s,                 // else keep the same
    ) :
    s
\$\endgroup\$
1
2
\$\begingroup\$

05AB1E, 14 11 bytes

¥.±γ€g.¥üŸè

-3 bytes porting @UnrelatedString's Jelly answer.

Try it online or verify all test cases.

Original 14 bytes answer:

ü2.γ`.S}ε˜ι`θª

Try it online or verify all test cases.

Explanation:

¥           # Get the forward-differences of the (implicit) input-list
 .±         # Get the sign (-1 if <0; 0 if 0; 1 if >0) for each value
   γ        # Split it into equal adjacent values
    €g      # Get the length of each inner list
      .¥    # Undelta with leading 0
        ü   # For each overlapping pair:
         Ÿ  #  Create an inclusive ranged list
          è # (0-based) index those lists into the (implicit) input
            # (after which the resulting list of lists is output implicitly)

ü2          # Split the (implicit) input-list into overlapping pairs
  .γ        # Adjacent group these pairs by:
    `       #  Pop and push both values in the pair to the stack
     .S     #  And compare them (-1 if a<b; 0 if a==b; 1 if a>b)
   }ε       # After the adjacent group-by: map over each list of pairs:
     ˜      #  Flatten the list
      ι     #  Uninterleave it into two parts ([a,b,c,d,e,f] → [[a,c,e],[b,d,f]])
       `    #  Pop and push both lists to the stack
        θ   #  Pop the second list and leave just its last value
         ª  #  Append it to the first list ([a,c,e,f])
            # (after which the resulting list of lists is output implicitly)
\$\endgroup\$
2
\$\begingroup\$

TI-Basic, 65 bytes

Input A
tanh(99ΔList(ʟA
augment(Ans,{9→B
1→I
For(J,1,N
If ʟB(I)=ʟB(J
End
Disp seq(ʟA(X),X,I,J
J→I
End

prints each sublist on a new line

  • ΔList( is a diff function
  • tanh(99X) is a sign function for integers: tanh(0)=0, tanh(99)=1 and tanh(-99)=-1
  • we add a 9 at the end of ʟB, different than any value a diff can take, so that the last sublist is always printed
  • I stores the index of the start of the current sublist
  • if the signs are equal, we cut the For loop short with the End
  • otherwise, the End is skipped, so we print the sublist from I to J and set I to J

demo

\$\endgroup\$
2
\$\begingroup\$

Haskell, 95 bytes

(!)=compare
x#y=show x++' ':y
f(a:x@(b:c:t))|a!b==b!c=a#f x|1>0=a#(b#('|':f x))
f[x,y]=x#(y#"")

Try it online!

A bit weird output: a string where trends are separated by | and elements are space separated.

Alternatively
Output a list of lists for 111 bytes.

f l=l?(l!2)
(#)=compare
(a:t@(b:c:r))!x|a#b==b#c=t!(x+1)|1>0=x:t!2
_!x=[x]
l?(h:t)=take h l:drop(h-1)l?t
_?x=[]

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Vyxal, 15 bytes

¯±ĠvL1p¦2lvƒṡ‹İ

Try it Online!

How?

¯±ĠvL1p¦2lvƒṡ‹İ
¯               # Deltas (consecutive differences) of (implicit) input
 ±              # Sign of each
  Ġ             # Group consecutive identical items
   vL           # Length of each
     1p         # Prepend a one
       ¦        # Cumulative sums
        2lvƒṡ   # For each overlapping pair, get an inclusive range between them
             ‹  # Decrement each
              İ # Index each into the (implicit) input
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 90 bytes

f=(a,s,c=[],[v,...b]=a,x=[...c,v],q=Math.sign(b[0]-v))=>1/v?s-q?[x,...f(a,q)]:f(b,q,x):[c]

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Retina, 75 bytes

\d+
*
L$`(?<=((_+)))((,(?<2>\2_+))+|(,(?!\1|\6)(_+))+|(,\1)+)\b
$1$&
_+
$.&

Try it online! Only supports positive integers due to language limitations. Explanation:

\d+
*

Convert to unary.

(?<=((_+)))

For each trend starting just after a value (either the first value or the last value of the previous trend)...

((,(?<2>\2_+))+|(,(?!\1|\6)(_+))+|(,\1)+)\b

... match either a trend where the value increases each time, a trend where the value decreases each time, or a trend where the value doesn't change, ...

L$`
$1$&

... and output the matched trend preceded by the initial value.

_+
$.&

Convert to decimal.

\$\endgroup\$
1
\$\begingroup\$

Julia, 62 bytes

!x=(a=1;~ =diff;findall(~[sign.(~x);9].!=0).|>i->x[a:(a=1+i)])

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Python, 132 bytes

c=lambda a,b,*_:(a>b)-(a<b)
f=lambda h,*t,v=3:([[[h]],[]][c(h,*t)==v]+((r:=f(v=c(h,*t),*t))[0].insert(0,h)or r)if t else[[h]])[v>2:]

Attempt This Online!

Python, 138 136 bytes

-2 bytes thanks to @Kevin Cruijssen

t=lambda a,b,*c:(a>b)-(a<b)
def f(x):
    i=j=0
    while-~i<len(x):
        k=t(*x[j:])
        while-~j<len(x)and t(*x[j:])==k:j+=1
        yield x[i:j+1];i=j

Attempt This Online!

\$\endgroup\$
1
  • \$\begingroup\$ while i+1/while j+1 can be while-~i/while-~j to save 2 bytes. (Relevant tip.) \$\endgroup\$ Apr 21 at 10:25
1
\$\begingroup\$

MATLAB, 92 bytes

function t(m)
d=[1 find(diff(sign(diff(m))))+1 numel(m)];for i=2:numel(d)
m(d(i-1):d(i)),end

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.