Sort a nested list

23

You must write a program or function that sorts a nested list. Here are the rules for sorting a nested list:

Let's take this list as an example:

((5, 2), 2, 7, (2, 1, (3, 4)), 9)

Each element in this list has a "priority". An element counts as a number or a sublist. First, get the priority of each element at the same depth. If an element is just a number, its priority is the same as the number itself. If an element is a sublist, its priority is the sum of all the numbers in it, not including any sub-sublists.

So, the priorities of all the elements of depth 1 are:

 (  7 )  2  7  (    3       )  9
((5, 2), 2, 7, (2, 1, (3, 4)), 9)

Sort each element by priority. If there is a tie, you must keep the same order as the original list.

 2  (     3      )  (  7 )  7  9
(2, (2, 1, (3, 4)), (5, 2), 7, 9) 

Repeat for every sublist. So on this sublist

(2, 1, (3, 4))

Our priorities look like:

 2  1  (  7  )
(2, 1, (3, 4))

So sorted, it looks like:

(1, 2, (3, 4))

(3, 4) is already sorted, so we're done. Repeat for (5, 2) which results in (2, 5) and we're done! Our final list is:

(2, (1, 2, (3, 4)), (2, 5), 7, 9) 

Rules:

  • Highly doubtful, but just in case Mathematica has something for this, no nested list sorting builtins are allowed. Regular sorting functions are allowed.

  • I/O can be in any reasonable format.

  • Every sublist will contain at least one number or list. Also, sublists can be nested several levels deep. For example, in (1, 2, (((3)))) the (((3))) has a priority of 0, since it has only sublists in it.

  • Invalid lists (unmatched parentheses, non-numbers, wrong bracket types, negative numbers, etc.) result in undefined behavior.

Test I/O:

(1, 2, 3) ---> (1, 2, 3)

(1, 2, 6, 3, 9, 8) ---> (1, 2, 3, 6, 8, 9)

(4, 3, (2), (1)) ---> ((1), (2), 3, 4)

(4, 3, (2), ((1))) ---> (((1)), (2), 3, 4)

(5, (1, 2, (9, 8))) ---> ((1, 2, (8, 9)), 5)

(3, (1, 2), (2, 1)) ---> (3, (1, 2), (1, 2))

(3, (1, 2, (99)), (2, 1, (34))) ---> (3, (1, 2, (99)), (1, 2, (34)))

(7, 2, (1, (9, 12)), (4, 3, 2, (1, 2))) ---> ((1, (9, 12)), 2, 7, (2, 3, (1, 2), 4))

Shortest answer in bytes wins.

James

Posted 2016-03-22T00:35:54.717

Reputation: 54 537

Can we assume the numbers are integers? – isaacg – 2016-03-22T06:20:25.383

@isaacg Yes, you can. – James – 2016-03-22T07:08:53.880

Answers

5

Jelly, 13 bytes

fFSµ€Ụị߀µ¹<?

Try it online! or verify all test cases.

How it works

fFSµ€Ụị߀µ¹<?  Main link. Input: A (list)

   µ€          Apply the chain to the left to each item B in A.
 F             Flatten B.
f              Filter; intersect B with flattened B, yielding a list.
               This returns the numbers in B if B is a list, [B] if B is a number.
  S            Compute the sum of the resulting list.
     Ụ         Sort the indices of A according to the computed sums.
       ߀      Recursively apply the main link to each B in A.
      ị        Retrieve the items of the list (right) at those indices (left).
         µ     Convert the preceding chain into a single link.
            ?  If:
           <     A compared with itself is truthy:
                   Execute the link to the left.
          ¹      Else, apply the identity function to A.

Comparing (<) a number with itself yields 0 (falsy), but comparing a non-empty list with itself yields a list of 0's (truthy), so < can be used to distinguish numbers from lists.

Dennis

Posted 2016-03-22T00:35:54.717

Reputation: 196 637

0 is False, but a box of 0s is True, but an empty box is False. Interesting how Python works. :P – cat – 2016-03-22T22:38:24.857

Looks like 25 bytes (when encoded in UTF-8) to me. – Rotsor – 2016-03-26T18:42:34.983

@Rotsor That sounds about right. However, Jelly uses custom code page that encodes all 256 characters it understands as single bytes.

– Dennis – 2016-03-26T19:02:26.120

17

Python 2, 114 101 78 73 62 bytes

k=lambda t:t*(t<[])or t.sort(key=k)or sum(z for z in t if[]>z)

I knew there was a better way to filter lists out.

Sorts a python list (and its sublists) in-place.

https://eval.in/540457 thanks @tac for letting me know in-place solutions are acceptable, and @xnor + @feersum for further optimizations!

Orez

Posted 2016-03-22T00:35:54.717

Reputation: 471

1Some more optimization: k=lambda t:t*(t<[])or sum(z for z in t if[t.sort(key=k)]>z). – xnor – 2016-03-22T07:38:16.250

