Numbers Increase While Letters Decrease

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!

• Comments are not for extended discussion; this conversation has been moved to chat. Sep 3 '17 at 13:51

Python 2, 5352 51 bytes

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

def F(x):n=sorted(x);print[n.pop(-(e>x))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.

• This version fails with IndexError: pop index out of range. The former solution did work. Aug 9 '17 at 11:28
• 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 Aug 9 '17 at 11:30
• Save two with e>x Aug 10 '17 at 1:16
• Think you can save another one with n.pop(-(e>x)) Aug 10 '17 at 6:09
• @RootTwo That does the opposite of the intended behavior. Aug 10 '17 at 7:09

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)

• I think this needs to cover for all integers, so replace the second filter with 83=⎕DR¨⍵ Aug 9 '17 at 11:14
• @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.
Aug 9 '17 at 11:47
• is it always 3=10|⎕DR for integers? Aug 9 '17 at 12:13
• @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.
Aug 9 '17 at 12:18

JavaScript (ES6), 7151 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]

• 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). Aug 9 '17 at 19:05
• @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! Aug 9 '17 at 19:12
• Nice solution, could you shift/pop on a.sort() each loop instead of assigning to x?: .map(n=>a.sort()[1/n?'shift':'pop']()) Aug 9 '17 at 19:50
• @CraigAyre Good catch! Aug 9 '17 at 20:00
• I'm pretty sure +n can be used instead of 1/n Aug 10 '17 at 22:56

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.

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!

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

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).

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

JavaScript, 164162158 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)

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
")

• for(j=i+1;j<l;j++) -> for(j=i++;++j<l;) and remove the increment in the outer loop. Aug 9 '17 at 13:21
• Welcome PPCG too! Aug 9 '17 at 13:22
• @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. Aug 9 '17 at 13:44
• 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] Aug 9 '17 at 13:49
• Ah, sure... Why didn't I think of x[i++]=x[m]... Thanks! Aug 9 '17 at 13:56

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.

• 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. Aug 10 '17 at 13:08
• You can save two bytes by removing the spaces in the #includes Aug 10 '17 at 22:57

Jelly, 9 bytes

OÞḢṪOƑ}?Ɱ


Try it online!

Takes input as a flat array (Jelly typically treats strings as char arrays, so [5, 'a', 'x', 3, 6, 'b'] -> [5, ['a'], ['x'], 3, 6, ['b']]. The Footer flattens this array for you).

How it works

OÞḢṪOƑ}?Ɱ - Main link. Takes an array A on the left
Þ        - Sort by:
O         -   Ordinal
This shuffles the strings to the end,
sorting both the strings and the numbers
Ɱ - Over each element E in A:
?  -   If statement:
OƑ}   -     Condition: E is a number?
Ḣ       -     Then: Pop and yield the first element of A sorted
Ṫ      -     Else: Pop and yield the last element of A sorted


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)&


FOÞɓṪ}Ḣ}ẇØa$?€  Try it online! Basically a port of Rod's Python solution. 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).  • 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. – Adám Aug 9 '17 at 12:00 • @Adám What do you mean by just split in two homogeneous arrays which each should be easily sortable? Aug 9 '17 at 12:00 • 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. – Adám Aug 9 '17 at 12:03 • @Adám If the OP considers this invalid, I will do exactly what you said (This would result in a much, much longer approach) Aug 9 '17 at 12:04 Python, 145139 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! • 139 bytes Aug 9 '17 at 11:46 • type(x)==str would save some bytes over using isinstance(...) i think Aug 9 '17 at 13:25 • @Chris_Rands thanks! Aug 9 '17 at 13:38 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.; 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]  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 C (gcc), 125113 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. 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. Mathematica, 107 bytes (s=#;s[[p]]=Sort[s[[p=#&@@@s~($=Position)~_String]],#2~Order~#>0&];s[[c]]=Sort@s[[c=#&@@@s~$~_Integer]];s)&  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  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!

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 =)

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


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"]


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]


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.

APL (Dyalog), 26 bytes

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


(uses ⎕IO=1)

Try it online!

05AB1E, 14 bytes

{Ivydićˆë¤ˆ¨]¯


Explanation:

{         # Sort the (implicit) input-list, which will put all sorted digits at the
# front, and all sorted letters at the end
I        # Push the input-list again
v       # For-each over its items y:
ydi    #  If the current character y is a digit:
ć   #   Extract the head of the sorted-list; pop and push remainder-list and first
#   item separated to the stack
ˆ  #   Pop and add this first item to the global array
ë    #  Else (it's a letter instead):
¤   #   Get the last item of the sorted list (without popping the list itself)
ˆ  #   Pop and add this last item to the global array
¨ #   And then remove this last item from the list
]       # Close both the if-else statement and loop
¯      # And push the global array
# (after which it is output implicitly as result)