51
\$\begingroup\$

Input

A non-empty encoded string consisting of printable ASCII characters (in the range 32-126), where some missing letters have been replaced with _.

Output

A decoded string of the same length with all letters in lowercase, including the missing ones.

How?

Edit: As mentioned by @Deusovi in the comments, this is a variant of Bacon's cipher.

  • Gather all letters in the original string and group them by 5. Additional letters that do not fit in a full group of 5 are ignored.
  • Convert each group into binary: lowercase = 0, uppercase = 1. This leads to a list of integers.
  • Use each value N in this list to replace each _ in the original string with the N-th letter of the alphabet (0-indexed), in order of appearance.

Example: prOGraMMIng PuZZleS & cOde ____

prOGr --> 00110 -->  6 -->  7th letter = 'g'
aMMIn --> 01110 --> 14 --> 15th letter = 'o'
gPuZZ --> 01011 --> 11 --> 12th letter = 'l'
leScO --> 00101 -->  5 -->  6th letter = 'f'

By replacing the missing letters and converting everything back to lowercase, the original string is unveiled:

programming puzzles & code golf

This is the expected output.

Clarifications and rules

  • The missing letters are guaranteed to appear at the end of the string. More formally: there will never be any letter after the first _ in the input string. However, there may be other printable ASCII characters such as spaces and punctuation marks.
  • The input is guaranteed not to contain any useless capital letter: all capital letters are bits set to 1 which are required to decode the missing letters. Everything else is in lowercase.
  • The input string is guaranteed to be valid. Especially:
    • It will always contain enough full groups of 5 letters to decode the underscores.
    • The binary-encoded integers are guaranteed to be in the range [0-25].
  • There may be no _ at all in the input string, in which case you just have to return the input.
  • This is , so the shortest answer in bytes wins!

Test cases

Input : hello!
Output: hello!

Input : helLO, worl_!
Output: hello, world!

Input : i aM yoUr faTh__.
Output: i am your father.

Input : prOGraMMIng PuZZleS & cOde ____
Output: programming puzzles & code golf

Input : Can YOu gUesS tHE ENd oF This ____?
Output: can you guess the end of this text?

Input : THe qUICk brown FOx JUMps oVEr the la__ ___.
Output: the quick brown fox jumps over the lazy dog.

Input : RoadS? wHERe we're goinG WE doN't need _____.
Output: roads? where we're going we don't need roads.

Input : thE greatESt Trick thE DeVIl EVer PUllEd wAs CONvInciNg tHe WorLD h_ ____'_ _____.
Output: the greatest trick the devil ever pulled was convincing the world he didn't exist.

Some extra test-cases:

Input : BInar_
Output: binary

Input : 12 MonKey_
Output: 12 monkeys

Input : hyPerbolIZ__
Output: hyperbolized

Input : {[One Last Test ca__]}
Output: {[one last test case]}
\$\endgroup\$
11
  • \$\begingroup\$ Should we only consider as many groups of 5 as there are underscores in the input? \$\endgroup\$ Commented May 12, 2018 at 18:10
  • \$\begingroup\$ In that case, the rule when there's no _ in the input string is a bit of a special case. \$\endgroup\$ Commented May 12, 2018 at 18:17
  • 1
    \$\begingroup\$ Ooh, Bacon cipher! \$\endgroup\$
    – Deusovi
    Commented May 12, 2018 at 19:38
  • 1
    \$\begingroup\$ @SztupY As The input is guaranteed not to contain any useless capital letter, in case there is no underscore there also won't be a capital letter. \$\endgroup\$
    – Laikoni
    Commented May 14, 2018 at 11:03
  • 1
    \$\begingroup\$ @KirillL. Yes, anything in [32-126]. I've added another test case. \$\endgroup\$
    – Arnauld
    Commented May 14, 2018 at 12:16

31 Answers 31

22
\$\begingroup\$

05AB1E, 18 bytes

Code:

áS.u5ôJC>.bv'_y.;l

Uses the 05AB1E encoding. Try it online!

Explanation:

á                       # Remove non-letters from the input string.
 S                      # Split the result into individual characters.
  .u                    # Check if is uppercase for each character.
    5ôJ                 # Split into binary numbers of length 5.
       C                # Convert from binary to decimal.
        >               # Add one.
         .b             # Map 1 → A, 2 → B, 3 → C, ..., 25 → Y, 26 → Z.
           v            # For each letter:
            '_y.;       #   Replace the first occurrence of '_' with the current letter.
                 l      #   Convert the string to lowercase.
\$\endgroup\$
9
\$\begingroup\$

