Indentation-based Sort
Indentation-based Sort
Given an ordered list of same-case letter strings (a-z XOR A-Z) where each string is preceded by 0 or more space ( ) characters, output the same list but with the strings sorted at each level of indentation. Indentation depths under different parents count as distinct lists for sorting purposes.
If your input is:
bdellium
fox
hound
alien
aisle
wasabi
elf
alien
horseradish
xeno
irk
wren
tsunami
djinn
zebra
your output should be
aisle
horseradish
xeno
wasabi
alien
elf
bdellium
alien
fox
hound
djinn
zebra
irk
tsunami
wren
If you like, think of it like a directory listing, and you need to sort the names within each directory.
This is a code-golf, so the answer which uses the fewest bytes wins.
Btw, for next time, consider using the Sandbox to iron out issues with a challenge before posting it to the main site.
– Adám
Aug 21 at 22:34
@Adám No, the recursive string parsing logic required is the reason I wrote this prompt.
– Techrocket9
Aug 21 at 22:43
If the input is
['a','..b', '.c', '..d']
, what should the output be? ['a','..b', '.c', '..d']
or ['a','.c','..b', '..d']
or some other thing? (I'm using '.'
instead of space for visual clarity).– Chas Brown
Aug 22 at 0:03
['a','..b', '.c', '..d']
['a','..b', '.c', '..d']
['a','.c','..b', '..d']
'.'
@streetster strings (a-z XOR A-Z)
– Adám
Aug 22 at 21:12
9 Answers
9
Pyth, 23 bytes
jeMtSuaG+f</Td/HdeGHQ]Y
Try it here!
Python 2, 117 bytes
lambda S:[s[-1]for s in sorted(reduce(lambda t,s:t+[[v for v in t[-1]if v.count(' ')<s.count(' ')]+[s]],S,[]))[1:]]
Try it online!
Takes as input a list of strings; and outputs a list of strings, sorted as required.
The idea is to turn each element into a list which contains the "absolute path" as a list; and then let Python handle the sorting. E.g. if the input is:
[
'a',
' c',
' d',
' b'
]
Then via the reduce()
, we convert to a list of lists:
reduce()
[
['a'],
['a',' c'],
['a',' c', ' d'],
['a',' b']
]
which gets sorted as:
[
['a'],
['a',' b']
['a',' c'],
['a',' c', ' d'],
]
and then output the last element of each list in the list-of-lists to get:
[
'a',
' b'
' c',
' d',
]
Wow the solution I was about to post was 183 bytes...I suck lol
– Rushabh Mehta
Aug 22 at 0:18
@Rushabh Mehta: My first try was around 205 bytes... then hack away! :)
– Chas Brown
Aug 22 at 0:28
APL (Dyalog Unicode), 31 bytesSBCS
Anonymous prefix lambda, takes and returns list of strings.
⍵[⍋1=≢⍵:⍺⋄⍵⍀↑' '(,⊂⍨⊣=,)¨⍵]
Try it online!
…
"dfn";
⍵
is argument
⍵
⍵[
…]
index the argument with the following indices:
⍵[
]
' '(
…)¨⍵
apply the following tacit function to each string with space as left argument:
' '(
)¨⍵
,
concatenate the space to the string
,
⊣=
Boolean list indicating where the space is equal to each character that
⊣=
,⊂⍨
use that to partition (begin part where true) the concatenation of space and string
,⊂⍨
↑
mix list of lists of strings into matrix of strings
↑
…
⍀
vertical cumulative reduction by this "dfn"; ⍺
and ⍵
are upper and lower args:
⍀
⍺
⍵
≢⍵
the length of the lower string
≢⍵
1=
is that equal to 1? (i.e. is there nothing but the single space there?)
1=
:⍺
if so, return the upper argument
:⍺
⋄⍵
else, return the lower argument
⋄⍵
⍋
grade up (find indices which will sort that)
⍋
Retina, 47 bytes
+-1m`(?<=^2(S+).*?¶( *))
$1
O$`
$L$&
S+
Try it online! Note: Several lines have trailing spaces. Explanation:
+-1m`(?<=^2(S+).*?¶( *))
$1
The first step is to insert each word into following lines at the same indentation. For example, with the lines aisle
, wasabi
and elf
the resulting lines are aisle
, aisle wasabi
and aisle wasabi elf
. I discovered this regex by trial and error so there may be edge cases with it.
aisle
wasabi
elf
aisle
aisle wasabi
aisle wasabi elf
O$`
$L$&
We can now sort the lines case-insensitively.
S+
Delete all of the inserted words.
Perl 6, 120 83 81 63 54 37 47 42 bytes
-5 bytes thanks to nwellnhof
my@a;.sort:@a[+.comb(' ')..*+1]=$_;~@a
Try it online!
This uses Chas Brown's method. An anonymous code block that takes a list of lines and returns a list of lines.
# Anonymous code block
my@a; # Declare a local list
.sort # Sort the given list of lines
: # By converting each line to:
@a[+.comb(' ')..*+1]=$_; # Set the element at that indentation level onwards to that line
~@a # And return the list coerced to a string
@nwellnhof Thanks for pointing that out. I think I've fixed it in the latest version
– Jo King
Aug 22 at 9:57
@nwellnhof Ah well, it was shorter in a previous iteration. Thanks for the bytes off, but I had to change it slightly
– Jo King
Aug 22 at 11:01
Oh, right. Actually, something like
my@a;.sort:@a[+.comb(' ')...*>@a]=$_;~@a
is required to support higher indentation levels.– nwellnhof
Aug 22 at 12:02
my@a;.sort:@a[+.comb(' ')...*>@a]=$_;~@a
Clean, 112 101 bytes
import StdEnv
f=flatten
?e=[0\' '<-e]
$[h:t]#(a,b)=span(u= ?u> ?h)t
=sort[[h:f($a)]: $b]
$e=
f o$
Try it online!
Anonymous function :: [[Char]] -> [[Char]]
which wraps $ :: [[Char]] -> [[[Char]]]
into the right output format. $
groups the strings into "more spaces than" and "everything else afterwards", recurses over each group and sorts when they're adjoined. At each step, the list being sorted looks like:
:: [[Char]] -> [[Char]]
$ :: [[Char]] -> [[[Char]]]
$
[[head-of-group-1,child-1,child-2..],[head-of-group-2,child-1,child-2..]..]
Clean, 127 bytes
import StdEnv
$l=[x++y\z<- ?(map(span((>)'!'))l),(x,y)<-z]
?[h:t]#(a,b)=span((u,_)=u>fst h)t
=sort[[h:flatten(?a)]: ?b]
?e=
Try it online!
Defines the function $ :: [[Char]] -> [[Char]]
which separates the strings into tuples in the form (spaces, letters)
which are recursively sorted by the helper function ? :: [([Char],[Char])] -> [[([Char],[Char])]]
.
$ :: [[Char]] -> [[Char]]
(spaces, letters)
? :: [([Char],[Char])] -> [[([Char],[Char])]]
Explained:
$ list // the function $ of the list
= [ // is
spaces ++ letters // the spaces joined with the letters
\ sublist <- ? ( // from each sublist in the application of ? to
map ( // the application of
span ((>)'!') // a function separating spaces and letters
) list // to every element in the list
)
, (spaces, letters) <- sublist // the spaces and letters from the sublist
]
? [head: tail] // in the function ? of the head and tail of the input
# (group, others) // let the current group and the others equal
= span ( // the result of separating until ... is false
(u, _) = u > // only elements where there are more spaces
fst head // than in the head of the input
) tail // the tail of the input
= sort [
[head // prepend the head of the input to
: flatten (?group) // the flat application of ? to the first group
] // and prepend this to
: ?others // the application of ? to the other group(s)
]
? empty = // match the empty list
JavaScript (Node.js), 114 100 92 88 bytes
x=>x.map(y=>a=a.split(/ */.exec(y)[0]||a)[0]+y,a="_").sort().map(x=>/ *w+$/.exec(x)[0])
Try it online!
Similar approach to Chas Brown's Python answer, but using regular expressions instead.
x => x.map( //
y => a = a.split( // Renders the indentation paths
/ */.exec(y)[0] // Checks the indentation level
|| a // If this is the top level, go to root
)[0] + y, // Appends the child to the parent
a = "_" // At first the cursor is at the root
) //
.sort() // Sorts the indentation paths
.map( //
x => / *w+$/.exec(x)[0] // Extracts only the last level of the path
) //
K4, 51 bytes
Solution:
,/(1#'r),'.z.s@'1_'r:(w_x)@<x@w:&s=&/s:+/'" "=/:x
Example:
q)k),/(1#'r),'.z.s@'1_'r:(w_x)@<x@w:&s=&/s:+/'" "=/:x("bdellium";" fox";" hound";" alien";"aisle";" wasabi";" elf";" alien";" horseradish";" xeno";"irk";"wren";"tsunami";"djinn";" zebra")
"aisle"
" horseradish"
" xeno"
" wasabi"
" alien"
" elf"
"bdellium"
" alien"
" fox"
" hound"
"djinn"
" zebra"
"irk"
"tsunami"
"wren"
Assumptions:
a. That each hierarchy will start with the lowest level, i.e. you will not get:
bdellium
fox
hound
alien
Explanation:
,/(1#'r),'.z.s@'1_'r:(w_x)@<x@w:&s=&/s:+/'" "=/:x / the solution
/ lambda function, implicit x
" "=/:x / " " equal to each right (/:) x
+/' / sum up each
s: / save as s
&/ / find the minimum (ie highest level)
s= / is s equal to the minimum?
& / indices where true
w: / save as w
x@ / index into x at these indices
< / return indices to sort ascending
@ / index into
( ) / do this together
w_x / cut x at indices w
r: / save as r
1_' / drop first from each r
.z.s@ / apply recurse (.z.s)
,' / join each both
( ) / do this together
1#'r / take first from each r
,/ / flatten
Perl 5, 166 bytes
sub fmy$p=shift;my@r;while(@i)$i[0]=~/S/;$c=$-[0];if($p<$c)$r[-1].=$_ for f($c)elsif($p>$c)lastelsepush@r,shift@isort@rpush@i,$_ while<>;print sort@[f 0]
Ungolfed (sort of):
sub f
my $p = shift;
my @r;
while(@i)
$i[0] =~ /S/;
$c = $-[0];
if($p < $c)
$r[-1] .= $_ for f($c)
elsif ($p > $c)
last
else
push @r, shift @i
sort @r
push @i, $_ while <>;
print sort@[f 0]
It's a pretty straightforward recursive implementation. We check the indentation level by searching for the first non-space character (/S/
) and getting its index ($-[0]
). Unfortunately, we actually have to declare a handful of variables that are used in the recursion, or else they'll be implicitly global and the recursion won't work correctly.
/S/
$-[0]
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Nice challenge!
– Adám
Aug 21 at 22:22