Numbers Increase While Letters Decrease

23

0

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!

TheLethalCoder

Posted 2017-08-09T10:45:47.160

Reputation: 6 930

Comments are not for extended discussion; this conversation has been moved to chat.

– Martin Ender – 2017-09-03T13:51:22.117

Answers

7

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.

Leo

Posted 2017-08-09T10:45:47.160

Reputation: 8 482

13

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.

Rod

Posted 2017-08-09T10:45:47.160

Reputation: 17 588

This version fails with IndexError: pop index out of range. The former solution did work.

– Mr. Xcoder – 2017-08-09T11:28:21.083

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 – Mr. Xcoder – 2017-08-09T11:30:27.323

@Mr.Xcoder thanks for the help c: – Rod – 2017-08-09T11:31:21.083

save a byte with e<=9 – Chris_Rands – 2017-08-09T15:00:27.843

2Save two with e>x – Gavin S. Yancey – 2017-08-10T01:16:08.160

Think you can save another one with n.pop(-(e>x)) – RootTwo – 2017-08-10T06:09:38.217

1@RootTwo That does the opposite of the intended behavior. – LyricLy – 2017-08-10T07:09:35.967

Saved another byte. – Jonathan Frech – 2017-08-14T04:16:15.773

9

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)

Adám

Posted 2017-08-09T10:45:47.160

Reputation: 37 779

I think this needs to cover for all integers, so replace the second filter with 83=⎕DR¨⍵ – Uriel – 2017-08-09T11:14:08.807

@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. – Adám – 2017-08-09T11:47:42.227

is it always 3=10|⎕DR for integers? – Uriel – 2017-08-09T12:13:55.353

@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. – Adám – 2017-08-09T12:18:47.610

8

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]

Arnauld

Posted 2017-08-09T10:45:47.160

Reputation: 111 334

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). – Justin Mariner – 2017-08-09T19:05:44.367

@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! – Arnauld – 2017-08-09T19:12:05.193

1Nice solution, could you shift/pop on a.sort() each loop instead of assigning to x?: .map(n=>a.sort()[1/n?'shift':'pop']()) – Craig Ayre – 2017-08-09T19:50:38.073

@CraigAyre Good catch! – Arnauld – 2017-08-09T20:00:29.677

I'm pretty sure +n can be used instead of 1/n – Conor O'Brien – 2017-08-10T22:56:37.847

@ConorO'Brien That would work for the test cases, but the OP confirmed in the comments that the input array may contain zeros. – Arnauld – 2017-08-11T00:28:02.003

oh, I see. How about ~n? – Conor O'Brien – 2017-08-11T00:40:00.013

@ConorO'Brien Then that would fail for letters: e.g. ~"a" === -1. – Arnauld – 2017-08-11T00:43:21.897

5

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!

Giuseppe

Posted 2017-08-09T10:45:47.160

Reputation: 21 077

You can shave off a few characters with d=n%in%0:9 – Miff – 2017-08-09T14:49:48.570

4

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

Justin Mariner

Posted 2017-08-09T10:45:47.160

Reputation: 4 746

15 bytes – Shaggy – 2017-08-09T12:27:59.677

@Shaggy Nice, that's really clever. Thanks! – Justin Mariner – 2017-08-09T12:32:38.493

You forgot to switch Vo and Vv around. I'm convinced there has to be a shorter way, without the ternary. – Shaggy – 2017-08-09T12:37:13.817

@Shaggy Oh, whoops. And yeah, just if o could remove from the beginning with negative values or something... – Justin Mariner – 2017-08-09T12:40:45.620

4

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

mackoo13

Posted 2017-08-09T10:45:47.160

Reputation: 61

for(j=i+1;j<l;j++) -> for(j=i++;++j<l;) and remove the increment in the outer loop. – TheLethalCoder – 2017-08-09T13:21:23.473

Welcome PPCG too! – TheLethalCoder – 2017-08-09T13:22:35.683

@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. – mackoo13 – 2017-08-09T13:44:11.587

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] – TheLethalCoder – 2017-08-09T13:49:11.313

Ah, sure... Why didn't I think of x[i++]=x[m]... Thanks! – mackoo13 – 2017-08-09T13:56:41.207

Welcome and nice first answer! I suggest checking out the ES6 Tips, as well as the general JS tips linked there. By using eval to avoid return, moving some assignments and statements around, and moving the typeof check to a helper function, I golfed this down to 142 bytes here.

– Justin Mariner – 2017-08-09T18:37:23.190

3

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.

aschepler

Posted 2017-08-09T10:45:47.160

Reputation: 717

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. – Felix Palmen – 2017-08-10T13:08:02.357

You can save two bytes by removing the spaces in the #includes – Conor O'Brien – 2017-08-10T22:57:19.313

2

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!

Uriel

Posted 2017-08-09T10:45:47.160

Reputation: 11 708

139 bytes – officialaimm – 2017-08-09T11:46:03.807

type(x)==str would save some bytes over using isinstance(...) i think – Chris_Rands – 2017-08-09T13:25:10.683

@Chris_Rands thanks! – Uriel – 2017-08-09T13:38:46.527

2

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!

J42161217

Posted 2017-08-09T10:45:47.160

Reputation: 15 931

2

Jelly, 14 bytes

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

Try it online!

Basically a port of Rod's Python solution.

PurkkaKoodari

Posted 2017-08-09T10:45:47.160

Reputation: 16 699

2

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

Mr. Xcoder

Posted 2017-08-09T10:45:47.160

Reputation: 39 774

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 – 2017-08-09T12:00:15.783

@Adám What do you mean by just split in two homogeneous arrays which each should be easily sortable? – Mr. Xcoder – 2017-08-09T12:00:45.843

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 – 2017-08-09T12:03:35.390

@Adám If the OP considers this invalid, I will do exactly what you said (This would result in a much, much longer approach) – Mr. Xcoder – 2017-08-09T12:04:45.123

2

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.

Felix Palmen

Posted 2017-08-09T10:45:47.160

Reputation: 3 866

2

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

Magic Octopus Urn

Posted 2017-08-09T10:45:47.160

Reputation: 19 422

2

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]

RootTwo

Posted 2017-08-09T10:45:47.160

Reputation: 1 749

2

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

streetster

Posted 2017-08-09T10:45:47.160

Reputation: 3 635

2

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.

Titus

Posted 2017-08-09T10:45:47.160

Reputation: 13 814

1

Mathematica, 107 bytes

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

user202729

Posted 2017-08-09T10:45:47.160

Reputation: 14 620

1

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

Grzegorz Puławski

Posted 2017-08-09T10:45:47.160

Reputation: 781

1

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!

Xcali

Posted 2017-08-09T10:45:47.160

Reputation: 7 671

1

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

Gustavo Gabriel

Posted 2017-08-09T10:45:47.160

Reputation: 111

1

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

Christian Sievers

Posted 2017-08-09T10:45:47.160

Reputation: 6 366

1

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]

RootTwo

Posted 2017-08-09T10:45:47.160

Reputation: 1 749

1

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.

NikoNyrh

Posted 2017-08-09T10:45:47.160

Reputation: 2 361

0

APL (Dyalog), 26 bytes

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

(uses ⎕IO=1)

Try it online!

ngn

Posted 2017-08-09T10:45:47.160

Reputation: 11 449