Perl 5 -pF -MList::Util=sum, 75 bytes

@a=grep!/\W|\d/,@F;s!_!(a..z)[sum map{a gt shift@a&&16/2**$_}0..4]!eg;$_=lc

Try it online!

Explanation:

  • -pF reads a line of input into the variable $_ and, split into characters, into the array @F.
  • @a=grep!/\W|\d/,@F sets the array @a equal to those members of @F that don't satisfy the regex \W|\d. \W is anything but letters, numbers, and _; \d is numbers. So \W|\d is anything but letters and _, and @a has all the letters and _ characters. We will wind up never examining the _ characters in @a. (Note that this only works because the input is guaranteed ASCII.)
  • map{a gt shift@a&&16/2**$_}0..4 does the following for 0 through 4: It shifts the next element off of @a, shortening it, and evaluates whether a is asciibetically greater than that element (i.e. whether that element is uppercase). If so, && isn't short-circuited, so we get 16 divided by 2 to rhe power of the input value (0 through 4). Otherwise && is short-circuited and we get 0. map returns the list of five numbers to sum, which adds them.
  • That's the element we want from the list a..z, and that's what we get from (a..z)[…].
  • s!_!…!eg converts each _ in $_, in turn, to the appropriate letter.
  • $_=lc converts $_ to the lowercase version of itself, and -p prints it.
\$\endgroup\$
0
8
\$\begingroup\$

Python 2, 113 bytes

s=input()
i=k=0
for c in s:
 if c.isalpha():k+=k+(c<'a');i+=1;s=s.replace('_',chr(k%32+97),i%5<1)
print s.lower()

Try it online!

\$\endgroup\$
7
\$\begingroup\$

J, 62 61 bytes

tolower u:@]`(I.@t=.'_'=[)`[}+/@t$97+_5#.\3|0 2 4-.~'@Z`z'&I.

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Nice answer. 3|0 2 4-.~'@Z`z'&I. is especially nice. \$\endgroup\$
    – Jonah
    Commented Oct 23, 2023 at 19:56
6
\$\begingroup\$

Retina, 91 90 bytes

T`l`a
T`L`A
[^Aa]

L`.{5}
A
aA
+`Aa
aAA
+T`_lA`l_`[^A]A
^
$+¶
+`_(.*)¶a+(.)
$2$1
0G`
T`L`l

Try it online! Explanation:

T`l`a
T`L`A
[^Aa]

Translate lowercase letters to a and uppercase letters to A, deleting everything else.

L`.{5}

Split the Aas into groups of 5.

A
aA
+`Aa
aAA

Convert from binary into unary, treating A as 1 and a as 0. Since there were 5 Aas originally, there are 5 as left, plus a number of As depending on the desired position in the alphabet.

+T`_lA`l_`[^A]A

Increment the last a according to the number of following As.

^
$+¶

Prepend the original input.

+`_(.*)¶a+(.)
$2$1

Replace any _s with the next decoded letter.

0G`

Remove any spare decoded letters.

T`L`l

Lowercase everything.

Retina 0.8.2, 117 bytes

.+
$&¶$&
T`L`l`^.*
T`l`a`.*$
T`L`A
T`aAp`aA_`.*$
(.*¶)?.{5}
$&;
A
aA
+`Aa
aAA
+T`_lA`l_`[^A]A
+`_(.*¶)a+(.);
$2$1
1G`

Try it online! Explanation:

.+
$&¶$&

Duplicate the input.

T`L`l`^.*

Lowercase the first copy.

T`l`a`.*$

Translate lowercase letters to a in the second copy.

T`L`A

Translate uppercase letters to A. These must be in the second copy because the first copy was already lowercased.

T`aAp`aA_`.*$

Delete everything else in the second copy.

(.*¶)?.{5}
$&;

Split the second copy (now just Aas) into groups of 5.

A
aA
+`Aa
aAA
+T`_lA`l_`[^A]A
+`_(.*¶)a+(.);
$2$1
1G`

Decode the letters and insert them as before.

\$\endgroup\$
5
\$\begingroup\$

Jelly,  28 27  26 bytes

-1 thanks to Erik the Outgolfer & dylnan

Not a very Jelly-friendly challenge!

