3
\$\begingroup\$

Specifically, this references JavaScript, a programming language that happens to be one you can run on StackExchange/StackOverflow. However, you may use any programming language to do this challenge. This is not actual JS golfing, but merely a "code-golfable" challenge. Actual complete parsing of JS is not needed.

An example of JS looks like this (try running all the snippets in here):

const constant = 1
var value = 2
for (let i = 0; i < 21; i++) {
    value += constant + value // Addition
}
// Print the value
document.write(value)

Problem is, I want my code size to be a bit smaller. So, I could rename the variables to make it look like this:

var a = 1
var b = 2
for (let i = 0; i < 21; i++) {
    b += a + b // Addition
}
// Print the value
document.write(b)

Notice how the variables have been changed to one letter, and the const keyword has been shortened to var. Then, we can remove most spaces and all comments:

var a=1
var b=2
for (let i = 0; i < 21; i++) {
b+=a+b
}
document.write(b)
(Again, this is not "complete" compression, and is just a challenge.)

So, here are the rules (look at the test case; it will make more sense):

  • The keywords const , let , var , while, and for are not variables (notice the spaces!) and should not be changed, with the exception of const , which should be changed to var for shortening.
  • A variable is defined as the longest string of characters that contain only the characters 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$, not including the rule above. Furthermore, a variable is a variable if and only if one variable is preceded with const , let , or var (with spaces).
  • All variables with the same name must be renamed into a single character variable (one of abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$). All variables with different names must have different values in the result. You may assume that there are no more than 50 unique variables, so only one character is required.
  • A single-line comment is all the characters at or after //
  • All single-line comments should be removed.
  • All spaces that are not within keywords must be removed. (Not newlines!)
  • After all of this, remove any empty lines. This includes comment removal.
  • Compact numbers! If a number is an integer, it is defined a number, but with a . and all zeros at the end.
  • If an integer has more than two zeros, write the number before the final zeros (before the dot), an e, then the number of zeros (30000 to 3e4, 1515000 to 1515e3). This is optional for two zeros. While this is not complete, it would be too complicated to compact numbers well.
  • You should output all other characters normally.

There is only one test case, but it is a strange one!

Test case:

// Hello world, but stranger!

   // funny spacing

const m = 72000.00 / 1000
var h = String.fromCharCode(m)
  let string = " world!" // Value to print
 
 var l
for (let i = 1.00000; i < string.length; i++) {
// Number prototype to throw off some lazy variable detection
Number
      console.log(string[i]) // Print each value
   l += string[i]
}
string = string.split("").join("<br>")
 h = l.toUpperCase()

document.write("<!DOCTYPE html><i>" + h.toUpperCase() + "    " + string + "</i>")

Result:

var m=72e3/1e3
var h=String.fromCharCode(m)
let s=" world!"
var l
for(let i=1;i<s.length;i++){
Number
console.log(s[i])
l+=s[i]
}
s=s.split("").join("<br>")
h=l.toUpperCase()
document.write("<!DOCTYPE html><i>"+h.toUpperCase()+"    "+s+"</i>")

Notice Number! This is , so the shortest answer in bytes wins.

\$\endgroup\$
12
  • \$\begingroup\$ Surely the single character variable shouldn't be one of 0123456789? And would it be a valid assumption that all long variable names don't start with a digit? \$\endgroup\$
    – jezza_99
    Feb 23, 2023 at 20:03
  • 2
    \$\begingroup\$ So should const change to var or not? (first rule says no, first example says yes) \$\endgroup\$ Feb 23, 2023 at 20:17
  • 2
    \$\begingroup\$ Why isn't for (let i = 0; i < 21; i++) { in the final iteration of the example for(let i=0;i<21;i++){ (spaces removed, except after let )? \$\endgroup\$ Feb 23, 2023 at 21:17
  • 3
    \$\begingroup\$ doesn't this question essentially require you to parse the entire JS grammar? Seems a bit broad \$\endgroup\$
    – Sisyphus
    Feb 23, 2023 at 23:36
  • 1
    \$\begingroup\$ @Infigon Oh, this isn't a duplicate, and is in fact quite an interesting and different challenge. It's just related, thought I'd send the link if anyone was interested in a similar challenge \$\endgroup\$
    – noodle man
    Feb 24, 2023 at 22:17

