dev.to staff

Posted on

# Daily Challenge #179 - Hide Phone Numbers

### Setup

Implement a function that will hide the last six digits of each phone number. Your function should be able to understand the separators for US numbers. Phone numbers can be separated by spaces, periods, or hyphens.

### Examples

Original: 201-680-0202
Encrypted: `201-6XX-XXXX

### Tests

encryptNum("328 6587120")
encryptNum("212-420-0202")
encryptNum("211-458-7851")

This challenge comes from otrebor6 on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Aldwin Vlasblom • Edited

JavaScript:

const encryptNum = num => num.replace(/(\d[ .-]?){6}\$/, x => x.replace(/\d/g, 'X'))

The first replace finds the last 6 digits, with or without trailing separators.
The resulting match undergoes another replace call to substitute any digits for X's.

The first expression can also be relaxed to allow any separators: /(\d[^\d]?){6}\$/, or even any number of separators: /(\d[^\d]*){6}\$/.

Alvaro Montoro

This is neat

C I R L O R M ⚡

Winner

Edvinas Pranka

Javascript

function encryptNum(num){
return num.substr(0, 5) + 'XX-XXXX'
}

Lamonte

This only works if you're expecting a certain value. Output would be weird otherwise.

Edvinas Pranka

Yeah, but the data validation is not required in this challange.

George WL • Edited

True, but it does expect the tests to all pass still, which they won't with that solution

Amin

Nice short solution!

If I'm being correct, for the string "328 6587120", the expected output should be "328 6XXXXXX".

But in your case, the output would be "328 6XX-XXXX".

Idan Arye

Rust:

fn encrypt_num(phone_number: String) -> String {
let mut bytes = phone_number.into_bytes();
for ch in bytes.iter_mut().rev().filter(|b| {
let c = **b as char;
'0' <= c && c <= '9'
}).take(6) {
*ch = 'X' as u8;
}
String::from_utf8(bytes).unwrap()
}

fn main() {
assert_eq!(encrypt_num("328 6587120".to_owned()), "328 6XXXXXX");
assert_eq!(encrypt_num("212-420-0202".to_owned()), "212-4XX-XXXX");
assert_eq!(encrypt_num("211-458-7851".to_owned()), "211-4XX-XXXX");
}

Valts Liepiņš

Haskell, similar idea to my solution in Ruby

import Data.Char (isDigit)

encryptNum :: String -> String
encryptNum = reverse . encryptDigit 0 . reverse
where
encryptDigit 6 xs     = xs
encryptDigit i (x:xs) | isDigit x = 'X' : encryptDigit (i+1) xs
| otherwise =  x  : encryptDigit i xs

lusen / they / them 🏳️‍🌈🥑

Python, a straightforward approach that would be fast to design and implement without bugs during an interview. It is big-O optimal and easy to understand and refactor.

def encrypt(phone_num):
# define some const. easy to refactor into method sig or config elsewhere later
ENCRYPT_NUMBER = 6
ENCRYPT_SYMBOL = 'X'

# we'll return this at the end - reversed and joined.
encrypted = []
# count the numbers we encrypt
counter = 0

# start at the end and encrypt numbers until we reach ENCRYPT_NUMBER
for ch in phone_num[::-1]:
# we're done encrypting so pass everything along
if counter == ENCRYPT_NUMBER:
encrypted.append(ch)
continue

try:
# if character is a number, encrypt it
int(ch)
counter += 1
encrypted.append(ENCRYPT_SYMBOL)
except ValueError:
# character is not a number so pass it  through
encrypted.append(ch)

# return a string in the right order
return ''.join(encrypted[::-1])

print(encrypt("328 6587120")) # 328 6XXXXXX
print(encrypt("212-420-0202")) # 212-4XX-XXXX
print(encrypt("211-458-7851")) # 211-4XX-XXXX
print(encrypt("211 458 7851")) # 211 4XX XXXX
print(encrypt("2114587851")) # 2114XXXXXX

Amin

Elm

encryptDigit : Char -> Char
encryptDigit character =
if Char.isDigit character then
'X'

else
character

encryptNum : String -> String
encryptNum string =
String.map encryptDigit ( String.dropLeft 5 string )
|> String.append ( String.left 5 string )

zoe-j-m

Scala,

def encryptNum(input : String) = {
def encryptInner(remain : Int, toGo : List[Char]) : List[Char] = {
toGo match {
case Nil => Nil
case _ if remain == 0 => toGo
case x::xs if x.isDigit => 'X' :: encryptInner(remain - 1, xs)
case x::xs => x :: encryptInner(remain, xs)
}
}

encryptInner(6, input.reverse.toList).reverse.mkString
}

Matt Ellen • Edited

ONE REGEX TO RULE THEM

Put your separators where you want! I'll find them all.

const encryptNum = num => num.replace(/((\d[-\s.]?){4})(\d)([\s.-]?)(\d)([\s.-]?)(\d)([\s.-]?)(\d)([\s.-]?)(\d)([\s.-]?)(\d)/, '\$1X\$4X\$6X\$8X\$10X\$12X');

Dart - not a fan of being creative.

String encryptNum(String phoneNum) {
var regexp = RegExp(r"([0-9- .]+)");
var result = regexp?.firstMatch(phoneNum)?.group(0);
result = result?.replaceAll(new RegExp(r"(\.|-|\s)"), "");
if(result.length == 10) {
var val = StringBuffer();
for(var idx = 0; idx < result.length; idx++) {
if(idx < 4) {
val.write(result[idx]);
} else {
val.write("X");
}
switch(idx) {
case 2:
case 5:
val.write("-");
break;
}
}
return val.toString();
}
return null;
}
print(encryptNum("142 424 2142"));
print(encryptNum("142 424-2142"));
print(encryptNum("142 424.2142"));
print(encryptNum("142 4242142"));
print(encryptNum("142-424-2142"));
print(encryptNum("142-424 2142"));
print(encryptNum("142-424.2142"));
print(encryptNum("142-4242142"));
print(encryptNum("142.424 2142"));
print(encryptNum("142.424-2142"));
print(encryptNum("142.424.2142"));
print(encryptNum("142.4242142"));
print(encryptNum("142424-2142"));
print(encryptNum("142424.2142"));
print(encryptNum("142424 2142"));
print(encryptNum("1424242142"));

Alvaro Montoro

JavaScript

If we want all the phone numbers to have the same format, we could use a regular expression to detect all the digits (assuming that the phone number is correct) and format it accordingly:

const encryptNum = (num) => {
const nums = num.match(/\d/gi).join("");
return `\${nums.substring(0,3)}-\${nums.substring(3,6)}-\${nums.substring(6)}`;
}

If we want to keep the original format (and again, assuming the phone number is correct), another solution would be transforming the string into an array (strings are immutable, an array would be easier to operate), parse the last 6 digits and return its concatenation:

const encryptNum = (num) => {
let numbers = 0;
let position = num.length - 1;
let formattedNum = num.split("");

while (numbers < 6 && position > 0) {
if (num[position] >= "0" && num[position] <= "9") {
formattedNum[position] = "x";
numbers++;
}
position--;
}

return formattedNum.join("");
}

Viktor

JavaScript solution:

const encryptNum = num => {
num = num.replace(/[-,\s]/g,"");
return `\${num.substring(0,3)}-\${num.substring(3,4)}XX-XXXX`
}

Valts Liepiņš

Ruby

def encryptNum num
num.reverse!.gsub(/\d/).with_index { |d, i| i < 6 ? 'X' : d }.reverse!
end

Toan Le Viet

My basic solution in JS: Reverse string then replace with 'X' and count to 6

George WL • Edited

Didn't even know you can embed into comments, that's really cool.

Do you not feel that solution is overcomplicating it?