DEV Community

Daily Challenge #179 - Hide Phone Numbers

dev.to staff on February 02, 2020

Setup Implement a function that will hide the last six digits of each phone number. Your function should be able to understand the separ...
Collapse
 
avaq profile image
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}$/.

Collapse
 
alvaromontoro profile image
Alvaro Montoro

This is neat

Collapse
 
cirlorm_io profile image
C I R L O R M ⚡

Winner

Collapse
 
epranka profile image
Edvinas Pranka

Javascript

function encryptNum(num){
  return num.substr(0, 5) + 'XX-XXXX'
}
Collapse
 
exts profile image
Lamonte

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

Collapse
 
epranka profile image
Edvinas Pranka

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

Thread Thread
 
georgewl profile image
George WL • Edited

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

Collapse
 
aminnairi profile image
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".

Collapse
 
idanarye profile image
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");
}
Collapse
 
cipharius profile image
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
Collapse
 
candidateplanet profile image
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
Collapse
 
aminnairi profile image
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 )
Collapse
 
zoejm profile image
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
}
Collapse
 
mellen profile image
Matt Ellen-Tsivintzeli • 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');
Collapse
 
exts profile image
Lamonte • Edited

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"));
Collapse
 
alvaromontoro profile image
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("");
}
Collapse
 
viktordimitrievski profile image
Viktor

JavaScript solution:

const encryptNum = num => {
    num = num.replace(/[-,\s]/g,"");
    return `${num.substring(0,3)}-${num.substring(3,4)}XX-XXXX`
}
Collapse
 
cipharius profile image
Valts Liepiņš

Ruby

def encryptNum num
    num.reverse!.gsub(/\d/).with_index { |d, i| i < 6 ? 'X' : d }.reverse!
end
Collapse
 
toanleviettiki profile image
Toan Le Viet

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

Collapse
 
georgewl profile image
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?