19
\$\begingroup\$

Inspired by one of many bugs I ran into while implementing selection sort in trilangle.

Given a non-empty list of non-negative integers, "sort" the list using the following procedure:

  1. Find and output the smallest value in the list.
  2. If the list only has one element, stop.
  3. Recurse on a sublist:
    • If the last element in the list is the smallest, recurse on everything but the last value.
    • Otherwise, recurse on everything but the first value.

Your code doesn't have to follow this procedure exactly, as long as the result is the same.

Sample implementation in JS:

function* sort(arr) {
    if (arr.length === 1) {
        return yield arr[0];
    }
    const min = Math.min(...arr);
    yield min;
    if (min === arr.at(-1)) {
        arr.pop();
        yield* sort(arr);
    } else {
        const [, ...rest] = arr;
        yield* sort(rest);
    }
}

Some notes on the algorithm:

  • The output will always contain the same number of elements as the input.
  • The input may contain duplicates.
  • Every element in the output is an element of the input, but they don't necessarily appear the same number of times.
  • The elements of the output will be in nondecreasing order.

I/O

I/O defaults apply. Since I don't see this mentioned there, I will explicitly add the following rule:

You are allowed to implement this destructively. If taking the input by reference, the input does not have to have the same value once the procedure is completed. Note that I use this in the sample implementation above: I call .pop() without copying the array first.

Examples

[4, 3, 1, 2] => [1, 1, 1, 2]
[1, 2, 3, 4] => [1, 2, 3, 4]
[3, 2, 1, 0] => [0, 1, 2, 3]
[1, 2, 3, 1] => [1, 1, 2, 3]
[101, 103, 101, 105] => [101, 101, 101, 105]

Scoring

This is , so shortest program (per language) wins.

\$\endgroup\$
3
  • \$\begingroup\$ Can I take the input reversed? \$\endgroup\$ Commented Mar 9, 2023 at 5:30
  • \$\begingroup\$ Can I assume the input is always non-negative? \$\endgroup\$ Commented Mar 9, 2023 at 5:34
  • 1
    \$\begingroup\$ @CommandMaster I'm going to say no, you have to take the input in the specified order. And the question explicitly says "non-negative integers" \$\endgroup\$
    – Bbrk24
    Commented Mar 9, 2023 at 5:43

19 Answers 19

10
\$\begingroup\$

Vyxal, 15 13 10 9 bytes

