23
\$\begingroup\$

Inspired by this Stack Overflow question: Sorting a list: numbers in ascending, letters in descending. Your task is to solve the following problem and, as this is , you should do so in as few as bytes as possible.

You should take a list of objects as input containing letters (any reasonable form: string, char, etc) and numbers. You should then sort the numbers into ascending order and the letters into descending order. However, you should keep letters in letter positions and numbers in number positions. For example, if the list is:

[L, D, L, L, D, L]

The output list should be in the form of:

[L, D, L, L, D, L]

Workthrough

Input: ['a', 2, 'b', 1, 'c', 3]

  • Sort the numbers into ascending order: [1, 2, 3]
  • Sort the letters into descending order: ['c', 'b', 'a']
  • Join them back but keep the order the same: ['c', 1', 'b', 2, 'a', 3]

Rules

  • The list will only contain letters and digits.
  • The list may be empty.
  • The list may only contain letters or only digits.
  • If your language does not support mixed type arrays you may use digit characters instead of numbers. Note that if your language does support this you must use mixed types.
  • Letters will only be [a-z] or [A-Z], you may choose which one.
  • Letters are sorted as a being lowest, z being highest i.e. a = 1, z = 26.
  • Standard loopholes are forbidden.
  • I/O may be by any standard means including as a string.

Test cases

[5, 'a', 'x', 3, 6, 'b'] -> [3, 'x', 'b', 5, 6, 'a']

[ 3, 2, 1] -> [ 1, 2, 3 ]

[ 'a', 'b', 'c' ] -> [ 'c', 'b', 'a' ]

[] -> []

[ 2, 3, 2, 1 ] -> [1, 2, 2, 3]

As this is the shortest answer in bytes wins!

\$\endgroup\$
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Martin Ender Sep 3 '17 at 13:51

25 Answers 25

6
\$\begingroup\$

Retina, 10 bytes

O`\d
O^`\D

Try it online!

The O stage in Retina can directly perform the kind of selective sorting required by this challenge.

Here the first line sorts digits while the second line sorts non-digits in reverse.

\$\endgroup\$
13
\$\begingroup\$

Python 2, 53 52 bytes

-2 bytes thanks to g.rocket
-1 byte thanks to Jonathan Frech

def F(x):n=sorted(x);print[n.pop((e<x)-1)for e in x]

Try it online!

The sorted list will have the numbers first and then the chars like [3, 5, 6, 'a', 'b', 'x'], then use e<x to filter what is number and what is char, in python any number is less than a list (input) and a list is less than a string.

\$\endgroup\$
  • \$\begingroup\$ This version fails with IndexError: pop index out of range. The former solution did work. \$\endgroup\$ – Mr. Xcoder Aug 9 '17 at 11:28
  • \$\begingroup\$ This works though, with 55 bytes too. 1-(e<'`') should be (e<'`')-1. You just placed them in the wrong order. BTW, you ninja'd me :/ I had this \$\endgroup\$ – Mr. Xcoder Aug 9 '17 at 11:30
  • \$\begingroup\$ @Mr.Xcoder thanks for the help c: \$\endgroup\$ – Rod Aug 9 '17 at 11:31
  • 2
    \$\begingroup\$ Save two with e>x \$\endgroup\$ – g.rocket Aug 10 '17 at 1:16
  • 1
    \$\begingroup\$ @RootTwo That does the opposite of the intended behavior. \$\endgroup\$ – LyricLy Aug 10 '17 at 7:09
9
\$\begingroup\$

APL (Dyalog), 27 26 bytes

Expects characters to be uppercase

(⍋⊃¨⊂)@(~e)(⍒⊃¨⊂)@(e←∊∘⎕A)

Try it online!

This is just two applications of the form f@g, apply the function f on the items indicated by g.

For the first application we use:
f: ⍒⊃¨⊂ the descending grades () each pick (⊃¨) from the entire argument ().
g: (e←∊∘⎕A) members () of () the Alphabet (⎕A), and store () this function as e.

For the second application we use:
f: ⍋⊃¨⊂ the ascending grades () each pick (⊃¨) from the entire argument ().
g: (~e) not (~) members of the alphabet (e; the function we stored before)

\$\endgroup\$
  • \$\begingroup\$ I think this needs to cover for all integers, so replace the second filter with 83=⎕DR¨⍵ \$\endgroup\$ – Uriel Aug 9 '17 at 11:14
  • \$\begingroup\$ @Uriel Doesn't actually seem to be a requirement, but did save a byte. Also, ⎕DR is not universally 83 for numbers, only for small integers. \$\endgroup\$ – Adám Aug 9 '17 at 11:47
  • \$\begingroup\$ is it always 3=10|⎕DR for integers? \$\endgroup\$ – Uriel Aug 9 '17 at 12:13
  • \$\begingroup\$ @Uriel Yes: 0=UnicodeChar, 1=Boolean, 2=ClassicChar, 3=int, 5=float, 6=pointer, 7=decimal, 9=complex. ⌊0.1×⎕DR gives you the number of bits used to represent each scalar, except for pointers, which depend on the architecture but always are 326. Thus, all numbers are 2|⎕DR. \$\endgroup\$ – Adám Aug 9 '17 at 12:18
8
\$\begingroup\$

JavaScript (ES6), 71 51 47 bytes

Saved 20 bytes by just using sort(), as suggested by @JustinMariner
Saved 4 more bytes thanks to @CraigAyre

Using a similar approach as Rod's Python answer:

a=>[...a].map(n=>a.sort()[1/n?'shift':'pop']())

Test cases

let f =

a=>[...a].map(n=>a.sort()[1/n?'shift':'pop']())

console.log(JSON.stringify(f(['a', 2, 'b', 1, 'c', 3]))) // -> ['c', 1', 'b', 2, 'a', 3]
console.log(JSON.stringify(f([5, 'a', 'x', 3, 6, 'b']))) // -> [3, 'x', 'b', 5, 6, 'a']
console.log(JSON.stringify(f([3, 2, 1]))) // -> [ 1, 2, 3 ]
console.log(JSON.stringify(f(['a', 'b', 'c']))) // -> [ 'c', 'b', 'a' ]
console.log(JSON.stringify(f([]))) // -> []
console.log(JSON.stringify(f([2, 3, 2, 1]))) // -> [1, 2, 2, 3]

\$\endgroup\$
  • \$\begingroup\$ Am I missing something or couldn't you remove the whole sort function and just use sort() on its own? It seems to sort in the same way without a function (in Chrome/FF/Edge). \$\endgroup\$ – Justin Mariner Aug 9 '17 at 19:05
  • \$\begingroup\$ @JustinMariner At first, I thought the numeric values could be numbers -- in which case a simple sort() would fail. But since we're limited to digits, you are correct: that does work. Thanks! \$\endgroup\$ – Arnauld Aug 9 '17 at 19:12
  • 1
    \$\begingroup\$ Nice solution, could you shift/pop on a.sort() each loop instead of assigning to x?: .map(n=>a.sort()[1/n?'shift':'pop']()) \$\endgroup\$ – Craig Ayre Aug 9 '17 at 19:50
  • \$\begingroup\$ @CraigAyre Good catch! \$\endgroup\$ – Arnauld Aug 9 '17 at 20:00
  • \$\begingroup\$ I'm pretty sure +n can be used instead of 1/n \$\endgroup\$ – Conor O'Brien Aug 10 '17 at 22:56
5
\$\begingroup\$

R, 83 76 bytes

-7 bytes thanks to Miff

function(n){u=unlist
d=n%in%0:9
n[d]=sort(u(n[d]))
n[!d]=sort(u(n[!d]),T)
n}

This is the same as the below, but it allows for mixed-type input as a list rather than an atomic vector (which would typecast everything as characters with mixed types).

Try it online!

R, 68 61 bytes

-7 bytes thanks to Miff

function(n){d=n%in%0:9
n[d]=sort(n[d])
n[!d]=sort(n[!d],T)
n}

Anonymous function. All digits are cast to characters in this case. n[-d] is the array without the digits. Returns NULL (empty list) on empty input.

Try it online!

\$\endgroup\$
  • \$\begingroup\$ You can shave off a few characters with d=n%in%0:9 \$\endgroup\$ – Miff Aug 9 '17 at 14:49
4
\$\begingroup\$

Japt, 18 15 bytes

Thanks @Shaggy for -3 bytes and for help fixing for arrays with 0s.


c ñc
®¤?Vv :Vo

First line is intentionally left blank.

Try it online! using -Q to view the formatted array.

Explanation

First line is blank to avoid overwriting the input array.
[5, 'a', 'x', 3, 6, 'b']

c ñc

Make a copy by flattening (c) the input array, then sort (ñ) with strings represented by their char code (c). This is stored in V.
[3, 5, 6, 'a', 'b', 'x']

£

Then map the input array by the function...

¤?Vv :Vo

Turn numbers into binary strings (truthy) or strings into "" (falsy) (¤). If truthy, remove from the start of V (v), otherwise remove from the end (o).

\$\endgroup\$
  • \$\begingroup\$ 15 bytes \$\endgroup\$ – Shaggy Aug 9 '17 at 12:27
  • \$\begingroup\$ @Shaggy Nice, that's really clever. Thanks! \$\endgroup\$ – Justin Mariner Aug 9 '17 at 12:32
  • \$\begingroup\$ You forgot to switch Vo and Vv around. I'm convinced there has to be a shorter way, without the ternary. \$\endgroup\$ – Shaggy Aug 9 '17 at 12:37
  • \$\begingroup\$ @Shaggy Oh, whoops. And yeah, just if o could remove from the beginning with negative values or something... \$\endgroup\$ – Justin Mariner Aug 9 '17 at 12:40
4
\$\begingroup\$

JavaScript, 164 162 158 142 bytes

edit 1: 2 bytes less after removing a redundant assignment of v.

edit 2: 4 bytes less thanks to TheLethalCoder.

edit 3: 16 bytes less thanks to brilliant hints from Justin Mariner

x=>eval("n=v=>typeof(v)=='number';l=x.length;for(i=0;i<l;x[i++]=x[m],x[m]=w){for(v=w=x[j=m=i];++j<l;)n(e=x[j])==n(w)&&e<v==n(w)&&(m=j,v=e)}x")

It's my very first time in code-golf, so it can surely be improved... But still, worth a try.

The program performs a variant of selection sort, which only takes into account the values of the same type as the current one (swapping only a number and a number, or a letter and a letter)

Readable form:

x=>eval("
    n=v=>typeof(v)=='number';
    l=x.length;
    for(i=0;i<l;x[i++]=x[m],x[m]=w){
        for(v=w=x[j=m=i];++j<l;) 
            n(e=x[j])==n(w) && e<v==n(w) && (m=j,v=e)
    }
    x
")
\$\endgroup\$
  • \$\begingroup\$ for(j=i+1;j<l;j++) -> for(j=i++;++j<l;) and remove the increment in the outer loop. \$\endgroup\$ – TheLethalCoder Aug 9 '17 at 13:21
  • \$\begingroup\$ Welcome PPCG too! \$\endgroup\$ – TheLethalCoder Aug 9 '17 at 13:22
  • \$\begingroup\$ @TheLethalCoder if we increment the counters so early, we'll also need to change the lines where i and j are used... But the idea is really smart, I'll think how to make use of it anyway. \$\endgroup\$ – mackoo13 Aug 9 '17 at 13:44
  • \$\begingroup\$ You can increment j as I suggested, I didn't see you use i further down just change x[i]=x[m] too x[i++]=x[m] \$\endgroup\$ – TheLethalCoder Aug 9 '17 at 13:49
  • \$\begingroup\$ Ah, sure... Why didn't I think of x[i++]=x[m]... Thanks! \$\endgroup\$ – mackoo13 Aug 9 '17 at 13:56
3
\$\begingroup\$

C++17 (gcc), 219 bytes

#include <variant>
#include <set>
using V=std::variant<char,int>;void f(V*a,V*b){std::set<V>S[2];for(V*c=a;c<b;++c)S[c->index()].insert(*c);auto
C=S->rbegin();auto N=S[1].begin();for(;a<b;++a)*a=(a->index()?*N++:*C++);}

Try it online!

Hardly competitive. But I must support mixed-type arrays? FINE.

Accepts an array of variants in range style, and modifies it in place. Copies the input into two sorted sets, and then back into the input/output array.

\$\endgroup\$
  • \$\begingroup\$ This is interesting. I wouldn't interpret "supporting mixed-type arrays" in this way. Otherwise, I'd have to use an array of void * in C ;) But, yes, interesting to see a solution jumping through such a hoop. \$\endgroup\$ – Felix Palmen Aug 10 '17 at 13:08
  • \$\begingroup\$ You can save two bytes by removing the spaces in the #includes \$\endgroup\$ – Conor O'Brien Aug 10 '17 at 22:57
2
\$\begingroup\$

Mathematica, 203 bytes

(K=Reverse;B=Complement;L=Length;S=Position[#,_Integer];T=Sort@Cases[#,_Integer];G=K@B[#,T];V=B[Range@L@#,Flatten@S];R=K@Sort@#;Table[R[[Min@S[[i]]]]=T[[i]],{i,L@T}];Table[R[[V[[i]]]]=G[[i]],{i,L@G}];R)&


Try it online!

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

Jelly, 14 bytes

FOÞɓṪ}Ḣ}ẇØa$?€

Try it online!

Basically a port of Rod's Python solution.

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

Pyth, 12 11 bytes

KSQm.(Kt>\@

Try it online! or Try the Test Suite.


Explanation

KSQm.(Kt<d\@  - Full program with implicit input.

KSQ           - Assign a variable K to the lexicographically sorted input.
   m          - Map over the input (with a variable d).
    .(K       - Pop the sorted list at this location:
       >\@    - If d is lexicographically lower than '@', at 0 (the first element). Else, at -1 (the last element).
\$\endgroup\$
  • \$\begingroup\$ Wait, you don't need to order the entire array, just split in two homogeneous arrays which each should be easily sortable. APL cannot sort mixed arrays either (yet), but I sort each type separately. \$\endgroup\$ – Adám Aug 9 '17 at 12:00
  • \$\begingroup\$ @Adám What do you mean by just split in two homogeneous arrays which each should be easily sortable? \$\endgroup\$ – Mr. Xcoder Aug 9 '17 at 12:00
  • \$\begingroup\$ As described in the OP's "Workthrough": 1. Make note of which elements are numeric and which are character. 2. Extract all numbers into a separate array and sort that. Do the same for the characters. 3. Put the sorted number back into the number slots. DO the same for the characters. \$\endgroup\$ – Adám Aug 9 '17 at 12:03
  • \$\begingroup\$ @Adám If the OP considers this invalid, I will do exactly what you said (This would result in a much, much longer approach) \$\endgroup\$ – Mr. Xcoder Aug 9 '17 at 12:04
2
\$\begingroup\$

Python, 145 139 130 bytes

6 bytes saved thanks to @officialaimm

9 bytes saved thanks to @Chris_Rands

g=lambda s,a:sorted(x for x in s if(type(x)==str)==a)
def f(s):l,n=g(s,1),g(s,0)[::-1];return[[n,l][type(x)==str].pop()for x in s]

Try it online!

\$\endgroup\$
  • \$\begingroup\$ 139 bytes \$\endgroup\$ – officialaimm Aug 9 '17 at 11:46
  • \$\begingroup\$ type(x)==str would save some bytes over using isinstance(...) i think \$\endgroup\$ – Chris_Rands Aug 9 '17 at 13:25
  • \$\begingroup\$ @Chris_Rands thanks! \$\endgroup\$ – Uriel Aug 9 '17 at 13:38
2
\$\begingroup\$

05AB1E, 17 bytes

SaJ¹á{R¹þ{«vyay.;

Try it online!


SaJ               # Push 1 if letter 0 else, for all letters in string.
   ¹á{R           # Reverse sort letters from input.
       ¹þ{        # Regular sort digits from input.
          «       # Concatenate those two things.
           v      # For each letter in the sorted string...
            ya    # 0 if digit, 1 if letter.
              y.; # Replace first instance of 0/1 with digit/letter.

Using the sort-by closure actually was worse: Σ©Ç®ai0<*}}¹SaJsvyay.;

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

Python 3, 77 bytes

This answer is based on the comment that says you can use '1', '2', etc if chars and digits are not comparable in the language. 'a' and 1 are not comparable in Python 3.

def f(s):x=sorted(s,key=lambda c:ord(c)-95);return[x.pop(-(c>'.'))for c in s]
\$\endgroup\$
2
\$\begingroup\$

q/kdb+, 54 53 bytes

Solution:

{x[w,q]:asc[x w:(&)d],desc x q:(&)(~)d:-7=type@/:x;x}

Examples:

q){x[w,q]:asc[x w:(&)d],desc x q:(&)(~)d:-7=type@/:x;x}(5;"a";"x";3;6;"b") / mixed list
3
"x"
"b"
5
6
"a"
q){x[w,q]:asc[x w:(&)d],desc x q:(&)(~)d:-7=type@/:x;x}3 2 1   / simple list
1 2 3
q){x[w,q]:asc[x w:(&)d],desc x q:(&)(~)d:-7=type@/:x;x}"abc"   / simple list
"cba"
q){x[w,q]:asc[x w:(&)d],desc x q:(&)(~)d:-7=type@/:x;x}2 3 2 1 / simple list
1 2 2 3

Explanation:

Find the chars in the list, sort descending, find the longs in the list, sort them ascending, join to get a list of, e.g. ("x";"b";"a";3;5;6), then assign the sorted values back to their original positions in the list, e.g. at 0 3 4 1 2 5.

Golfing is just switching out q keywords (each, where and not) for their k equivalent (which requires them to be wrapped in brackets).

{x[w,q]:asc[x w:where d],desc x q:where not d:-7=type each x;x} / ungolfed
{                                                           ; } / lambda function with 2 statements
                                                 type each x    / return types of elements in mixed list
                                              -7=               / true where item is a long
                                            d:                  / save this bool array in d
                                        not                     / invert
                                  where                         / indices where true (we have chars)
                                q:                              / save these indices in q
                              x                                 / values of x at these indices
                         desc                                   / sort them descending
                        ,                                       / join/contatenate
                where d                                         / indices where we have digits
              w:                                                / save this in w
            x                                                   / values of x at these indices
        asc[           ]                                        / sort them ascending
 x[w,q]:                                                        / assign this list to x at indices w,q
                                                             x  / return x

Edits

  • -1 byte as don't need square brackets around desc
\$\endgroup\$
2
\$\begingroup\$

C (gcc), 125 113 110 bytes

main(i){char*b,*c,s[99];for(gets(c=b=s);*++c||*(c=++b);)i=*b&64,i^*c&64||*c>*b^!i&&(i=*c,*c=*b,*b=i);puts(s);}

Try it online!

Explained:

main(i)
{
    char*b,*c,s[99];

    // slightly modified stupid bubblesort, this line in fact
    // does nested looping with a single for statement
    for(gets(c=b=s);*++c||*(c=++b);)
    // (undefined behavior here, there's no sequence point between accesses to c,
    // so this could go wrong. Works with the gcc version on tio.)

        // determine whether the current b is a letter:
        i=*b&64,

        // for doing anything, b and c must be the same "type":
        i^*c&64

            // when c > b for letter or c <= b for digit
            || *c>*b^!i

            // then swap
            && (i=*c,*c=*b,*b=i);

    puts(s);
}

Letters are expected in uppercase.

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

PHP, 66 bytes:

for($a=$argv,sort($a);a&$c=$argv[++$i];)echo$a[$c<A?++$k:--$argc];

takes input from command line arguments, prints a string. Run with -nr or try it online.

Yields a warning in PHP 7.1; replace a& with ""< to fix.

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

Mathematica, 107 bytes

(s=#;s[[p]]=Sort[s[[p=#&@@@s~($=Position)~_String]],#2~Order~#>0&];s[[c]]=Sort@s[[c=#&@@@s~$~_Integer]];s)&
\$\endgroup\$
1
\$\begingroup\$

C# (.NET Core), 171 bytes

a=>{var b=a.Where(x=>x is int).ToList();b.Sort();int i=0,j=0;return a.Select(x=>b.Contains(x)?b[i++]:a.Except(b).OrderByDescending(y=>y).ToList()[j++]);}

Byte count also includes:

using System.Linq;

Try it online!

Explanation:

a =>
{
    var b = a.Where(x => x is int).ToList(); // Filter to only ints and transform to list
    b.Sort();                                // Sort the list
    int i = 0, j = 0;                        // Create index counters
    return a.Select(x =>                     // Replace each input element with
                    b.Contains(x) ?          // If it is in list b:
                    b[i++] :                 // Get the next element from b
                    a.Except(b)              // Otherwise take input and filter out those in b
                     .OrderByDescending(x=>x)// Order them z to a
                     .ToList()[j++]);        // Get the next element
\$\endgroup\$
1
\$\begingroup\$

Perl 5, 107 + 1 (-n) = 108 bytes

y/][//d;@a=split/, /;@l=sort grep/\D/,@a;@d=sort grep/\d/,@a;@r=map{/\d/?pop@d:shift@l}@a;$"=", ";say"[@r]"

Try it online!

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

Ruby, 265 bytes

x.sort_by(&:to_s).select{|a| a.is_a?(String)}.zip(x.map.with_index {|a, i| a.is_a?(String) ? i : nil}.compact).each{|a,i| x[i] = a}
x.sort_by(&:to_s).select{|a| a.is_a?(Integer)}.zip(x.map.with_index {|a, i| a.is_a?(Integer) ? i : nil}.compact).each{|a,i| x[i] = a}

Try it online!

First timer here, My solution is definetly not the best one. But since this is my first answer, I thought in posting just for the fun of it.

Looking foward to see better Ruby answers, to see what is the best approach. I hope I improve in future answers =)

Readable

x = ["c", 1, "a", 3, "b", 2]

b = x.map.with_index {|a, i| a.is_a?(Integer) ? i : nil}.compact
s = x.map.with_index {|a, i| a.is_a?(String) ? i : nil}.compact

o = x.sort_by(&:to_s).select{|a| a.is_a?(Integer)}
d = x.sort_by(&:to_s).select{|a| a.is_a?(String)}

d.zip s
d.zip(s).each {|a, i| x[i] = a}

o.zip b
o.zip(b).each {|a, i| x[i] = a }

p x
\$\endgroup\$
1
\$\begingroup\$

Haskell, 108 bytes

There may be shorter ways, but I just had to try it with the Lens library.

import Control.Lens
import Data.List
i(!)f=partsOf(traverse.filtered(!'='))%~f.sort
f x=x&i(<)id&i(>)reverse

I could define f to just be the composition of the two i invocations, but I'd still have to apply x to it to avoid a type error from the monomorphism restriction. Note that the type of f is Traversable t => t Char -> t Char so it can be used with Strings which are lists of Chars as well as with arrays of Chars.

Here are the test cases:

*Main> map f ["5ax36b","321","abc","","2321"]
["3xb56a","123","cba","","1223"]
\$\endgroup\$
1
\$\begingroup\$

Python 3, 91 bytes

def f(s):x=sorted(s,key=lambda c:(type(c)==str,c));return[x.pop(-(type(c)==str))for c in s]
\$\endgroup\$
1
\$\begingroup\$

Clojure, 151 bytes

#(map(fn[t c](nth((if(=(type 1)t)vec reverse)(sort((group-by type %)t)))(-(c t)1)))(map type %)(reductions(partial merge-with +)(for[i %]{(type i)1})))

Example:

(def f #( ... ))
(f [5 \a \x 3 6 \b])
; (3 \x \b 5 6 \a)

This calculates the cumulative sum count of integers and characters, and uses it to lookup the correct element from a sorted list of corresponding type's elements.

\$\endgroup\$
0
\$\begingroup\$

APL (Dyalog), 26 bytes

a[⍒i][⍋⍋27>i←(⎕a,⌽⍳9)⍳a←⎕]

(uses ⎕IO=1)

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.