29
\$\begingroup\$

0xUsernames

There's so many people using a messaging service that they're running out of space to store all the usernames! To fix this, they are going to start storing usernames as hexadecimal, where possible.

If a username consists of only the characters 0123456789ABCDEF (case insensitive), it can be converted to a hexadecimal and stored as an integer. For example, the username ba5eba11 can be interpreted as 0xBA5EBA11, a hexadecimal integer.

But what about 05AB1E? That's got a leading zero, which would be lost. So, whenever we convert a username, we make sure to prepend a 1 before reading it as an integer.


The Challenge

Your task is to write a program or function which, given a non-empty username as a string, 'hexa-compresses' the username:

  • If it can be interpreted as a hexadecimal integer, prepend a 1, interpret as hexadecimal, and then print the result as base 10.
  • Otherwise, just return the string unmodified.

This is , so the shortest solution (in bytes) wins! Built-in base conversion functions are permitted.


Test Cases

You can assume that any resulting integers with be within your language's standard integer range.

As with usernames on most messaging systems, the input strings will only contain alphanumerics and underscores.

Remember, you always need to add a leading 1 before conversion!

"ba5eba11" -> 7421737489
"05AB1E"   -> 17148702
"dec0de"   -> 31375582
"Beef"     -> 114415    
"da7aba5e" -> 7960443486
"500"      -> 5376

"DENNIS" -> "DENNIS"
"Garth"  -> "Garth"
"A_B_C"  -> "A_B_C"
"0x000"  -> "0x000"

For reference, here is a Python 3 implementation I used for the test cases (ungolfed):

import re

def convert_name(name):
    if re.fullmatch('^[0-9A-Fa-f]+$', name):
        return int('1' + name.upper(), base = 16)
    else:
        return name
\$\endgroup\$
5
  • \$\begingroup\$ Ah, didn't see that. Also, what if some of the larger test cases result in numbers outside the bounds of our language's largest integer type? \$\endgroup\$
    – Doorknob
    Jan 2, 2017 at 14:54
  • 3
    \$\begingroup\$ @Doorknob good catch. I'll say that a resulting integer will never be more than your language's standard integer type. (please don't abuse this and use a language with 1-bit integers) \$\endgroup\$
    – FlipTack
    Jan 2, 2017 at 14:55
  • \$\begingroup\$ Is it ok to assume the input is uppercase only? \$\endgroup\$
    – Adám
    Jan 3, 2017 at 16:05
  • \$\begingroup\$ @Adám sorry, but your program should be case insensitive (see test cases) \$\endgroup\$
    – FlipTack
    Jan 3, 2017 at 18:58
  • \$\begingroup\$ Like Unary except it's encoding usernames instead of BF \$\endgroup\$
    – MilkyWay90
    Feb 20, 2019 at 16:23

24 Answers 24

26
\$\begingroup\$

05AB1E, 4 bytes

D1ìH

Explanation

D    Duplicate input
 1ì  Prepend 1
   H Interpret as hexadecimal and implicitly display the value in base 10

If the input has invalid hex characters, H won't push anything so the last value on the stack will be the duplicated input, that's why the program prints its input in case of invalid input.

Try it online!

\$\endgroup\$
5
  • 9
    \$\begingroup\$ The irony is pretty strong here. 05AB1E is a valid username. \$\endgroup\$
    – devRicher
    Jan 2, 2017 at 16:52
  • 1
    \$\begingroup\$ That's right, however the name was chosen as a hexadecimal number. Hence it's valid :) \$\endgroup\$
    – Osable
    Jan 2, 2017 at 16:58
  • \$\begingroup\$ Wondered why you were duping. Trying to think of a way to use $ instead though.... \$\endgroup\$ Jan 2, 2017 at 19:11
  • 1
    \$\begingroup\$ This is invalid: DENNIS outputs 1DENNIS, not DENNIS. \$\endgroup\$
    – Makonede
    Jan 27, 2022 at 0:19
  • \$\begingroup\$ This answer is five years old. TIO's 05AB1E legacy is based on a commit from July 2018. Well, It appears that this answer is even more legacy ;) . \$\endgroup\$
    – Osable
    Jan 29, 2022 at 7:58
19
\$\begingroup\$

JavaScript (ES6), 15 bytes

s=>'0x1'+s-0||s

How it works