ḟŒs$Ƈ<”[s5Ḅ+97Ọż@ṣ”_$FṁLŒl

A monadic link accepting and returning lists of characters.

Try it online!

How?

ḟŒs$Ƈ<”[s5Ḅ+97Ọż@ṣ”_$FṁLŒl - Link: list of characters    e.g. "MfUNE_?"  (shorthand for ['M','f','U','N','E','_','?'])
    Ƈ                      - filter keep each if:
   $                       -   last two links as a monad:
 Œs                        -     swap-case
ḟ                          -     filter discard
                           - ...i.e. keep A-Z,a-z since they change when the case is swapped
                           -                                  "MfUNE"
      ”[                   - literal character                '['
     <                     - less than? (i.e. is upper-case?) [1,0,1,1,1]
        s5                 - split into fives                 [[1,0,1,1,1]]
          Ḅ                - from base two (vectorises)       [[23]]
           +97             - add (vectorises) ninety-seven    [[120]]
              Ọ            - from ordinals (vectorises)       [['x']]
                    $      - last two links as a monad:
                  ”_       -   literal character              '_'
                 ṣ         -   split at                       [['M','f','U','N','E'],['?']]
               ż@          - swapped @rgument zip             [[['M','f','U','N','E'],'x'],['?']]
                     F     - flatten                          "MfUNEx?"
                       L   - length (of input)                7
                      ṁ    - mould like                       "MfUNEx?"
                           - ...removes any excess characters
                        Œl - lower-case                       "mfunex?"
\$\endgroup\$
0
5
\$\begingroup\$

APL (Dyalog Unicode), 46 bytesSBCS

Anonymous lambda, Assumes ⎕IO (Index Origin) to be 0.

{_←'_'=⊢⋄819⌶A[2⊥⍉(+/_⍵)5⍴A∊⍨⍵∩A,819⌶A←⎕A]@_⍵}

Try it online!

{} two-statement function; is the argument, separates statements

 argument (no-op function)
'_'= where equal to an underscore (i.e. a Boolean mapping function)
_← assign that function to _

A[]@_⍵ put the following characters of A at positions of underscores in the argument
  ⎕A the uppercase Alphabet
  A← assign that to A
  819⌶ lowercase it (819 ≈ BIg, with no left argument means not big, i.e. lowercase)
  A, prepend the uppercase alphabet; this gives us all letters
  ⍵∩ intersection of the argument and that; just the letters of the argument
  A∊⍨ which of those are members of the uppercase alphabet; uppercase bits
  ()5⍴reshape that to the following number of rows, and five columns:
   _⍵ the mask of underscores in the argument
   +/ sum that; number of underscores
   transpose (to treat each row as a number rather than as a bit position)
  2⊥ evaluate as base-2
819⌶ lowercase everything

\$\endgroup\$
0
5
\$\begingroup\$

Scala, 189 bytes

def f(s:Array[Char])={var j=0;s.zipWithIndex.collect{case(95,i)=>s(i)=(Integer.parseInt(s.filter(_.isLetter)slice(j,j+5)map(k=>if(k<91)1 else 0)mkString,2)+97)toChar;j+=5};s.map(_.toLower)}

Try it online!

Explanation:

def f(s: Array[Char]) = {                // takes a String in input
  var j = 0                              // j stores at which block of 5 letters we're currently at
  s.zipWithIndex.collect {               // Array('h', 'e', ...) => Array(('h', 0) ('e', 1), ...) and we apply a collect transformation (filter/map)
    case (95, i) =>                      // we only handle cases where the char is '_' (95)
      s(i) = (                           // we modify the char at index i with the following
        Integer.parseInt(                // Integer.parseInt("00110", 2) = 6
          s                              //
            .filter(_.isLetter)          // filter out non letter chars (spaces, punct, figures, ...) from the input string (thanks @Arnauld for the fix)A
            .slice(j, j+5)               // "substring" the array to the block of 5 letters in question
            .map(                        // map on the current block of 5 letters
              k =>                       // the index of the next char in the block f 5 (e.g. 13)
                if (k < 91) 1 else 0     // if the current char is upper case (<91) then we replace it by a bit true, otherwise by a bit false
            )mkString,                   // Array(0, 1, 1, ...) => "011..."
          2                              // cast string to binary
        )                                //
        + 97                             // +97 to create a lower case char
      )toChar                            // cast from int to char
      j += 5                             // update the starting index of the next block of 5 letters
    }                                    //
  s.map(_.toLower)                       // return the updated seq of chars all in lower case
}                                        //
\$\endgroup\$
0
5
\$\begingroup\$

JavaScript (Node.js), 94 bytes

x=>x.toLowerCase(w=/[a-z]/gi).replace(/_/g,g=Z=>Z>62?(Z-52).toString(36):g(w.exec(x)<g|2*-~Z))

Try it online!

JavaScript (Node.js), 125 124 bytes

x=>x.replace(s=/./g,c=>parseInt(c,36)>9?c.toLowerCase(s+=+(c<{})):c=='_'?('0b'+s.slice(4,9)-~9).toString(36,s=s.slice(5)):c)

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Jelly, 26 bytes

xn¥Œs<”[s5Ḅ‘ịØaṛi”_ḟ0Ɗ¦ƒŒl

Try it online!

Different approach from Jonathan Allan's. EDIT: So, I, uh, apparently thought of the same byte reduction as Jonathan Allan, so it doesn't hurt to mention his name again.

\$\endgroup\$
3
\$\begingroup\$

CJam, 43 bytes

q'_/_0={el_eu=!},_eu.=5/2fb65f+:c1$,(<.+:el

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Clean, 180 ... 150 bytes

import StdEnv
?s=['a'+sum[i\\i<-:""&c<-s|c<'a']: ?(drop 5s)]
@['_':b][x:y]=[x: @b y]
@[a:b]z=[toLower a: @b z]
@e _=e
$s= @s(?(filter isAlpha s))

Try it online!

Defines the function $ :: [Char] -> [Char] with @ :: [Char] [Char] -> [Char] as a helper to replace underscores, and ? :: [Char] -> [Char] as a helper to generate the replacement characters.

\$\endgroup\$
2
  • \$\begingroup\$ How does the i<-:"" part work? Are chars implicitly converted to numbers when summing or adding them? \$\endgroup\$
    – Laikoni
    Commented May 14, 2018 at 12:48
  • \$\begingroup\$ @Laikoni no, there's no implicit conversion. You can add and subtract Chars though. \$\endgroup\$
    – Οurous
    Commented May 14, 2018 at 20:31
3
\$\begingroup\$

JavaScript (Node.js), 100 bytes

s=>s.toLowerCase(c=/[a-z]/gi).replace(/_/g,_=>(g=n=>n?(c.exec(s)<{})*n+g(n>>1):10)(16).toString(36))

Try it online!

Thanks to @Arnauld, saves 2 bytes.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @Arnauld you are right. edited to /[a-z]/gi now. \$\endgroup\$
    – tsh
    Commented May 14, 2018 at 7:27
3
\$\begingroup\$

Stax, 22 bytes

â╟▓ïMeee¶▐f◄┴≈┘n╛äyΩ○N

Run and debug it

The general approach is a regular expression replacement of "_" using a callback function that slices letters of the inputs to compute each replacement character.

v       convert to lower case
'_      "_" string literal
{       begin block for regex replacement
  yVl|& all the letters only from the original input
  5/    split into chunks of 5
  i@    keep the ith one, where i is the 0-based number of times this block has run
  {97<m map 5-letter chunk to bits to indicate which are lowercase
  :b    decode as 5-bit integer
  97+]  add 97 and wrap in array to convert to lower case character
}       end block for regex replacement
R       do regex replacement

Run this one

\$\endgroup\$
3
\$\begingroup\$

R, 153 135 113 bytes

function(s,S=utf8ToInt(s)){S[S==95]=2^(4:0)%*%matrix(S[S%in%c(65:90,97:122)]<95,5)+97
cat(tolower(intToUtf8(S)))}

Try it online!

Issues some warnings with the use of matrix but that shouldn't affect the result. Also issues warnings as [<- assignment will remove extraneous assigned objects by default.

40(!) bytes down thanks to JayCe's improvements

\$\endgroup\$
8
  • \$\begingroup\$ I don't think you need ,length(L)%/%5 \$\endgroup\$
    – JayCe
    Commented May 14, 2018 at 17:05
  • \$\begingroup\$ Also no need to define L? \$\endgroup\$
    – JayCe
    Commented May 14, 2018 at 17:07
  • \$\begingroup\$ @JayCe okay well today I learned that [<- will throw out elements past the length of the indexes... \$\endgroup\$
    – Giuseppe
    Commented May 14, 2018 at 17:21
  • \$\begingroup\$ Me too actually! \$\endgroup\$
    – JayCe
    Commented May 14, 2018 at 17:26
  • \$\begingroup\$ Translated your approach to intToUtf8 \$\endgroup\$
    – JayCe
    Commented May 14, 2018 at 19:03
3
\$\begingroup\$

C (gcc), 111 109 101 100 bytes

Edit: Added lowercasing per @FrownyFrog's comment; thanks to Lynn, Christoph and user5329483 for their suggestions!

f(s,t,i)char*s,*t;{for(t=s;t=strchr(t,95);*t=i+1)for(i=3;i<64;s++)isalpha(*s)?i=2*i|*s<97,*s|=32:0;}

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ You can save 2 bytes with i+=i+(*s<97). \$\endgroup\$
    – lynn
    Commented May 13, 2018 at 9:51
  • \$\begingroup\$ You could abolish j by introducing a marker bit in i and rewriting the second for as for(i=1;i<32;s++). And compensate the extra 32 in the outer for. As a newbee here I count a seven byte spare. \$\endgroup\$ Commented May 14, 2018 at 16:40
  • \$\begingroup\$ Found another byte: for(i=3;i<96;s++) brings the 65 down to a single-digit number, aka 1. \$\endgroup\$ Commented May 15, 2018 at 17:56
3
\$\begingroup\$

Haskell, 139 bytes

([]%)
a%(x:y)|x=='_'=['a'..]!!foldl1((+).(2*))(take 5a):drop 5a%y|x>'@',x<'['=[x..]!!32:(a++[1])%y|x>'`',x<'{'=x:(a++[0])%y|1<2=x:a%y
a%e=e

Try it online!

\$\endgroup\$
3
\$\begingroup\$

TypeScript's Type System, 637 526 515 505 496 490 489 bytes

//@ts-ignore
type a<S="abcdefghijklmnopqrstuvwxyz",A=[]>=S extends`${infer F}${infer R}`?a<R,[...A,F]>:A;type u<S,D>=D extends`${infer A}${infer R}`?`${u<S,R>}${u<S,R>}${S[A]extends a[any]?"":1}`:D;type c<S,T=[]>=S extends[{},{},{},{},{},...infer R]?c<R,[...T,a[a<u<S,"43210">>["length"]]]>:T;type p<S,O=[]>=S extends[infer A,...infer R]?p<R,F<A,0>extends a[any]?[...O,A]:O>:O;type F<S,V=c<p<a<S>>>>=[S,V]extends[`${infer A}_${infer B}`,[{},...infer R]]?F<`${A}${V[0]}${B}`,R>:Lowercase<S>

Try it at the TS Playground!

I managed to golf off a load of bytes:

  1. I started by using @emanresu A's much shorter binary-to-unary type, but noticed that in this case it would be shorter if it operated on a tuple rather than string of bits, so I modified it accordingly.
  2. I then thought to have the binary-to-unary type operate not on 1s and 0s, but on the characters themselves, and check if it's uppercase or lowercase within that same type, saving a lot of bytes.
  3. I no longer needed the Split type for anything other than the alphabet, so I inlined the alphabet into it's definition.
  4. I realized that F could be inlined into the FillBlanks type, by Lowercaseing the final result and giving the fill string a default. I also utilized this tip of mine to reduce two extends clauses into one, saving around 10 bytes.
  5. I changed the Chunk type to operate on a list of characters, so the pattern match could be reduced to just "5 items and the rest", with the 5 items selected individually, and did the splitting in F.
  6. I did a bit more rearranging to reduce the Chunk type a lot more.
  7. I golfed away 2 infer clauses and removed a pair of unneeded parentheses.
  8. I changed u back to using a string and then splitting it to get its length in c.

I’d say at this point this is one of my most well-golfed TS submissions.


This was a fun one!

The following explanation more closely resembles my old 637 bytes version, but the general structure is still roughly the same.

// Split<Str> converts string Str to a tuple of characters.
type Split<
  Str extends string,
  Arr extends string[] = []
> = Str extends `${infer A}${infer R}` ? Split<R, [...Arr, A]> : Arr;

// Lowercase alphabet tuple.
type Alphabet = Split<"abcdefghijklmnopqrstuvwxyz">;

// To get the case of a letter:
// if it is in the lowercase alphabet, return 0, else 1.
type Case<C extends string> = C extends Alphabet[number] ? 0 : 1;

// Replace all occurrences of Find with Rep in string Str.
type Replace<
  Str extends string,
  Find extends string,
  Rep extends string
> = Str extends `${infer Start}${Find}${infer Rest}`
  // Recursively substitute until no more occurrences are found.
  ? Replace<`${Start}${Rep}${Rest}`, Find, Rep>
  : Str;

// See https://esolangs.org/wiki/Binary_to_unary_conversion
type BinToUn1<In extends string> = Replace<In, "1", "0*">
type BinToUn2<In extends string> = Replace<In, "*0", "0**">
type BinToUn3<In extends string> = Replace<In, "0", "">
type BinaryToUnary<Binary extends string> = BinToUn3<
  BinToUn2<BinToUn1<Binary>>
>;


// This type does the bulk of the work.
type Chunk<
  S extends string,
  T extends string = ""
  // Map each chunk of five letters to:
> = S extends `${infer A}${infer B}${infer C}${infer D}${infer E}${infer R}`
  ? Chunk<
      R,
      // The character of the alphabet at index
      `${T}${Alphabet[
        // convert from binary
        Split<BinaryToUnary<
          // the case of each character in the chunk.
          `${Case<A>}${Case<B>}${Case<C>}${Case<D>}${Case<E>}`>
        >["length"]]}`
    >
  : T;

// Filter out non-alphabetic characters.
type Alphabetics<
  S extends string,
  O extends string = ""
> = S extends `${infer A}${infer R}`
  ? Alphabetics<R, Lowercase<A> extends Alphabet[number] ? `${O}${A}` : O>
  : O;

// Fill each underscore with the corresponding character
// of the fill string.
type FillBlanks<
  Str extends string,
  Fill extends string
> = Str extends `${infer Start}_${infer Rest}`
  ? Fill extends `${infer X}${infer R}`
    ? FillBlanks<`${Start}${X}${Rest}`, R>
    : Str
  : Str;

// Final type that composes all of the above to a single type:
type F<S extends string> = Lowercase<FillBlanks<S, Chunk<Alphabetics<S>>>>;

Try it at the TS Playground!

All of the recursive types used here are tail-recursive, increasing the recursion limit from 50 to 999. The only exception to this is the binary-to-unary type, but it won't get near the recursion depth for any 5-bit number.

\$\endgroup\$
6
  • \$\begingroup\$ Why is Split necessary? string already implements ArrayLike<string>. \$\endgroup\$
    – Bbrk24
    Commented Aug 11, 2023 at 16:49
  • \$\begingroup\$ @Bbrk24 Because you can't index into or get the length of a string type. \$\endgroup\$ Commented Aug 11, 2023 at 16:55
  • \$\begingroup\$ You can save 2 bytes by using 2 instead of * (you don't need to quote 20) \$\endgroup\$
    – Bbrk24
    Commented Aug 11, 2023 at 17:15
  • \$\begingroup\$ @Bbrk24 Thought of that but forgot to add it, thanks! \$\endgroup\$ Commented Aug 11, 2023 at 17:23
  • \$\begingroup\$ You can save ~30 bytes by using the binary-to-unary algorithm from my answer here (note: While it's not tail-recursive the recursive calls only go log(n) levels deep) \$\endgroup\$
    – emanresu A
    Commented Aug 12, 2023 at 7:56
2
\$\begingroup\$

Red, 247 bytes

func[s][a: charset[#"a"-#"z"#"A"-#"Z"]v: copy""parse s[any[copy c a(append v to-string c)|
skip]]k: 0 t: copy""repeat n(length? v)/ 5[c: 0 p: 16
loop 5[if v/(k: k + 1) <#"a"[c: c + p]p: p / 2]append t#"a"+ c]foreach c
t[replace s"_"c]lowercase s]

Try it online!

More readable:

f: func[s][
    a: charset[#"a"-#"z"#"A"-#"Z"]
    v: copy ""
    parse s[any[copy c a(append v to-string c)| skip]]
    k: 0
    t: copy ""
    repeat n (length? v) / 5[
        c: 0
        p: 16
        loop 5[
            if v/(k: k + 1) < #"a" [c: c + p]
            p: p / 2
        ]
        append t #"a" + c
    ]
    foreach c t[replace s "_" c]
    lowercase s
]
\$\endgroup\$
2
\$\begingroup\$

Go, 219 217 192 210 209 156 bytes

Saved 25 bytes thanks to @Lynn! Saved 53 bytes thanks to @ovs!

Had to lose 18 bytes because of a bug with strings with no underscores :(

func p(s string){b:=0;n:=0;for _,c:=range s{if IsLetter(c){b+=b;if IsUpper(c){b+=1};n++;s=g.Replace(s,"_",string('a'+b%32),(5-n%5)/5)}};Print(g.ToLower(s))}

Try it online!

\$\endgroup\$
0
2
\$\begingroup\$

Haskell, 165 bytes

g""
import Data.Char
f t(x:s)('_':r)=x:f t s r
f[0,a]s r=g(s++[chr a])r
f[n,a]s(c:r)=toLower c:f[div n$1+sum[1|isAlpha c],a+sum[n|isUpper c]]s r
f _ s e=e
g=f[16,97]

Try it online! Example usage: g"" "BInar_" yields "binary".

\$\endgroup\$
2
\$\begingroup\$

PowerShell, 121 bytes

switch -r($args|% t*y){_{$_=$a[$n++]+97}[a-z]{$x+=$x+($_-le96);if(!(++$i%5)){$a+=,$x;$x=0};$_=$_-bor32}.{$r+=[char]$_}}$r

Try it online!

Less golfed:

switch -Regex ($args|% toCharArray){
    _ {                     # is underscore
        $_=$a[$n++]+97      # get a char from the array of letter
    }

    [a-z] {                 # is letter
        $x+=$x+($_-le96)    # $x=2*$x+($_-le96)
        if(!(++$i%5)){      # if(++$i%5 -eq 0)
            $a+=,$x         # add an element to the array of letters
            $x=0            # init
        }
        $_=$_-bor32         # to lower
    }

    . {                     # is any char ('_' and letters included)
        $r+=[char]$_        # add the char to result
    }
}
$r
\$\endgroup\$
1
\$\begingroup\$

Ruby, 107 106 103 bytes

->s{s.chars.map{|c|c !~/\W|\d/?c<?a?1:0:p}.join.scan(/.{5}/){|b|s[?_]&&=(b.to_i(2)+97).chr};s.downcase}

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Japt, 25 bytes

r'_@r\L mè\A sTT±5 ÍdIÄÃv

Try it


Explanation

r'_                           :Replace underscores
   @                          :Pass each match through a function
    r                         :  From original input remove
     \L                       :    /[^a-zA-Z]/g
        m                     :  Map
         è                    :    Count
          \A                  :      /[A-Z]/g
             s                :  Slice
              T               :    From index T (initially 0)
               T±5            :    To index T+=5
                   Í          :  Convert from base-2 string to base-10 integer
                     IÄ       :  Add 64+1
                    d         :  Get character at that codepoint
                       Ã      :End function
                        v     :Lowercase
\$\endgroup\$
1
\$\begingroup\$

Pyth, 36 bytes

Km@Gim!}kGd2c@+r1GGQ5VQp?qN\_.(KZr0N

Try it here

Explanation

Km@Gim!}kGd2c@+r1GGQ5VQp?qN\_.(KZr0N
             @+r1GGQ                   Get the letters from the input...
            c       5                  ... in chunks of 5.
 m        d                            For each chunk...
     m!}kG                             ... check if each letter is uppercase...
    i      2                           ... converted to binary...
  @G                                   ... and get the corresponding letter.
                     VQp               For each character in the input...
K                       ?qN\_.(KZ      ... if the character is '_', replace it...
                                 r0N   ... otherwise, lowercase it.
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -p, 78 bytes

for$h(s/\W|\d//gr=~y/a-z/0/r=~y/A-Z/1/r=~/.{5}/g){s%_%chr 65+oct"0b$h"%e}$_=lc

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ I fixed it with 3 more bytes, which makes your answer a bit better under the current rules. \$\endgroup\$
    – Xcali
    Commented May 15, 2018 at 13:35
  • \$\begingroup\$ Not better but different. Each language + options is considered separately, not competing with the same language + other options, as I understand it. \$\endgroup\$
    – msh210
    Commented May 21, 2018 at 5:41
1
\$\begingroup\$

Python 3.5, 296 bytes

u=input();f=u.find('_');m=''.join([c for c in u if c.isalpha()]);z=[chr(int(''.join(['0'if o.islower() else'1' for o in l]),2)+65)for l in[m[h:h+5]for h in range(0,len(m),5)]if len(l)==5];[z.insert(v,d)for v,d in enumerate(u[f:])if d!="_"];u=list(u);u[f:]=z[:len(u[f:])];print(''.join(u).lower())

Try it online

First code golf :)

(I know its not small in bytes, I was just having fun making a 1 line code)

Here is the explanation:


User input

u=input()


Finds the index of the first _ in the string and stores it

f=u.find('_')


strips string of all non-alpha characters

m=''.join([c for c in u if c.isalpha()])


Splits the alpha string into an array with each element consisting of 5 characters

Ex. ['THeqU', 'ICkbr', 'ownFO', 'xJUMp', 'soVEr', 'thela']

Then converts lowercase characters to 0 and uppercase characters to 1

Ex. ['11001', '11000', '00011', '01110', '00110', '00000']

and converts the binary string to an integer, adds 65 and converts that to a character

Ex. ['z', 'y', 'd', 'o', 'g', 'a']

z=[chr(int(''.join(['0' if o.islower() else '1' for o in l]),2)+65) for l in [m[h:h+5] for h in range(0,len(m),5)] if len(l)==5]


finds all characteres after the first _ and pushes them into the array z at their respective locations (defined above)

Ex. ['z', 'y', ' ', 'd', 'o', 'g', '.', 'a']

[z.insert(v,d) for v,d in enumerate(u[f:]) if d!="_"]


split our string into a list of characters

u=list(u)


slice our string from the first _ to the end of the list and replace it with the array z. I also had to slice the array z to the length of the split string from the first _ to the end because I got an extra character in the lazy dog example (the "a" at the end of the examples above)

u[f:]=z[:len(list(u[f:]))]


*print out the answer *

print(''.join(u).lower())


\$\endgroup\$
2
  • \$\begingroup\$ There's more to golfing than just getting everything on one line. 165 bytes \$\endgroup\$
    – Jo King
    Commented May 16, 2018 at 10:27
  • \$\begingroup\$ You can remove the space between o.islower() and else, and I think '1' and for. Also, you can change if d!="_" to if"_"!=d, but the above comment already does that. \$\endgroup\$
    – Adalynn
    Commented May 17, 2018 at 12:52
1
\$\begingroup\$

Java 10, 186 184 162 bytes

s->{var b="";for(Byte c:s.getBytes())s=(b+=c>64&c<91?1:c>96&c<123?0:"").length()>4?s.replaceFirst("_",(char)(97+c.valueOf(b,2))+(b="")):s;return s.toLowerCase();}

-2 bytes thanks to @ceilingcat.

Try it online.

Explanation:

s->{                        // Method with String as both parameter and return-type
  var b="";                 //  Binary-String, starting empty
  for(Byte c:s.getBytes())  //  Loop over the characters of the input-String:
    s=(b+=c>64&c<91?        //   If the current character is a lowercase letter:
           1                //    Append "1" to the binary-String
          :c>96&c<123?      //   Else-if it's an uppercase letter:
           0                //    Append "0" to the binary-String
          :                 //   Else (it's not a letter):
           ""               //    Append nothing to the binary-String
      ).length()>4?         //   And if the length of the new binary-String is now 5:
       s.replaceFirst("_",  //    Replace the first "_" in the input-String with
          (char)(97+c.valueOf(b,2))
                            //    the binary-String as character
         +(b="")):s;        //    And reset the binary-String
  return s.toLowerCase();}  //  Return the modified lowercased input-String
\$\endgroup\$
1
  • \$\begingroup\$ @ceilingcat Thanks, but I've been able to save 22 more bytes by changing the for-loop to a for-each loop instead. \$\endgroup\$ Commented Jun 13, 2021 at 21:05
1
\$\begingroup\$

jq, 165 159 bytes

Unnecessary space before as, and found a place where(a|[thing])[] could be replaced with (a|thing)

reduce(split("[^a-z]";"i")|add|explode|while(length>4;.[5:])[:5]|map(try(.>91//1)+0//0)|reduce .[]as $x(3;2*.+$x)+1)as $r(ascii_downcase;sub("_";[$r]|implode))

Try it online! Try it online!

Phew, this was a fun one! reduce is the real star of the show here.

reduce (
    # filter to alphanumeric characters and explode string into codepoints
    split("[^a-z]";"i") | add | explode |
    # chunk letters from string into full groups of 5
    while(length>4; .[5:])[:5] |
    # for each chunk emit 1 if uppercase, 0 if lowercase
    map(try(.>91//1)+0//0) |
    # reduce each chunk to a codepoint
    # start with 3, double and add the next element in the array.
    # [0,0,1,1,0] -> 6 [0,1,1,0] -> ... 102, we need to add one to map correctly
    reduce .[] as $x(3;2*.+$x)+1
# for each code point $r:
) as $r (
    # initialize with lowercased input
    ascii_downcase;
    # substitute "_" with the current letter ([codepoint]|implode)
    sub("_";[$r]|implode)
)
\$\endgroup\$
1
\$\begingroup\$

Vyxal, 135 bitsv2, 16.875 bytes

\_ÞIλ?Ǎ5ẇƛaB›øA}¢ɽ

Try it Online!

The s flag is for user convenience. Outputs as a list of characters otherwise.

Explained

\_ÞIλ?Ǎ5ẇƛaB›øA}¢ɽ­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁣⁢‏⁠‎⁡⁠⁤⁤‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁢⁡⁢‏‏​⁡⁠⁡‌­
\_ÞIλ          }¢   # ‎⁡Replace the indices that correspond to "_"s by each letter in:
     ?Ǎ5ẇ           # ‎⁢  Push the input with non-letters removed, wrapped in chunks of length 5
         ƛ     }    # ‎⁣  To each chunk:
          aB›       # ‎⁤    int(is_upper(chunk), 2) + 1
             øA     # ‎⁢⁡    Nth letter
                 ɽ  # ‎⁢⁢Convert that to lowercase
💎

Created with the help of Luminespire.

\$\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.