@xnor I think that solution is not quite correct: https://eval.in/540447 . For this example, we recurse down to the first sublist and grab the initial z from it, 5. Then in the conditional, we sort the list we're iterating over(!), so when we grab the next z it is ALSO 5, leading to a sum of 10. We then sort the outer list with these keys and get [6, [1, 5]], which is incorrect as "If there is a tie, you must keep the same order as the original list." The interesting thing is we call sort on both lists twice, so this only happens on equal keys:if the sublist were less it would sort back.

– Orez – 2016-03-22T14:19:37.027

Good catch. It's funny that the iteration continues with the now-sorted list. I feel like there should still be a shorter way to stick in the None output of t.sort(key=k), but I'm not seeing it. – xnor – 2016-03-22T23:29:52.723

False is 0 for the purposes of + and by extension, sum. Can't think of how that saves bytes, though. – CalculatorFeline – 2016-03-23T14:57:30.693

@CatsAreFluffy list.sort returns None, not False. – Dennis – 2016-03-26T17:00:24.623

Oh. But why do you need t*? – CalculatorFeline – 2016-03-26T20:06:10.443

Have you considered sorted? – CalculatorFeline – 2016-03-29T16:07:53.463

4

Lua, 172 bytes

function p(a)if type(a)~="table"then return a end s(a)local t=0 for i,v in next,a do t=t+p(v)end return t end
function s(t)table.sort(t,function(a,b)return p(a)<p(b)end)end

The function s sorts a Lua table (a data structure that serves as a list among other things in Lua) in place according to the rules.

Trebuchette

Posted 2016-03-22T00:35:54.717

Reputation: 1 692

I love how type(a) returns a string – cat – 2016-03-31T01:28:41.393

Finally an answer using Lua. – Leaky Nun – 2016-04-02T08:33:46.447

3

Mathematica, 50 bytes

#0/@SortBy[#,Tr@Cases[#,_Integer,{0,1}]&]~Check~#&

Simple recursive method that uses SortBy. Ignore the messages.

LegionMammal978

Posted 2016-03-22T00:35:54.717

Reputation: 15 731

3

Haskell, 160 151 135 bytes

import Data.List
data T=N Int|T[T]deriving Show
p(N x)=x
p(T t)=sum$[x|N x<-t]
f(N x)=N x
f(T t)=T$sortBy((.p).compare.p)$map f$t

The first problem is nested lists. Haskell requires that all elements of a list have the same type; in particular, an integer and a list of integers aren't the same type. In other words, a variable-nested list isn't a list, it's a rose tree!

So first, we have to define a data type for rose trees:

data T = N Int | T [T]

(Strictly, deriving Show is only necessary if you want to see the results. But that's a technicality.) With this definition in place, we can write a list such as (1, 2, (3, 4)) as

T [N 1, N 2, T [N 3, N 4]]

which is considerably less readable. But whatever; it's a trivial, mechanical translation. Prefix every number with N and every subtree with T.

Now we need to compute priorities. This would be easy if the priority of a subtree was simple the sum of all elements it contains. That would be a trivial recursive loop. But since it isn't, we need to define two functions: one which recurses, and one which does not.

p (N x) = x
p (T t) = sum $ map q t

q (N x) = x
q _     = 0

If we were to sum all subelements, then q would not need to exist, saving a huge number of characters. Oh well!

Edit: Actually, several commentors point out that you can avoid q using a list comprehension: [ x | N x <- t]. Good call, guys!

(In fact, p would not need to exist either; we could have the compiler auto-generate an Ord instance for us in a handful of characters, and this default implementation would match the spec.)

Finally, we need to recursive over all sub-trees and sort them:

f (N x) = N x
f (T t) = T $ sortBy (\ x y -> compare (p x) (p y)) $ map f $ t

That is, f sorts a tree by recursively applying itself to all elements (map f), and then calling the sortBy function to sort the top-level. The first line says that sorting a number does nothing, and is necessary to terminate the recursion.

MathematicalOrchid

Posted 2016-03-22T00:35:54.717

Reputation: 1 451

2sortBy (\ x y -> compare (p x) (p y)) is just sortOn p. Use the infix version of map in p: sum$q<$>t. – nimi – 2016-03-22T17:01:13.240

@nimi Where is sortOn defined? I've always wanted to know... – MathematicalOrchid – 2016-03-22T17:17:12.753

Data.List since 4.8.0.0. – nimi – 2016-03-22T17:26:18.337

@nimi Oh cool, so they finally added it to base? Neat. If I update GHC someday I'll look forward to that... – MathematicalOrchid – 2016-03-22T17:30:11.747

With LambdaCase, p can be golfed as (\case N x->x;T t->sum[x|N x<-t]), obviating the q. Also, (\ x y -> compare (p x) (p y)) is also ((.p).compare.p). – Will Ness – 2016-03-24T14:16:17.907

@WillNess Doesn't adding {-# LANGUAGE LambdaCase #-} negate the savings? Good point about comparing though... – MathematicalOrchid – 2016-03-24T14:21:21.257

ah, yes. those pesky rules of counting... – Will Ness – 2016-03-24T14:23:12.263

I'm always tempted to cheat and say I have all those settings and imports in my ghci.conf file anyway (I actually do), and so don't have to count them. (multi-line let definitions also work in GHCi...) It would drop the count down to 109 bytes (with Data.List autoloaded as well)! Pity. – Will Ness – 2016-03-24T14:28:03.813

2you can still shave off some 16 bytes with the list comprehension trick, p(T t)=sum[x|N x<-t] and data T=N Int|T[T]deriving Show. :) – Will Ness – 2016-03-24T14:33:16.783