'0x1'+s converts the input into a literal hexadecimal string with a prepended 1, e.g. 0x105ab1e. Then -0 casts the result to a number. JavaScript sees the 0x at the beginning and implicitly tries to convert from hexadecimal; if s contains any non-hexadecimal chars, this returns NaN. Since this is falsy (and output 0 can never be given because of the prepended 1), we can use ||s to return s if the hex conversion failed.

Test snippet

f = s=>'0x1'+s-0||s

for(i of [
  "ba5eba11", "05AB1E", "dec0de", "Beef", "da7aba5e", "500",
  "DENNIS", "Garth", "A_B_C", "0x000"
]) console.log(i + ":", f(i));

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Very nice solution! \$\endgroup\$
    – Grax32
    Jan 3, 2017 at 2:26
  • \$\begingroup\$ Implicit casting is truly beautiful... :') \$\endgroup\$
    – Downgoat
    Jan 5, 2017 at 9:23
10
\$\begingroup\$

Python 2, 44 bytes

Takes input as a quoted string. -2 bytes thanks to Rod!

a=input()
try:exec'a=0x1'+a
except:1
print a

As we're guaranteed that the input will only contain alphanumerics and underscores, there's no way to create valid Python after 0x1 other than having a hex string. If the input is anything else, the error is ignored, and printing as it originally was.

I couldn't seem to make a regex match any shorter than try/except. In fact, regex turned out to be terribly verbose:

import re
lambda n:re.match('^[0-9A-F]+$',n,2)and int('1'+n,16)or n
\$\endgroup\$
6
  • \$\begingroup\$ you can also replace a=int('1'+a,16) with exec'a=0x1'+a (probably, need to test) \$\endgroup\$
    – Rod
    Jan 2, 2017 at 15:07
  • \$\begingroup\$ You know, we are going to have to same exact answer if I continue golfing right? \$\endgroup\$ Jan 2, 2017 at 15:15
  • \$\begingroup\$ Doesn't work for usernames that would be valid Python in that context, e.g. "+input()". \$\endgroup\$ Jan 2, 2017 at 19:10
  • \$\begingroup\$ fails for "abc " (notice the whitespace at the end)(int allows whitespace at beginning and end) \$\endgroup\$
    – Siphor
    Jan 2, 2017 at 20:32
  • \$\begingroup\$ I don't know exactly how it is for Python 2, but I think you can remove the brackets () at input() \$\endgroup\$
    – user60199
    Jan 3, 2017 at 20:20
8
\$\begingroup\$

Perl 6, 19 bytes