2 Answers 2

7
\$\begingroup\$

Python 3, 501 483 488 492 bytes

import re
def G(m):b=m.group;return b(1)+f'e{H(b(2))}'+b(3)
E='\n'
I='\\g<1>'
H=len
C='abcdefghijklmnopqrstuvwxyz'
C=[*C,*C.upper(),*'_$']
def S(A):
 s=lambda p,r='':re.sub('(?m)'+p,r,A);A=s('const ','var ');V=re.findall('(?m)(?:let|var) (\w+)\s',A);V.sort(key=H)
 for D in V:A=s(D,C.pop())if~-H(D)else C.remove(D)or A
 A=s('//.*| *$');A=s('^ *');A=s(' ?([=+*\\-\(\)<>/]+) ?',I);A=s(r'(?:\.0+)([^\d])',I);A=s('; +',';');F=A=s(r'(\d)(0+)([^\d])',G);A=s(E*2,E)
 while F!=A:F=A;A=s(E*2,E)
 print(A.strip())

Try it online!

I somehow managed to half the number of bytes from when I first started shortening... never done that before
Anyway, I used a lot of regex to replace the strings properly. To test, just make the variable A contain the javascript code. Then, at the end, print A.

Changes:
-4 bytes by using flag in pattern instead thanks to @Unmitigated
-5 bytes by inlining the for loop thanks to @Julian
+11 bytes to use standard I/O methods. Made this so much uglier and some fixes

\$\endgroup\$
7
  • 1
    \$\begingroup\$ It's shorter to set the multiline flag in the string itself: re.sub('(?m)'+p,r,A) \$\endgroup\$ Feb 24, 2023 at 0:21
  • 1
    \$\begingroup\$ you can also inline the "for in if else" for D in V:C.remove(D)if H(D)<2else s(D,C.pop())? \$\endgroup\$
    – Julian
    Feb 24, 2023 at 1:58
  • \$\begingroup\$ Wow, I don't even know half of these Python characters! But I thought a high-level answer would use a lot of regexes ;) \$\endgroup\$
    – Infigon
    Feb 24, 2023 at 2:59
  • 1
    \$\begingroup\$ Sorry about the late catch @12944querty, but could you add something to change for into for and while into while? Accidental miscommunication on my part. \$\endgroup\$
    – Infigon
    Feb 24, 2023 at 13:51
  • 1
    \$\begingroup\$ @YuriGinsburg Fixed, thanks \$\endgroup\$ Feb 27, 2023 at 13:50
5
\$\begingroup\$

05AB1E, 137 134 bytes

¶¡ε„//¡нðÚð¡εD'¬¿Qi'¢Ž}.γžj'$«©så}}˜Ćü2εнð«D„¢Ž†³#ð«Qàiyθˆë¨}D¯ÙskDdi®èë\]JõK».γ'.Qyd~}εDdiÐïQiï}D'.å≠iγ`D¨_ig'es}J]JDI‚ε'"¡ιθ€…"ÿ"}`:

I/O as a multi-line string.