(:g…Ȯt=ǔḢ

Try it Online!

It's not every day I get to use recursion in a lambda Nevermind, I don't anymore.

-3 thanks to AndrovT

-1 by outputting items on newlines

Explained

(:g…Ȯt=ǔḢ
(          # input length times:
 :g…       #   get the minimum of the top of the stack without popping and print that without popping
    Ȯt     #   push the tail of the item over the top of the stack
      =    #   does that equal the min?
       ǔ   #  rotate the list right that many times (once if tail is min [placing tail at front] or no times at all).
        Ḣ  #  Remove the head of the list
\$\endgroup\$
2
  • 3
    \$\begingroup\$ 10 bytes, apparently checking if the last item is smallest manually is shorter \$\endgroup\$
    – AndrovT
    Commented Mar 9, 2023 at 8:09
  • \$\begingroup\$ @AndrovT very big brain idea. Makes sense that all is shoter than getting min and tail from the same list. \$\endgroup\$
    – lyxal
    Commented Mar 9, 2023 at 9:07
9
\$\begingroup\$

Jelly, 10 bytes

ṖḊṂṄ=Ṫʋ`?ƒ

Try it online!

Full program, printing each element on its own line with a trailing newline.

I tried a couple printing-based solutions earlier (including some with that also attempted to leverage it for ), but they all tied the pure solution. However, Jonathan Allan's golf to that gave me room to fit a print in with the extra space so long as it was also with dyadic chaining, which then required some creative wrestling with the loop quick of choice (at one point the program ended in µµ¿!) until it eventually inspired me to abuse ƒ.

         ƒ    Reduce over the list, starting with the list, by:
              Ignoring the right argument,
        ?     if
     Ṫʋ       the last element (popped) from
    =  `      the list of for each element in the list if it equals
  Ṃ           the minimum
   Ṅ          which is printed on its own line,
Ṗ             then remove the last element,
 Ḋ            else remove the first element.
         ƒ    The end result is an empty list with no effect on the output.

Jelly, 12 11 bytes

ṖḊ=ṂṪƊ?ƬṂ€Ṗ

Try it online!

-1 thanks to Jonathan Allan

Feels kinda clumsy, but may as well post it while it's the shortest :P

       Ƭ       Repeat while unique, collecting all results:
      ?        If
    Ṫ          the last element (popped) from
  =  Ɗ         the list of, for each element of the input, if it's equal to
   Ṃ           the minimum,
Ṗ              then remove the last element,
 Ḋ             else remove the first element.
        Ṃ€     Take the minimum of each result
          Ṗ    and remove the last.
\$\endgroup\$
0
9
\$\begingroup\$

Nekomata, 10 bytes

ᶦ{:Ɔ≥$tI}ṁ

Attempt This Online!

ᶦ{:Ɔ≥$tI}ṁ
ᶦ{      }     Iterate the block until it fails
       I      Choose the first non-failed result between the following two:
  :Ɔ≥$          If the last item is the smallest, remove it from the list; otherwise fail
      t         Remove the first item of the list
         ṁ    Take the minimum of each step of the iteration

Nekomata v0.1.0.0, 13 bytes

ⁱ{:↔C≥↔$t?∃}⊥
ⁱ{:↔C≥↔$t?∃}⊥
ⁱ{         }     Iterate the block until it fails
         ?∃       Choose the first non-failed result between the following two:
  :↔C≥↔$            If the last item is the smallest, remove it from the list; otherwise fail
        t           Remove the first item of the list
            ⊥   Take the minimum of each step of the iteration
\$\endgroup\$
2
  • 1
    \$\begingroup\$ This looks interesting, but I don't have Haskell set up on my machine, so I can't try it myself. Do you happen to have an online interpreter up yet? (No worries if not!) \$\endgroup\$
    – Bbrk24
    Commented Mar 9, 2023 at 6:31
  • 1
    \$\begingroup\$ @Bbrk24 Sorry, not yet. \$\endgroup\$
    – alephalpha
    Commented Mar 9, 2023 at 6:33
7
\$\begingroup\$

05AB1E, 10 9 bytes

vDW=Qθ._¦

-1 byte being inspired by the rotate used in @lyxal's Vyxal answer.

Try it online or verify all test cases.

Original 10 bytes answer:

vDW=Êθ·<.$

Outputs the result on separated newlines.

Try it online or verify all test cases.

Explanation:

v           # Loop over the (implicit) input-list, without using its values:
 D          #  Duplicate the current list
            #  (which will be the implicit input-list in the first iteration)
  W         #  Push its minimum (without popping the list)
   =        #  Output it with trailing newline (without popping)
    Q       #  Check for each value in the list whether it's equal to this minimum
     θ      #  Pop and push the last item
      ._    #  Rotate the list that many times towards the left
        ¦   #  Remove the first item

vDW=        # Same as above
    Ê       #  Check for each value in the list whether it's NOT equal to this minimum
     θ      #  Pop and push the last item
      ·     #  Double it
       <    #  Decrease it by 1
        .$  #  Remove that many leading items from the list
            #  (1 will remove the first item; -1 will remove the last item)
\$\endgroup\$
7
\$\begingroup\$

JavaScript (ES6), 65 bytes

f=a=>a+a?[m=Math.min(...b=[...a]),...f(b.pop()^m?a.slice(1):b)]:a

Try it online!

Commented

f = a =>            // recursive function taking the input array a[]
a + a ?             // if a[] is not empty:
  [                 //   append ...
    m = Math.min(   //   ... m, which is the minimum value in a[]
      ...b = [...a] //   define b[] as a copy of a[]
    ),              //
    ...f(           //   append the result of a recursive call:
      b.pop()       //     remove the last element from b[]
      ^ m ?         //     if it's not equal to the minimum:
        a.slice(1)  //       pass a[] without the first element
      :             //     else:
        b           //       pass b[] (i.e. a[] without the last element)
    )               //   end of recursive call
  ]                 //
:                   // else:
  a                 //   stop

JavaScript (ES13), 64 bytes

Suggested by @tsh.

f=a=>a+a?[m=Math.min(...a),...f(a,a.splice((a.at(-1)>m)-1,1))]:a

Attempt This Online!

\$\endgroup\$
3
  • \$\begingroup\$ I was confused by the ...b=[...a] for a moment, but I now see the b=[...a] is to define the copy, and the leading ... is for the Math.min. Didn't knew this was possible without parenthesis (...(b=a[...])). :) Nice answer btw! \$\endgroup\$ Commented Mar 9, 2023 at 10:28
  • \$\begingroup\$ @KevinCruijssen You can also do things like [...condition ? [] : [1,2,3]]. You actually rarely need brackets in these cases. \$\endgroup\$ Commented Mar 9, 2023 at 18:54
  • 1
    \$\begingroup\$ f=a=>a+a?[m=Math.min(...a),...f(a,a.splice((a.at(-1)>m)-1,1))]:a \$\endgroup\$
    – tsh
    Commented Mar 10, 2023 at 6:41
4
\$\begingroup\$

PARI/GP, 57 bytes

f(a)=if(#a,concat(m=vecmin(a),f(a[^if(m-a[#a],1,#a)])),a)

Attempt This Online!

\$\endgroup\$
3
\$\begingroup\$

Charcoal, 25 bytes

Wθ«⟦I⌊θ⟧≔⎇⁼↨θ⁰⌊θ…θ⊖LθΦθλθ

Try it online! Link is to verbose version of code. Explanation:

Wθ«

Repeat until the list is empty.

⟦I⌊θ⟧

Output its minimum.

≔⎇⁼↨θ⁰⌊θ…θ⊖LθΦθλθ

Remove the last or first element as appropriate.

\$\endgroup\$
3
\$\begingroup\$

Excel (ms365), 153 bytes

enter image description here - enter image description here - enter image description here - enter image description here - enter image description here

With data in A1:A4, formula in B1:

=TOCOL(BYCOL(REDUCE(A1:A4,A1:A3,LAMBDA(x,y,LET(z,TOCOL(TAKE(x,,-1),2),HSTACK(x,IF(TAKE(z,-1)=MIN(z),DROP(z,-1),DROP(z,1)))))),LAMBDA(a,MIN(TOCOL(a,2)))))
\$\endgroup\$
5
  • \$\begingroup\$ If I wanted to give a longer input, would I only have to change the A4 near the start of the formula, or is there more to it? \$\endgroup\$
    – Bbrk24
    Commented Mar 9, 2023 at 14:05
  • 2
    \$\begingroup\$ The only changes are A1:A4,A1:A3. Note that the A3 reference is one below the previous A4 reference @Bbrk24 \$\endgroup\$
    – JvdV
    Commented Mar 9, 2023 at 14:17
  • \$\begingroup\$ Maybe the part IF(TAKE(z,-1)=MIN(z),DROP(z,-1),DROP(z,1)) could be abbreviated to DROP(z,-1^(TAKE(z,-1)=MIN(z)))? \$\endgroup\$ Commented Mar 11, 2023 at 15:08
  • \$\begingroup\$ I've been playing with these constructs as well however it appears I couldn't get this to work inside DROP(). Could you? @JosWoolley \$\endgroup\$
    – JvdV
    Commented Mar 12, 2023 at 13:07
  • \$\begingroup\$ Hmmm. Works for my answer but not yours. Must be something buggy to do with its position within the LAMBDA/LET construction. \$\endgroup\$ Commented Mar 12, 2023 at 13:12
3
\$\begingroup\$

R, 68 63 57 bytes

f=function(l)if(L<-sum(l|1))c(m<-min(l),f(l[-L^!l[L]>m]))

Try it online!

-5 bytes thanks to pajonk. 50 bytes if using newer versions of R.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I think returning empty list in the if is unnecessary: -5 bytes. \$\endgroup\$
    – pajonk
    Commented Mar 9, 2023 at 19:41
  • \$\begingroup\$ @pajonk ah, yes, you're right. \$\endgroup\$
    – Giuseppe
    Commented Mar 9, 2023 at 20:08
2
\$\begingroup\$

Python, 53 bytes

f=lambda l:l and[m:=min(l)]+f(l[l[-1]>m:][:len(l)-1])

Attempt This Online!

Find the minimum. Remove the first element, or not, as appropriate; then take the first n−1 elements where n is the original number of elements, which removes the last element if the first wasn't removed. Call recursively.

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

Swift 5.6, 96 bytes

func f(x:[Int])->[Int]{x.min().map{[$0]+f(x:$0==x.last ?x.dropLast():[_](x.dropFirst()))} ?? []}

Try it on SwiftFiddle

It's not often you see Optional.map(_:) used in real-world code, since it's easily confused with other operations of the same name (i.e. Array.map(_:)).

Ungolfed:

func f(_ arr: [Int]) -> [Int] {
    return arr.min()
        .map { min in
            [min] + f(
                arr.last == min
                    ? arr.dropLast()
                    : Array(arr.dropFirst()) // dropFirst is lazy by default
            )
        } ?? []
}

Or, written without .map:

func f(_ arr: [Int]) -> [Int] {
    if let min = arr.min() {
        return [min] + f(
            arr.last == min
                ? arr.dropLast()
                : Array(arr.dropFirst())
        )
    } else {
        return []
    }
}
\$\endgroup\$
2
\$\begingroup\$

Arturo, 63 bytes

f:$[a][([]=a)?->a[m:min a@[m]++f(m=last a)?->chop a->drop a 1]]

Try it

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

Pip, 19 bytes

WgIg@v=PMNgDQgELPOg

Takes input as multiple command-line arguments. Attempt This Online!

Explanation

I was expecting to use recursion for this, but a plain old while loop ended up shorter.

WgIg@v=PMNgDQgELPOg
                    ; g is list of command-line args; v is -1 (implicit)
Wg                  ; Loop while g is not empty:
  I                 ;  If
        MNg         ;   Minimum of g
       P            ;   (Print that value)
      =             ;   Is equal to
   g@v              ;   Last element of g
                    ;  Then
           DQg      ;   Remove last element of g
              EL    ;  Else
                POg ;   Remove first element of g
\$\endgroup\$
2
\$\begingroup\$

Haskell + hgl, 25 bytes

uue$mn-<(nt?.tl~<gj*=*mn)

Explanation

This answer is based around the uue builtin, which has the type:

uue :: (List a -> (b, List a)) -> List a -> List b

This is a bit complex but it basically describes the recursion scheme we want, it accepts a function which removes a value from a list, and repeatedly applies the function until the list is empty collecting the results in a list.

With this all we have to do is build a function which takes a list and "removes" the proper value. Getting the value itself is really easy, it's the minimum, so we can just use mn. We just have to build the list itself. nt removes the last element from a list and tl removes the first. We combine them with the ternary (?.) to make nt?.tl which accepts a boolean and a list and removes the last if true and the first otherwise. Then we make gj*=*mn which takes a list and compares if the minimum mn and the last gj are equal. We combine these with (~<) to thread their arguments together, to build

nt?.tl~<gj*=*mn

Which removes an element based on the logic described in the question.

To finish this off we use the fanout (-<) to combine the parts int a single function emitting a tuple, and feed that to uue.

Reflection

This is pretty good, the logic in the question itself is pretty messy, and it feels like this captures it pretty efficiently. But I'm seeing a few things that could be improved.

  • I could pre-compose uue and variants with a fanout. This can't be done with every unfolding function, but it would save a bunch of bytes here, and has reusability.
    uef mn$nt?.tl~<gj*=*mn
    
  • ixu could do with a version pre-composed with ø. i.e. an "interate until empty" function. This would save 1 byte:
    mn<<ixe(nt?.tl~<gj*=*mn)
    
    While I'm at that I might as well add other variants, like ixz for "iterate until zero".
\$\endgroup\$
1
\$\begingroup\$

Go, 166 160 bytes

func f(I[]int)[]int{m,L,k:=I[0],len(I),I
if L<2{return I}
for _,e:=range I{if e<m{m=e}}
if m==I[L-1]{k=f(I[:L-1])}else{k=f(I[1:])}
return append([]int{m},k...)}

Attempt This Online!

A quick-and-dirty port of the sample implementation.

Changelog:

  • L==1 -> L<2, return I[0:1] -> return I: -5 bytes
\$\endgroup\$
4
  • \$\begingroup\$ I don't think the [0:1] on the second line is necessary at all, and the fact that the input is non-empty means L==1 could be L<2. There's probably better ways to shorten it, but I don't know Go. \$\endgroup\$
    – Bbrk24
    Commented Mar 9, 2023 at 15:41
  • \$\begingroup\$ [0:1] because just [0] is an int, vs return type []int \$\endgroup\$
    – bigyihsuan
    Commented Mar 9, 2023 at 19:38
  • \$\begingroup\$ If the list only has one element, why can't you just return I? \$\endgroup\$
    – Bbrk24
    Commented Mar 9, 2023 at 19:38
  • \$\begingroup\$ that's very true \$\endgroup\$
    – bigyihsuan
    Commented Mar 9, 2023 at 19:41
1
\$\begingroup\$

Julia 1.0, 41 bytes

!l=!l[(2:end).-(l[end]==@show min(l...))]

Try it online!

I interpreted "1. Find and output the smallest value in the list." as "print the value with garbage around it", hopefully that's acceptable. Also ends with an error. Otherwise, cleaner version below

Julia 1.0, 55 bytes

!l=l>[] ? [(m=min(l...););!l[(2:end).-(l[end]==m)]] : l

Try it online!

Clean version (returns the "sorted" list)

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

Excel, 136 bytes

Define g in Name Manager as:

=LAMBDA(a,
    LET(
        b,TAKE(a,-1),
        c,MIN(a),
        d,DROP(a,-1^(c=b)),
        e,MIN(d),
        IF(COUNT(d),VSTACK(e,g(d)))
    )
)

Then, within the worksheet:

=LET(f,A1:A4,VSTACK(MIN(f),DROP(g(f),-1)))

Assumes data in A1:A4.

\$\endgroup\$
3
  • \$\begingroup\$ How did you get 136? I count 192 as written, or 135 without whitespace. \$\endgroup\$
    – Bbrk24
    Commented Mar 11, 2023 at 13:35
  • \$\begingroup\$ Whitespace was included only for easy of reading. If it's forbidden by the forum rules I'll gladly remove. I counted the single-letter definition for g as an extra byte. \$\endgroup\$ Commented Mar 11, 2023 at 13:57
  • \$\begingroup\$ Adding whitespace certainly isn't forbidden, but I thought you have to count any whitespace you do use, so it's beneficial to remove any you don't need. Counting the name of "g" makes sense. \$\endgroup\$
    – Bbrk24
    Commented Mar 11, 2023 at 14:01
1
\$\begingroup\$

Pyth, 13 bytes

#=?qeQ
hSQPQt

Try it online!

Explanation

#=?qeQhSQPQtQ    # implicitly add Q
                 # implicitly assign Q = eval(input())
#                # repeat until error
 =               # assign to Q
  ?qeQhSQ        # if the last element of Q equals the minimum element of Q (min element is printed here by newline)
         PQ      #   all but the last element of Q
           tQ    # else all but the first element of Q
\$\endgroup\$
-1
\$\begingroup\$

as long as the numbers are within a small range, like [0, 10^8), one could do ::::

 echo '1,1,3,101,2,2,2,101,103,4,3,3,4,105,1,3,0,2,1,1' | 
  mawk  '
  function ______(__,___,_,____) {
      for(_ in __) break
      if (length(__) <= (_+=_^=NF=FS=OFS="")) { 
         if (+__[___=_--]<+__[_]) {
               __[_]=__[___] substr("",
               __[___]=__[_])
      } return } _=+(____=" ")
      for(___ in __) { !+(_=__[___]) ? ($!_=(_)____$!_) : $_=$_(____)_ } 
      return split($(_<_),__,NF=FS=OFS=____) 
  } BEGIN { CONVFMT = "%.250g" 
    ORS = "," } { printf("\n [ %s%.0s ]\n\n [ ",
                  $_, split($_,__,",")______(__)) 
    for(___ = length(__); ++_<___;) print __[_]
    printf("%s ]\n\n", __[___]) }'
 [ 1,1,3,101,2,2,2,101,103,4,3,3,4,105,1,3,0,2,1,1 ]


 [ 0,1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,101,101,103,105 ]

instead of doing any sort of comparisons, it's what I would call "NF-sort" - it's essentially like a hybrid between counting sort and pigeonhole (aka bucket) sort,

e.g. right before the last step in the function,

$101 == "101 101" - 2 copies of the string "101"

although the code isn't short at all, it's a purely linear algorithm (aka O(n)) since no comparisons, nested loops, or recursions are needed other than for the special case of "0", which are prepended at $1 instead.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The language I originally did this in has the integer limit of 2^23-1, so well within 10^8. Neat that you managed to do it in O(n), but given that this is shortest code and not best complexity, I don’t expect this answer to be too well received. \$\endgroup\$
    – Bbrk24
    Commented Mar 10, 2023 at 6:26
  • \$\begingroup\$ @Bbrk24 : it's quote-and-quote """"O(n)"""" with implied cheating since it leverages the fact awk reconstructs $0 sequentially, something that's "free" to this code but still very much carries a hidden cost that's hard to substantiate (or just call it O( n log n ) assuming their internal sorting isn't that bad ) ps : if you're bored, that integer limit can be rephrased as :::::::::::::::::::::::::::::::: ::::::::::::::::::; 2 ^ ( 32 - 3^2 ) - 3 + 2 \$\endgroup\$ Commented Mar 10, 2023 at 15:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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