{:16(1~S/_/Z/)//$_}

Test it

Expanded:

{   # bare block lambda with implicit parameter 「$_」

    :16(     # convert from base 16
      1
      ~      # Str concatenated
      S/_/Z/ # replace an underscore with an invalid base 16 character
    )

  //         # defined or

    $_       # the input unchanged

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

Perl, 27 bytes

-1 byte thanks to @ardnew.

26 bytes of code + -p flag.

$_=hex"1$_"if!/[^0-9a-f]/i

Supply the input without final newline. With echo -n for instance:

echo -n 05AB1E | perl -pe '$_=hex"1$_"if!/[^0-9a-f]/i'

Explanation
This is pretty straight forward: /[^0-9a-f]/i is true if the input contains a character other than those allowed inside hexadecimal numbers. If it's false, $_ (which contains the input) it is set to the converted value (the conversion is done by the builtin hex).
And $_ is implicitly printed thanks to -p flag.

\$\endgroup\$
2
  • \$\begingroup\$ you can shave a byte by avoiding the ternary operation $_=hex"1$_"if!/[^0-9a-f]/i \$\endgroup\$
    – ardnew
    Jan 3, 2017 at 17:21
  • \$\begingroup\$ @ardnew Hum, now that you say it, that ternary was pretty awful... Anyway, thanks! \$\endgroup\$
    – Dada
    Jan 3, 2017 at 18:35
4
\$\begingroup\$

Pyth - 9 bytes

.xi+1z16z

Same idea as fliptack's answer. Try hexadecimal-decimal conversion else output the input.

Try it here!

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

Batch, 33 bytes

@(cmd/cset/a0x1%1 2>nul)||echo %1

How it works

A string is passed in as an argument, 1 is prepended to it, and the string is implicitly converted to decimal and printed. If the string is not valid hexadecimal, it is simply displayed.

It should be noted that since batch math uses signed 32-bit integers, the biggest allowed username is FFFFFFF.

cmd /c takes the next command, runs it in a new terminal, and exits.

set /a performs math and implicitly displays the result in decimal when not stored to a variable.

0x1%1 tells set to prepend a 1 to the first argument (this is easy since all batch variables are strings) and indicates that the string should be treated as hexadecimal.

2>nul silences any errors resulting from an invalid hexadecimal number

|| is a logical OR and performs the command on the right if the command on the left is not successful. The parentheses make everything up to this point one command.

echo %1 simply displays the first argument.

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

Common Lisp, 71

(lambda(n)(or(ignore-errors(parse-integer(format()"1~A"n):radix 16))n))

Tests

Define function

CL-USER> (lambda(n)(or(ignore-errors(parse-integer(format()"1~A"n):radix 16))n))
#<FUNCTION (LAMBDA (N)) {10041D213B}>

Quote a list of expected inputs, as given by the question:

CL-USER> '("ba5eba11" -> 7421737489
"05AB1E"   -> 17148702
"dec0de"   -> 31375582
"Beef"     -> 114415    
"da7aba5e" -> 7960443486
"500"      -> 5376

"DENNIS" -> "DENNIS"
"Garth"  -> "Garth"
"A_B_C"  -> "A_B_C"
"0x000"  -> "0x000")
("ba5eba11" -> 7421737489 "05AB1E" -> 17148702 "dec0de" -> 31375582 "Beef" ->
 114415 "da7aba5e" -> 7960443486 "500" -> 5376 "DENNIS" -> "DENNIS" "Garth" ->
 "Garth" "A_B_C" -> "A_B_C" "0x000" -> "0x000")

Parse it and collect results

CL-USER> (loop for (in _ out) on * by #'cdddr
               collect (list in out (funcall ** in)))
(("ba5eba11" 7421737489 7421737489) ("05AB1E" 17148702 17148702)
 ("dec0de" 31375582 31375582) ("Beef" 114415 114415)
 ("da7aba5e" 7960443486 7960443486) ("500" 5376 5376)
 ("DENNIS" "DENNIS" "DENNIS") ("Garth" "Garth" "Garth")
 ("A_B_C" "A_B_C" "A_B_C") ("0x000" "0x000" "0x000"))

Check that the expected outputs match the actual ones:

CL-USER> (every (lambda (x) (equalp (second x) (third x))) *)
T
\$\endgroup\$
2
\$\begingroup\$

C, 108 bytes

i;f(char*s){char*S=malloc(strlen(s)+2);*S=49;strcpy(S+1,s);sscanf(S,"%x%c",&i,&i)<2?printf("%d",i):puts(s);}

This is a function that takes the string as an argument and prints the result to STDOUT.

i;                           // declare i as an int
f(char*s){
char*S=malloc(strlen(s)+2);  // allocate space for a new string with 1 more char
*S=49;                       // set the first char to '1' (ASCII 49)
strcpy(S+1,s);               // copy the original string to the remainder
sscanf(S,"%x%c",&i,&i)       // scan a hex integer followed by any char
<2?                          // if less than 2 items were scanned (i.e. the hex
                             // integer made up the entire string),
printf("%d",i)               // output the hex integer
:puts(s);}                   // otherwise, output the original string
\$\endgroup\$
2
  • \$\begingroup\$ Nice use of implicit int :) \$\endgroup\$
    – FlipTack
    Jan 2, 2017 at 15:14
  • \$\begingroup\$ 100 bytes \$\endgroup\$
    – ceilingcat
    May 25, 2020 at 4:47
2
\$\begingroup\$

Python 2 - 63, 52, 50, 46 Bytes

n=input()
try:n=int("1"+n,16)
except:1
print n

This uses Python's int() which converts any string with its appropriate base into base 10. In this case, the string is the number 1 attached to the input. If the input is invalid (has characters other than 0123456789ABCDEF (case-insensitive), it returns ValueError:

n = input()                   # Get input (with quotes)
try:                          # Trying conversion to base 10
    n = int("1"+n,16)        
except:                       # If invalid string for base 36,
    1                         # do nothing to n
print n                       # Print result

Try it here!

Thanks to @FlipTack for saving 15 bytes!

