-4

I have a credit card number like 1234567891234 and I want to show only the last 4 characters of this string, like #########1234. How can I do this?

Kache
  • 15,647
  • 12
  • 51
  • 79
  • While it looks like this is attracting downvotes because the OP didn't include any sample code, I think the question is clear enough (and broadly useful enough) that it doesn't really deserve closure or aggressive downvotes. YMMV. – Todd A. Jacobs Aug 28 '20 at 22:12
  • Hi Ozan, try editing your question to include what you've got so far. Or how you think you might go about it. You'll find people are more likely to help when they can see where you're going wrong rather than providing the entire program for you. – Kelly Bang Aug 28 '20 at 22:36

2 Answers2

4
string.gsub(/.(?=....)/, '*')
=> "*********1234" 

gsub without the ! does not mutate the original object that string points to and can take regex arguements.

. matches with a character that is not a line break and ?= is a positive lookahead, so any character that has four characters beyond it, that are not line breaks, will be replaced with the second gsub parameter, which is *.

string.gsub(/\d(?=[0-9]{4})/, '*')
=> "*********1234" 

produces the same output, looking for digits with \d and doing a positive lookahead with [0-9]{4} which matches for four characters between zero and nine.

benjessop
  • 1,729
  • 1
  • 8
  • 14
2

Masking All Digits Except Last Four

If you're just trying to mask the credit card number, there are a number of ways to do that. However, what makes it potentially tricky is that credit card numbers can have anywhere from 13-19 digits, although 16 is certainly the most common.

One of the easiest ways to work around this expected variation is to use is String#slice! to save the last four digits, and then String#tr to convert the remainder of the digits to your masking character. For example:

def mask_credit_card card_number
  credit_card_number = String(card_number).scan(/\d/).join
  last_four_digits   = credit_card_number.slice! -4..-1
  credit_card_number.tr("0-9", "#") << last_four_digits
end

# Test against various lengths & formats.
[
  "1234567890123456",
  "1234-5678-9012-3456",
  1234567890123456,
  "1234-567890-12345",
].map { |card_number| mask_credit_card card_number }
#=> ["############3456", "############3456", "############3456", "###########2345"]

Caveats & Considerations

Some cards like Diners Club can start with a zero, making the card number unsuitable for processing as an Integer. Treating the card number as a String can be more reliable, but forces you to think about how you'll handle unexpected characters or spacing.

Extracting digits with #scan is safer than using #delete when invoking the mask on unsanitized input. For example, String(card_number).delete "-\s\t" would normalize the example data above, but might not catch other unexpected characters. Never trust user input!

If you want to preserve spacing, dashes, and so forth in your masking, you run the risk that a malformed string (e.g. "1234-5678-9012-34 5-6") will yield unexpected results like " 5-6" as the last four digits. It's usually better to normalize your inputs, and apply your chosen formatting to your outputs (e.g. with printf or sprintf) instead. Of course, your specific use case may vary.

Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199