Converting "0xUsernames"

25

1

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

FlipTack

Posted 2017-01-02T14:28:54.460

Reputation: 13 242

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? – Doorknob – 2017-01-02T14:54:11.040

2@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) – FlipTack – 2017-01-02T14:55:40.863

Is it ok to assume the input is uppercase only? – Adám – 2017-01-03T16:05:30.043

@Adám sorry, but your program should be case insensitive (see test cases) – FlipTack – 2017-01-03T18:58:19.163

Like Unary except it's encoding usernames instead of BF – MilkyWay90 – 2019-02-20T16:23:17.243

Answers

27

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!

Osable

Posted 2017-01-02T14:28:54.460

Reputation: 1 321

9The irony is pretty strong here. 05AB1E is a valid username. – devRicher – 2017-01-02T16:52:31.717

1That's right, however the name was chosen as a hexadecimal number. Hence it's valid :) – Osable – 2017-01-02T16:58:12.913

Wondered why you were duping. Trying to think of a way to use $ instead though.... – Magic Octopus Urn – 2017-01-02T19:11:30.730

16

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));

ETHproductions

Posted 2017-01-02T14:28:54.460

Reputation: 47 880

2Very nice solution! – Grax32 – 2017-01-03T02:26:57.553

Implicit casting is truly beautiful... :') – Downgoat – 2017-01-05T09:23:12.977

10

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

FlipTack

Posted 2017-01-02T14:28:54.460

Reputation: 13 242

you can also replace a=int('1'+a,16) with exec'a=0x1'+a (probably, need to test) – Rod – 2017-01-02T15:07:39.923

You know, we are going to have to same exact answer if I continue golfing right? – Anthony Pham – 2017-01-02T15:15:33.010

Doesn't work for usernames that would be valid Python in that context, e.g. "+input()". – heinrich5991 – 2017-01-02T19:10:12.443

fails for "abc " (notice the whitespace at the end)(int allows whitespace at beginning and end) – Siphor – 2017-01-02T20:32:42.470

I don't know exactly how it is for Python 2, but I think you can remove the brackets () at input() – RudolfJelin – 2017-01-03T20:20:00.590

@RudolfLJelinek no, that would assign a to the input function. The function has to be called to take input from the user. – FlipTack – 2017-01-03T20:29:06.040

8

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

}

Brad Gilbert b2gills

Posted 2017-01-02T14:28:54.460

Reputation: 12 713

7

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.

Dada

Posted 2017-01-02T14:28:54.460

Reputation: 8 279

you can shave a byte by avoiding the ternary operation $_=hex"1$_"if!/[^0-9a-f]/i – ardnew – 2017-01-03T17:21:52.387

@ardnew Hum, now that you say it, that ternary was pretty awful... Anyway, thanks! – Dada – 2017-01-03T18:35:22.257

4

Pyth - 9 bytes

.xi+1z16z

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

Try it here!

Gurupad Mamadapur

Posted 2017-01-02T14:28:54.460

Reputation: 1 791

3

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

coredump

Posted 2017-01-02T14:28:54.460

Reputation: 6 292

3

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.

SomethingDark

Posted 2017-01-02T14:28:54.460

Reputation: 211

2

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

Doorknob

Posted 2017-01-02T14:28:54.460

Reputation: 68 138

Nice use of implicit int :) – FlipTack – 2017-01-02T15:14:57.543

2

JavaScript: 46 41 bytes

s=>/[^\dA-F]/i.test(s)?s:parseInt(1+s,16)

Luke

Posted 2017-01-02T14:28:54.460

Reputation: 4 675

The regex can be 2 bytes shorter: /[^0-9a-f]/i – GilZ – 2017-01-02T16:03:47.623

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. – Luke – 2017-01-02T16:10:49.107

2

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;

Alex Howansky

Posted 2017-01-02T14:28:54.460

Reputation: 1 183

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. – Titus – 2017-01-04T16:52:16.323

2

bash, 46 35 31 bytes

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

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

Mitchell Spector

Posted 2017-01-02T14:28:54.460

Reputation: 3 392

1

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!

Anthony Pham

Posted 2017-01-02T14:28:54.460

Reputation: 1 911

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. – 0WJYxW9FMN – 2017-01-02T14:41:09.180

@FlipTack Whoops, silly me. – 0WJYxW9FMN – 2017-01-02T14:42:06.117

1

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.

Simon Landry

Posted 2017-01-02T14:28:54.460

Reputation: 441

Trailing newlines on STDOUT are typically considered acceptable. – Mego – 2017-01-03T05:12:39.530

1

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.

idrougge

Posted 2017-01-02T14:28:54.460

Reputation: 641

2Could you explain your code please – Anthony Pham – 2017-01-03T16:31:14.570

1

Japt, 11 bytes

+`0x1{U}`ªU

Try it Online!

Big thanks to ETHproductions!

Oliver

Posted 2017-01-02T14:28:54.460

Reputation: 7 160

1This is one of the rare cases where you can get rid of the space :-) (Also, the TIO link is to the "Hello, World!" program) – ETHproductions – 2017-01-03T20:32:08.403

1

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

Adám

Posted 2017-01-02T14:28:54.460

Reputation: 37 779

0

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.

briantist

Posted 2017-01-02T14:28:54.460

Reputation: 3 110

0

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
  }

corvus_192

Posted 2017-01-02T14:28:54.460

Reputation: 1 889

0

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

raznagul

Posted 2017-01-02T14:28:54.460

Reputation: 424

0

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

Dwayne Slater

Posted 2017-01-02T14:28:54.460

Reputation: 111