\$\endgroup\$
2
  • \$\begingroup\$ What if the string doesn't start with a zero? You should only be adding a one to the left of the string if it starts with a zero. \$\endgroup\$
    – 0WJYxW9FMN
    Jan 2, 2017 at 14:41
  • \$\begingroup\$ @FlipTack Whoops, silly me. \$\endgroup\$
    – 0WJYxW9FMN
    Jan 2, 2017 at 14:42
2
\$\begingroup\$

JavaScript: 46 41 bytes

s=>/[^\dA-F]/i.test(s)?s:parseInt(1+s,16)
\$\endgroup\$
2
  • \$\begingroup\$ The regex can be 2 bytes shorter: /[^0-9a-f]/i \$\endgroup\$
    – GilZ
    Jan 2, 2017 at 16:03
  • \$\begingroup\$ I saved 1 byte by replacing 0-9 by \d, 3 bytes by adding the case-insensitive flag (thanks @GilZ) and 2 more bytes by removing F=, which isn't needed. Thanks for the suggestion. \$\endgroup\$
    – Luke
    Jan 2, 2017 at 16:10
2
\$\begingroup\$

PHP, 42 bytes

hex2bin() returns false if the input is not a valid hex string. This is shorter than using regex to look for non hex digits, but we need the @ operator because it's not silent when it fails.

<?=@hex2bin($s=$argv[1])?hexdec("1$s"):$s;
\$\endgroup\$
1
  • \$\begingroup\$ hex2bin fails for strings with uneven lengths. Still two bytes shorter than with preg_match though: <?=@hex2bin($s=$argv[1])|@hex2bin($s.a)?hexdec("1$s"):$s; for 57 bytes. \$\endgroup\$
    – Titus
    Jan 4, 2017 at 16:52
2
\$\begingroup\$

bash, 46 35 31 bytes

(echo $[0x1$1])2> >(:)||echo $1

Save as a script, and pass the username as an argument.

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

Dyalog APL, 37 bytes

Uses no built-in validation or hex-dec conversion. Requires ⎕IO←0 which is default on many systems.

{∧/(u←1(819⌶)⍵)∊d←⎕D,6↑⎕A:16⊥1,d⍳u⋄⍵}

Ungolfed:

{
    d ← ⎕D , 6 ↑ ⎕A
    u←1 (819⌶) ⍵
    ∧/ u ∊ d: 16 ⊥ 1 , d ⍳ u
    ⍵
}

d ← ⎕D , 6 ↑ ⎕Ad gets Digits followed by the first 6 elements of the Alphabet

u ← 1 (819⌶) ⍵u gets uppercased (819 ≈ "Big") argument

∧/ u ∊ d: if all elements of u are members of d, then:
16 ⊥ 1 , d ⍳ u find the indices of u in d, prepend a 1, and evaluate as base 16

else: return the (unmodified) argument

TryAPL online:

  1. Set ⎕IO to zero, define a replacement for (forbidden on TryAPL for security reasons), and set ⎕PP (Print Precision) to 10 for the large results

  2. Try all the test cases

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

Ruby, 47 44 bytes

p gets=~/^[a-f\d]+\s$/i?('1'+$&).to_i(16):$_

I could remove 3 bytes by changing puts for p, but I feel like the output would be considered wrong since it has a newline at the end.

Edit: Changed puts for p as trailing newlines are typically accepted, thanks @Mego.

\$\endgroup\$
1
  • \$\begingroup\$ Trailing newlines on STDOUT are typically considered acceptable. \$\endgroup\$
    – user45941
    Jan 3, 2017 at 5:12
1
\$\begingroup\$

Japt, 11 bytes

+`0x1{U}`ªU

Try it Online!

Big thanks to ETHproductions!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is one of the rare cases where you can get rid of the space :-) (Also, the TIO link is to the "Hello, World!" program) \$\endgroup\$ Jan 3, 2017 at 20:32
1
\$\begingroup\$

REXX, 49 48 bytes

signal on syntax
pull a
a=x2d(1||a)
syntax:
say a

The signal on syntax tells the interpreter to jump to the label syntax whenever a syntax error occurs. The program tries to reassign a with a hex-to-decimal converted version with a leading 1, but jumps to the syntax label if that fails. If the conversion does pass, it simply ignores the label and outputs the reassigned variable.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Could you explain your code please \$\endgroup\$ Jan 3, 2017 at 16:31
1
\$\begingroup\$