Some minor notes:

  1. The space at for ( is removed as well, unlike the test cases.
  2. Variables that are already a single letter (e.g. i) are still counted as any other variable, and therefore replaced with another letter in alphabetical order.
  3. Assumes the input JavaScript snippet will never start with a double quote ".

Try it online

Explanation:

¶¡                         # Split the (implicit) multi-line input-string on newlines
  ε                        # Map over each line:
   „//¡                    #  Split it on "//"
       н                   #  Only keep the first value of each list
        ðÚ                 #  Trim any leading/trailing spaces
   ð¡                      #  Then split it by spaces
                           #  (note: builtin `#` fails for lines that are lacking spaces)
     ε                     #  Map over each space-splitted substring:
      D                    #   Duplicate the current substring
       '¬¿Qi   }          '#   If it's equal to dictionary string "const"
            '¢Ž           '#    Push dictionary string "var" instead
      .γ        }          #   Then adjacent group-by the characters of the substring:
        žj                 #    Push builtin string "abc...xyzABC...XYZ012...789_"
          '$«             '#    Append "$"
             ©             #    Store this in variable `®` (without popping)
              s            #    Swap so the current character is at the top
               å           #    Check if this character is in this string
     }                     #  Close the inner map
   ˜                       #  Flatten it to a single list of strings
    Ć                      #  Enclose; append its own head (which we'll discard later)
     ü2                    #  Pop and push its overlapping pairs
       ε                   #  Map over each overlapping pair:
        н                  #   Pop and push the first item of the pair
         ð«                #   Append a space
           D               #   Duplicate it
        „¢Ž†³              #   Push dictionary string "var let"
             #             #   Split it on the space: ["var","let"]
              ð«           #   Append a space to each: ["var ","let "]
                Qài        #   If either is equal to the earlier pushed string:
                   yθ      #    Push the last item of the pair
                     ˆ     #    Pop and add it to the global array
                  ë        #   Else:
                   ¨       #    Remove the appended space again
                  }        #   Close the if-statement
        D                  #   Duplicate the string
         ¯                 #   Push the global array
          Ù                #   Uniquify it
           s               #   Swap so the copy of the string is at the top
            k              #   Get the index of this string in the global array
                           #   (or -1 if it's not in it)
             D             #   Duplicate this index
              di           #   If it's non-negative:
                ®          #    Push variable `®` ("a-zA-Z0-9_$")
                 è         #    Index the index into this string
               ë           #   Else:
                \          #    Discard the index from the stack
  ]                        # Close the open if-else and nested maps
   J                       # Join each inner list back together to a line
    õK                     # Remove any empty lines
      »                    # Join it back by newlines
.γ                         # Adjacent group-by characters again:
  '.Q                     '#  Check if it's equal to a "."
     yd                    #  Check if it's a digit
       ~                   #  OR to check if either of these two is truthy
 }ε                        # After the adjacent group-by: map over the substrings:
   D                       #  Duplicate the current substring
    di                     #  Pop the copy, and if it's an integer:
      Ð                    #   Triplicate it
       ï                   #   Pop one copy, and cast it to an integer
        Qi                 #   Pop the top two, and if their values are the same:
          ï                #    Cast it to an integer 
         }D                #   After this if, duplicate the result
           '.å≠i          '#   Pop the copy, and if it does NOT contain a ".":
                γ          #    Group to adjacent equal digits
                 `         #    Pop and push the groups to the stack
                  D        #    Duplicate the last one
                   ¨       #    Remove one of its digits
                    _i     #    If it's 0 (including "00"; "000"; etc.):
                      g    #     Pop and push its length
                       'e '#     Push an "e"
                         s #     Swap the length and "e" on the stack
                     }J    #    After the if-statement: join the stack back together
  ]                        # Close the still open if-statements and map
   J                       # Join everything back together
    D                      # Duplicate the string
     I‚                    # Pair the copy with the input-string
       ε                   # Map over this pair of multi-line strings:
        '"¡               '#  Split it on '"'
           ι               #  Uninterleave it into two parts
            θ              #  Pop and keep the last part
             €             #  Map over each string in this list:
              …"ÿ"         #  Surround it with leading/trailing '"'
       }`                  # After the map: push both lists of strings to the stack
         :                 # Replace them, to fix spaces within double-quotes
                           # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to use the dictionary?) to understand why '¬¿ is "const"; '¢Ž is "var"; and „¢Ž†³ is "var let".

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.