Reverse indentation

64

4

I've heard that your code can run faster if you indent it in reverse, so that the compiler can process it like a tree design pattern from the very top of the "branches" down. This helps because gravity will speed up the time it takes for your code to be compiled, and the data structure efficiency is improved. Here's an example, in Java scripting:

            function fib(n) {
        var a = 1, b = 1;
        while (--n > 0) {
    var tmp = a;
    a = b;
    b += tmp;
    if (a === Infinity) {
return "Error!";
    }
        }
        return a;
            }

But for some reason Notepad doesn't have a setting to do this automatically, so I need a program to do it for me.

Description

Submissions must take a code snippet as input, reverse the indentation, and output the resulting code.

This is done by the following procedure:

  • Split the code up into lines. Each line will start with zero or more spaces (there will be no tabs).

  • Find all unique indentation levels in the code. For example, for the above example, this would be

    0
    4
    8
    12
    
  • Reverse the order of this list of indentation levels, and map the reversed list to the original list. This is hard to explain in words, but for the example, it would look like

    0  — 12
    4  — 8
    8  — 4
    12 — 0
    
  • Apply this mapping to the original code. In the example, a line with 0-space-indentation would become indented by 12 spaces, 4 spaces would become 8 spaces, etc.

Input / Output

The input and output can be provided however you would like (STDIN/STDOUT, function parameter/return value, etc.); if your language does not support multiline input (or you just don't want to), you can use the | character to separate lines instead.

The input will consist of only printable ASCII + newlines, and it will not contain empty lines.

Test cases

Input:

function fib(n) {
    var a = 1, b = 1;
        while (--n > 0) {
            var tmp = a;
            a = b;
            b += tmp;
            if (a === Infinity) {
                return "Error!";
            }
        }
    return a;
}

Output: the example code above.

Input:

a
  b
  c
d
   e
        f
  g
   h

Output:

        a
   b
   c
        d
  e
f
   g
  h

Input:

1
 2
  3
 2
1

Output:

  1
 2
3
 2
  1

Input:

  foo

Output:

  foo

Doorknob

Posted 2014-12-18T21:36:12.500

Reputation: 68 138

21Its "JavaScript" not "Java scripting" :/ – Optimizer – 2014-12-18T21:36:59.977

75@Optimizer I see that my goal of infuriating as many people as possible with the first two paragraphs has been achieved. ;) – Doorknob – 2014-12-18T21:42:13.040

71 != as many people as possible. – Optimizer – 2014-12-18T21:44:50.380

WAT. How could anyone possibly think this is a good idea? – John Dvorak – 2014-12-18T21:49:54.363

23@JanDvorak The same guys that invented MLA style citations think this is a good idea. – Rainbolt – 2014-12-18T21:53:33.153

1

Are these the same guys who made the comefrom statement?

– FryAmTheEggman – 2014-12-18T22:03:06.830

Are we allowed to append a single newline? – Martin Ender – 2014-12-18T23:15:33.270

6Supposedly, it's faster. Let's assign a committee to it and wait a few years while we forget the purpose of it. – Conor O'Brien – 2014-12-18T23:19:16.693

1This explains why Python is so slow... – MikeTheLiar – 2014-12-19T14:26:08.953

4I literally laughed out loud at "for some reason, Notepad doesn't do this automatically" – Greg d'Eon – 2014-12-19T19:30:24.837

Before anything, this made me cringe like hell. – Joe Z. – 2015-04-14T23:09:48.980

2Gravity pulls sideways? coooooool – Rɪᴋᴇʀ – 2016-04-02T00:03:40.200

1This is almost as bad as using spaces for indenting – 12Me21 – 2017-02-18T22:58:33.370

This is infuriating...good job – SomeShinyObject – 2017-06-07T03:57:21.710

Answers

10

CJam, 43 39 36 35 bytes