Vyxal, 8 bytes

k6↔=[1pH

Try it Online!

k6↔      # Remove non-hex chars from input
   =[    # If it equals the original
     1p  # Prepend a 1
       H # Convert from hexadecimal
\$\endgroup\$
1
\$\begingroup\$

Whython, 34 bytes

lambda n:eval("_"in n or"0x1"+n)?n

Attempt This Online!

Explanation

The ? operator in Whython returns the value of its left operand, unless the left operand raised an error during execution, in which case it returns the value of its right operand. So in theory, all we have to put on the left is eval("0x1" + n): prepend a 1 and evaluate as a hexadecimal literal.

In practice, since Whython is based on Python 3, we run afoul of the Python 3 feature that underscores can be used as separators in numeric literals. Thus, an input like A_B_C does not error, but is treated as if it were ABC. That's not what we want. We need to make inputs with underscores cause errors too. Thus, we pass the following to eval:

"_" in n or "0x1" + n

If n contains any underscores, "_" in n is True, the or short-circuits, and we're left with eval(True), which errors because eval expects a string. If there are no underscores, "_" in n is False, and we go on to eval the hex-literal string as usual.

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

PowerShell, 35 bytes

param($v)(($h="0x1$v"|iex),$v)[!$h]

Try it online! or Run all test cases!

Explanation

  1. Take parameter ($v)
  2. Create a two-element array where the first element (0) is the result of a string containing 0x1$v piped into Invoke-Expression (iex), while simultaneously assigning this value to $h. If the conversion fails, $h will remain $null.
  3. The second element of the array is the original parameter.
  4. Index into the array with the boolean -not value of $h. Whatever $h is will be implicitly converted to [bool] ($null in the case of an invalid conversion will become $false, a positive integer in the case of successful conversion will become $true) before being negated, which is then implicitly converted to [int] by the array indexer [] ($true will be 1, $false will be 0), thus resulting in the first element of the array (the conversion result) chosen if the conversion was successful, and the second element chosen if the conversion was unsuccessful.
\$\endgroup\$
0
\$\begingroup\$

Scala, 40 bytes

s=>try{BigInt("1"+s,16)}catch{case e=>s}

Usage:

val f:(String=>Any)=s=>try{BigInt("1"+s,16)}catch{case e=>s}
f("ba5eba11") //returns 7421737489

Explanation:

s=>                //define a anonymous function with a parameter called s
  try {              //try...
    BigInt("1"+s,16)   //to contruct a BigInt from "1" prepended to the number, parsing it as base 16
  } catch {          //if the constructor throws an exception
    case e =>          //in case of an execption which we'll call e
      s                  //return s
  }
\$\endgroup\$
0
\$\begingroup\$

C#, 58 bytes

u=>{try{u=Convert.ToInt64("1"+u,16)+"";}catch{}return u;};

Ungolfed with test cases:

using System;
class Class
{
    public static void Main()
    {
        Func<string, string> convert = 
            u=>
            {
                try
                {
                    u = Convert.ToInt64("1" + u, 16) //Prepends "1" and tries to convert the string to and integer using base 16.
                        + ""; //Appending an empty string converts the integer to a string. Shorter than calling .ToString()
                }
                catch { } //If the conversion fails catch the exception and discard it.
                return u; //Return the result, or the unmodified input if the conversion failed.
            };

        Console.WriteLine(convert("ba5eba11"));
        Console.WriteLine(convert("05AB1E"));
        Console.WriteLine(convert("dec0de"));
        Console.WriteLine(convert("Beef"));
        Console.WriteLine(convert("da7aba5e"));
        Console.WriteLine(convert("500"));
        Console.WriteLine(convert("DENNIS"));
        Console.WriteLine(convert("Garth"));
        Console.WriteLine(convert("A_B_C"));
        Console.WriteLine(convert("0x000"));
        Console.Read();
    }
}

Try online

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

Dart, 51 bytes

(s)=>int.parse('1$s',radix:16,onError:(_)=>null)??s

Try it here

Most of the overhead comes from named parameters... Oh well!

At least Dart lets you be dynamically typed, if you want to :D

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

05AB1E, 19 bytes

"0x1ÿ".EDн>ΘI'_å~_×

Try it online!

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