@WillNess: if you don't care about imports you can also use on from Data.Function for (compare`on`p). – nimi – 2016-03-24T18:03:26.857

@nimi tryhaskell.org has on already defined too, but there seems to be no way to feed it a data declaration. otherwise, yes, compare `on` p == comparing p == ((.p).compare.p) (comparing is from Data.Ord, tryhaskell also has it already defined). – Will Ness – 2016-03-25T10:45:19.840

Incidentally: I had no idea that a pattern-match failure inside a list comprehension is not an error, just a guard. (Then again, I never use list comprehensions for any reason, so...) – MathematicalOrchid – 2016-03-27T09:51:09.680

1

have you included 2 bytes for each newline in your count? I think we're allowed to count them as singles. Also, there's no need for $ in sum$[x|N x<-t]. So, 135-5-1=129. :)

– Will Ness – 2016-07-31T12:37:03.647

2

CLISP, 380 bytes

(defun q(L)(if(null L)L(append(append(q(car(s(cdr L)(car L))))(list(car L)))(q(cadr(s(cdr L)(car L))))))))(defun s(L E)(if(not(atom(car L)))(setq L(cons(q(car L))(cdr L))))(cond((null L)'(nil nil))((<(v(car L))E)(list(cons(car L)(car(s(cdr L)E)))(cadr(s(cdr L)E))))(T(list(car(s(cdr L)E))(cons(car L)(cadr(s(cdr L)E)))))))(defun v(L)(if(atom L)L(apply'+(remove-if-not #'atom L))))

Call the function q with a list.

I'm a lisp noob, please don't kill me^^

Joba

Posted 2016-03-22T00:35:54.717

Reputation: 151

Haha, I was hoping somebody would do it in lisp! – James – 2016-03-22T17:26:20.007

1

Pyth, 15 bytes

L?sIbbossI#NyMb

Test suite

A recursive function, that works as follows:

L?sIbbossI#NyMb
L                  define y(b):
 ?sIb              If b is an integer:          (invariant under the s function)
     b             Return it.
            yMb    Else, apply y recursively to all of the elements of b,
      o            Then sort b by
        sI#N       For each element, the elements of that list that are integers.
                   This is luckily a nop on integers.
       s           Summed.

isaacg

Posted 2016-03-22T00:35:54.717

Reputation: 39 268

1

Java, 219 bytes

import java.util.*;List f(List l){l.sort(Comparator.comparing(o->{if(o instanceof Integer)return(Integer)o;f((List)o);return((List) o).stream().filter(i->i instanceof Integer).mapToInt(i->(Integer)i).sum();}));return l;}

With line breaks:

import java.util.*;
List f(List l){
    l.sort(Comparator.comparing(o -> {
        if (o instanceof Integer)
            return (Integer) o;
        f((List) o);
        return ((List) o).stream().filter(i -> i instanceof Integer).mapToInt(i -> (Integer) i).sum();
    }));
    return l;
}

There's a lot of casting going on which really racks up the byte count. :P

Integer values are fed into a Comparator, and nested lists are sorted first before the sum of only the integer values are given to the Comparator. These values are how the Comparator determines their position within the list when it is sorted.

Try it here.

TNT

Posted 2016-03-22T00:35:54.717

Reputation: 2 442

1Here is the same technique at 154 bytes int f(List l){l.sort(Comparator.comparing(o->o instanceof Integer?(int)o:f((List)o)));return l.stream().mapToInt(o->o instanceof Integer?(int)o:0).sum();} – Andreas – 2016-03-22T22:38:14.720

I think there is a lot more to squeeze, too. – Andreas – 2016-03-22T22:38:45.083

But there are a couple issues: you cannot explicitly convert Object to int like that, and the challenge seems to require that a list is output. – TNT – 2016-03-22T23:19:48.443

You actually save 1 byte by changing the instanceof to check for List instead of Integer. Integer is 7 bytes w/o curly braces, but List is 6 bytes with it. – Blue – 2016-03-23T11:23:12.637

@TNT You can cast an Object to a primitive in java 1.7 or higher. Of course if the Object is null you'll get a npe. I don't see any problem with sorting the list in place, the challenge doesn't seem to speak to the issue directly. – Andreas – 2016-03-23T16:14:41.627

0

JavaScript (ES6), 86 bytes

f=a=>a.map?a.map(f).sort((a,b)=>p(a)-p(b),p=a=>a.map?a.map(a=>t+=a.map?0:a,t=0)|t:a):a

All that array checking :-(

Neil

Posted 2016-03-22T00:35:54.717

Reputation: 95 035

1map.map.map.map.map.map.map.map.map – cat – 2016-03-22T22:30:54.257