qN/_{_Sm0=#}%___&$_W%er]z{~S*@+>N}%

This looks toooo long. I am sure I am not Optimizing enough!

How it works:

Basic idea is to split the input on newline, calculate the number of leading spaces in each line, sort and get unique numbers, copy that array and reverse the copy, transliterate the original in-order numbers with this two arrays and then finally form the final string using this information.

The lengthiest part is to figure out how many leading spaces are there in each line as CJam does not have an easy way of doing it.

Code expansion:

qN/_                                      "Split the string on newline and take copy";
    {_Sm0=#}%                             "Map this code block on the copy";
     _Sm                                  "Copy the string and remove spaces from the copy";
        0=                                "Get first non space character";
          #                               "Gets its index in original string";
             ___                          "Get 3 copies of the above array";
                &$_W%                     "Get unique elements, sort, copy and reverse";
                     er                   "Transliterate unique sorted elements with";
                                          "the unique reverse sorted in the copy";
                       ]z                 "Get array of [row,
                                          " original number of leading spaces,
                                          " required number of leading spaces]";
                         {~S*@+>N}%       "For each above combination";
                          ~S*             " unwrap and get leading space string";
                             @+           " prepend to the row";
                               >          " remove original spaces";
                                N         " put newline";

And in the spirit of the question. A real expansion of the code:

                                          qN/_                                      "Split the string on newline and take copy";
                                {_Sm0=#}%                             "Map this code block on the copy";
                               _Sm                                  "Copy the string and remove spaces from the copy";
                             0=                                "Get first non space character";
                          #                               "Gets its index in original string";
                         ___                          "Get 3 copies of the above array";
                       &$_W%                     "Get unique elements, sort, copy and reverse";
                     er                   "Transliterate unique sorted elements with";
"the unique reverse sorted in the copy";
                ]z                 "Get array of [row,
" original number of leading spaces,
" required number of leading spaces]";
             {~S*@+>N}%       "For each above combination";
          ~S*             " unwrap and get leading space string";
        @+           " prepend to the row";
     >          " remove original spaces";
    N         " put newline";

7 bytes saved thanks to Martin and 1 byte thanks to Dennis

Try it online here

Optimizer

Posted 2014-12-18T21:36:12.500

Reputation: 25 836

>

  • {}# has a bug: it returns an Integer, but it should return a Long. Ironically, i (cast to integer) fixes this. 2. Since ""# doesn't have the same bug, _Sm0=# is one byte shorter.
  • < – Dennis – 2014-12-19T03:29:10.743

    @Dennis Yeah, the bug is weird. Thanks for the workaround ! – Optimizer – 2014-12-19T07:01:29.930

    2this indentation in the expansion is so easy to read! You should reverse it! – DLeh – 2014-12-19T21:08:42.957

    13

    Python 2 - 137 131 bytes

    i=raw_input().split('|')
    f=lambda s:len(s)-len(s.lstrip())
    d=sorted(set(map(f,i)))
    for l in i:print' '*d[~d.index(f(l))]+l.lstrip()
    

    Takes input with | instead of \n.

    Explanation

    The first three lines are fairly straightforward. Make a list of all the lines in the input, define a function that tells you how much leading whitespace a string has, and make a sorted list of values that function spits out for each line of input.

    The last line is way more fun.

                                     l               # string with the line
                                   f(l)              # amount of leading whitespace
                           d.index(f(l))             # where it is in list of whitespace amounts
                          ~d.index(f(l))             # bitwise NOT (~n == -(n+1))
                        d[~d.index(f(l))]            # index into the list (negative = from end)
               print' '*d[~d.index(f(l))]            # print that many spaces...
               print' '*d[~d.index(f(l))]+l.lstrip() # plus everything after leading whitespace
    for l in i:print' '*d[~d.index(f(l))]+l.lstrip() # do the above for every line
    

    undergroundmonorail

    Posted 2014-12-18T21:36:12.500

    Reputation: 5 897

    Confirmed 137 :) – FryAmTheEggman – 2014-12-18T21:59:27.923

    @frya thanks :) – undergroundmonorail – 2014-12-18T22:06:41.107

    1This all seems fine in python 3 which should save you 2 bytes (pay 2 for () save 4 for raw_) – FryAmTheEggman – 2014-12-18T23:08:32.347

    1f(s)for s in i should be map(f,i). – feersum – 2014-12-18T23:14:19.263

    1A piece of magic: d=[];d+=set(L) is a shorter version of d=sorted(set(L)). – xnor – 2014-12-19T06:53:05.773

    @fry sure, but I like python2 better ;) – undergroundmonorail – 2014-12-19T15:14:20.997

    @fee not sure how i missed that, i use map all the time! thanks :) – undergroundmonorail – 2014-12-19T15:14:39.120

    @xno yeah, i was informed in chat and had't had a chance to play with it yet. thanks :) – undergroundmonorail – 2014-12-19T15:15:10.033

    @xnor I suggested that, thinking it was a good idea. But then I tried with larger and more numbers and found out that it doesn't actually work... – Sp3000 – 2014-12-19T15:34:07.890

    I have a suspicion that it fails if the largest element is larger than the number of elements there are, e.g. x=[];x+=set(random.randint(0, 10000) for i in range(10)) – Sp3000 – 2014-12-19T15:41:15.497

    Now make your "correctly" indented code run! – Ben – 2014-12-19T17:11:48.453

    7

    JavaScript, ES6, 113 103 101 bytes

    I am pretty sure this can be golfed at least a little further, but here goes.

    Never would have thought that there will be a 101 bytes JS solution, beating Python!

    f=a=>(b=c=[...Set(a.match(r=/^ */gm).sort())],c.map((x,i)=>b[x]=c.slice(~i)[0]),a.replace(r,x=>b[x]))
    

    This creates a method named f which can be called with the input string. If you are in a latest Firefox, you have template strings and you can call the method like

    f(`a
      b
      c
    d
       e
            f
      g
       h`)
    

    Otherwise, you can also call it like

    f("a\n\
      b\n\
      c\n\
    d\n\
       e\n\
            f\n\
      g\n\
       h")
    

    or, try the snippet below:

    g=_=>O.textContent=f(D.value)
    
    f=a=>(b=c=[...Set(a.match(r=/^ */gm).sort())],c.map((x,i)=>b[x]=c.slice(~i)[0]),a.replace(r,x=>b[x]))
    <textarea id=D></textarea><button id=B onclick=g()>Inverse!</button>
    <pre id=O></pre>

    Optimizer

    Posted 2014-12-18T21:36:12.500

    Reputation: 25 836

    You can save a coupes bytes by storing the regex as a variable, as it is used twice (you should be able to replace \s with a space character), and removing the parentheses around x in the replace function. – NinjaBearMonkey – 2014-12-19T01:57:08.647

    @hsl gee, thanks! I don't even know why I wrote (x) :/ – Optimizer – 2014-12-19T07:06:06.897

    You don't need both b and c do you? They just refer to the same array anyway. – Neil – 2016-05-11T15:00:15.663

    5

    Ruby, 63 bytes

    ->s{l=s.scan(r=/^ */).uniq.sort;s.gsub r,l.zip(l.reverse).to_h}
    

    This defines an unnamed function which takes and returns a string. You can call it by appending ["string here"] or by assigning it to a variable, and then calling that variable.

    How it works: s.scan(r=/^ */) gives a list of all leading spaces and stores that regex in r for later use. uniq eliminates duplicates. sort... sorts.

    Now skip to the end, l.zip(l.reverse) gives an array of pairs we want to substitute. to_h turns that into a hash, interpreting the pairs as key-value pairs.

    Now s.gsub replaced all matches of the regex (all leading spaces) by using that hash as a look up table to find the replacement.

    Martin Ender

    Posted 2014-12-18T21:36:12.500

    Reputation: 184 808

    3

    Pyth 39

    L-/bd/rb6dJcz\|KS{mydJFNJ+*d@_KxKyN>NyN
    

    Try it online.

    Uses the | delimiter option.

    FryAmTheEggman

    Posted 2014-12-18T21:36:12.500

    Reputation: 16 206

    2

    Japt -R, 27 bytes

    ·
    mâ\S
    Vâ n
    Ëx2 iSpWg~WbVgE
    

    Try it online!

    Unpacked & How it works

    Input: U = multiline string
    
    qR    Split by newline and implicit assign to U
    
    mâ\S
    m     Map over U...
     â\S    .search(/\S/); first index of non-whitespace char
          Implicit assign to V (V = array of indentations)
    
    Vâ n  Take unique elements of V, sort, and implicit assign to W
    
    mDEF{Dx2 iSpWg~WbVgE
    mDEF{                 Map over U...
         Dx2                Trim left
             iSp            Indent by this many spaces...
                     VgE      Find the current indentation stored in V
                   Wb         Find its index on W
                Wg~           Take the opposite element on W
    
    -R    Join with newline
    

    How it really works

                     Input: U = multiline string
    
                     qR    Split by newline and implicit assign to U
    
                     mâ\S
                     m     Map over U...
                   â\S    .search(/\S/); first index of non-whitespace char
             Implicit assign to V (V = array of indentations)
    
                     Vâ n  Take unique elements of V, sort, and implicit assign to W
    
                     mDEF{Dx2 iSpWg~WbVgE
                     mDEF{                 Map over U...
                Dx2                Trim left
          iSp            Indent by this many spaces...
    VgE      Find the current indentation stored in V
     Wb         Find its index on W
         Wg~           Take the opposite element on W
    
                     -R    Join with newline
    

    Bubbler

    Posted 2014-12-18T21:36:12.500

    Reputation: 16 616

    1

    PowerShell, 112 bytes

    $x=@($args|sls '(?m)^ *'-a|% m*|% v*|sort -u)
    [regex]::Replace($args,'(?m)^ *',{$x[-1-$x.IndexOf($args.Value)]})
    

    Try it online!

    Less golfed:

    $xIdents=@($args|select-string '(?m)^ *'-AllMatches|% matches|% value|sort -unique) # get a sorted set of indentations
    [regex]::Replace($args,'(?m)^ *',{$xIdents[-1-$xIdents.IndexOf($args.Value)]})    # replace each indentation with opposite one
    

    mazzy

    Posted 2014-12-18T21:36:12.500

    Reputation: 4 832

    1

    Scala, 176171

    def g(n:String)={val a=n.split('|').map(a=>a.prefixLength(' '==)->a)
    (""/:a){case(s,(l,p))=>val b=a.unzip._1.distinct.sorted
    s+" "*b.reverse(b.indexOf(l))+p.drop(l)+'\n'}}
    

    It will add an extra newline at the end. If I did not have to preserve spaces at the end of the line, I can get it to 167:

    def t(n:String)={val a=n.split('|').map(a=>a.prefixLength(' '==)->a.trim)
    (""/:a){(s,l)=>val b=a.unzip._1.distinct.sorted
    s+" "*b.reverse(b.indexOf(l._1))+l._2+'\n'}}
    

    Ungolfed:

          def reverseIndent(inString: String): String = {
        val lines = inString.split('\n')
        val linesByPrefixLength = lines.map { line =>
      line.prefixLength(char => char == ' ') -> line
        }
        val distinctSortedPrefixLengths = linesByPrefixLength.map(_._1).distinct.sorted
        val reversedPrefixes = distinctSortedPrefixLengths.reverse
        linesByPrefixLength.foldLeft("") { case (string, (prefixLength, line)) =>
      val newPrefixLength = reversedPrefixes(distinctSortedPrefixLengths.indexOf(prefixLength))
      val nextLinePrefix = " " * newPrefixLength
      string + nextLinePrefix + line.substring(prefixLength) + '\n'
        }
          }
    

    Chad Retz

    Posted 2014-12-18T21:36:12.500

    Reputation: 131

    0

    JavaScript, 351

    var i=0;var a=$("#i").html().split("\n");var b=[];for(;i<a.length;i++){j=a[i].match(/\s*/)[0];if(b.indexOf(j)<0){b.push(j);}}b.sort(function(a,b){return a - b;});var c=b.slice().reverse();var d="";for(i=0;i<a.length;i++){d+=a[i].replace(/\s*/,c[b.indexOf(a[i].match(/\s*/)[0])])+"\n";j=a[i].search(/\S/);if(b.indexOf(j)<0){b.push(j);}}$("#i").html(d);
    

    Ungolfed version:

    var i = 0;
    var a = $("#i").html().split("\n");
    var b = [];
    for (; i < a.length; i++) {
      j = a[i].match(/\s*/)[0];
      if (b.indexOf(j) < 0) {
        b.push(j);
      }
    }
    b.sort(function(a, b) {
      return a - b;
    });
    var c = b.slice().reverse();
    var d = "";
    for (i = 0; i < a.length; i++) {
      d += a[i].replace(/\s*/, c[b.indexOf(a[i].match(/\s*/)[0])]) + "\n";
      j = a[i].search(/\S/);
      if (b.indexOf(j) < 0) {
        b.push(j);
      }
    }
    $("#i").html(d);
    

    Testing

    var i=0;var a=$("#i").html().split("\n");var b=[];for(;i<a.length;i++){j=a[i].match(/\s*/)[0];if(b.indexOf(j)<0){b.push(j);}}b.sort(function(a,b){return a - b;});var c=b.slice().reverse();var d="";for(i=0;i<a.length;i++){d+=a[i].replace(/\s*/,c[b.indexOf(a[i].match(/\s*/)[0])])+"\n";j=a[i].search(/\S/);if(b.indexOf(j)<0){b.push(j);}}$("#i").html(d);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <pre><code id="i">
    function fib(n) {
        var a = 1, b = 1;
            while (--n > 0) {
                var tmp = a;
                a = b;
                b += tmp;
                if (a === Infinity) {
                    return "Error!";
                }
            }
        return a;
    }
    </code></pre>

    var i=0;var a=$("#i").html().split("\n");var b=[];for(;i<a.length;i++){j=a[i].match(/\s*/)[0];if(b.indexOf(j)<0){b.push(j);}}b.sort(function(a,b){return a - b;});var c=b.slice().reverse();var d="";for(i=0;i<a.length;i++){d+=a[i].replace(/\s*/,c[b.indexOf(a[i].match(/\s*/)[0])])+"\n";j=a[i].search(/\S/);if(b.indexOf(j)<0){b.push(j);}}$("#i").html(d);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <pre><code id="i">
    a
      b
      c
    d
       e
            f
      g
       h
    </code></pre>

    var i=0;var a=$("#i").html().split("\n");var b=[];for(;i<a.length;i++){j=a[i].match(/\s*/)[0];if(b.indexOf(j)<0){b.push(j);}}b.sort(function(a,b){return a - b;});var c=b.slice().reverse();var d="";for(i=0;i<a.length;i++){d+=a[i].replace(/\s*/,c[b.indexOf(a[i].match(/\s*/)[0])])+"\n";j=a[i].search(/\S/);if(b.indexOf(j)<0){b.push(j);}}$("#i").html(d);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <pre><code id="i">
    1
     2
      3
     2
    1
    </code></pre>

    var i=0;var a=$("#i").html().split("\n");var b=[];for(;i<a.length;i++){j=a[i].match(/\s*/)[0];if(b.indexOf(j)<0){b.push(j);}}b.sort(function(a,b){return a - b;});var c=b.slice().reverse();var d="";for(i=0;i<a.length;i++){d+=a[i].replace(/\s*/,c[b.indexOf(a[i].match(/\s*/)[0])])+"\n";j=a[i].search(/\S/);if(b.indexOf(j)<0){b.push(j);}}$("#i").html(d);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <pre><code id="i">
      foo
    </code></pre>

    The Guy with The Hat

    Posted 2014-12-18T21:36:12.500

    Reputation: 778

    0

    Haskell, 116

    import Data.List
    f s|l<-map(span(==' '))$lines s=unlines[k++b|(a,b)<-l,(k,r)<-reverse>>=zip$sort$nub$map fst l,r==a]
    

    proud haskeller

    Posted 2014-12-18T21:36:12.500

    Reputation: 5 866

    0

    PHP - 173 bytes

    The unoptimized code should be stored in the $v variable:

    <?php $f='preg_replace';$f($p='#^ *#me','$i[]='.$s='strlen("$0")',$v);$a=$b=array_unique($i);sort($a);rsort($b);echo$f($p,'str_repeat(" ",array_combine($a,$b)['.$s.'])',$v);
    

    Here is the ungolfed and commented version:

    <?php
    // Get the level of indentation for each line
    $preg_replace = 'preg_replace';
    $pattern = '#^ *#me';
    $strlen = 'strlen("$0")';
    $preg_replace($pattern, '$indentationLevelsOldList[] = '. $strlen, $value);
    
    // Create an array associating the old level of indentation with the new expected one
    $sortedArray = array_unique($indentationLevelsOldList);
    $reverseSortedArray = $sortedArray;
    
    sort($sortedArray);
    rsort($reverseSortedArray);
    
    $indentationLevelsNewList = array_combine($sortedArray, $reverseSortedArray);
    
    // Print the correctly indented code
    echo $preg_replace($pattern, 'str_repeat(" ", $indentationLevelsNewList['. $strlen .'])', $value);
    

    I've probably never written something so dirty. I'm ashamed.

    Blackhole

    Posted 2014-12-18T21:36:12.500

    Reputation: 2 362

    0

    Perl 5, 112

    111 + 1 for -n (-E is free)

    @{$.[$.]}=/( *)(.*)/;++$_{$1}}{map$_{$_[$#_-$_]}=$_[$_],0..(@_=sort keys%_);say$_{$.[$_][0]}.$.[$_][1]for 0..$.
    

    I'm sure it can be done in fewer strokes, but I don't see how at the moment.

    msh210

    Posted 2014-12-18T21:36:12.500

    Reputation: 3 094