Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

ruby - How to mask all but last four characters in a string

I've been attempting a coding exercise to mask all but the last four digits or characters of any input.

I think my solution works but it seems a bit clumsy. Does anyone have ideas about how to refactor it?

Here's my code:

def mask(string)
  z = string.to_s.length

  if z <= 4
    return string
  elsif z > 4
    array = []
    string1 = string.to_s.chars

    string1[0..((z-1)-4)].each do |s|
      array << "#"
    end

    array << string1[(z-4)..(z-1)]

    puts array.join(", ").delete(", ").inspect     
  end
end
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

positive lookahead

A positive lookahead makes it pretty easy. If any character is followed by at least 4 characters, it gets replaced :

"654321".gsub(/.(?=.{4})/,'#')
# "##4321"

Here's a description of the regex :

r = /
     .        # Just one character
     (?=      # which must be followed by
        .{4}  # 4 characters
     )        #
    /x        # free-spacing mode, allows comments inside regex

Note that the regex only matches one character at a time, even though it needs to check up to 5 characters for each match :

"654321".scan(r)
# => ["6", "5"]

/(.)..../ wouldn't work, because it would consume 5 characters for each iteration :

"654321".scan(/(.)..../)
# => [["6"]]
"abcdefghij".scan(/(.)..../)
# => [["a"], ["f"]]

If you want to parametrize the length of the unmasked string, you can use variable interpolation :

all_but = 4
/.(?=.{#{all_but}})/
# => /.(?=.{4})/

Code

Packing it into a method, it becomes :

def mask(string, all_but = 4, char = '#')
  string.gsub(/.(?=.{#{all_but}})/, char)
end

p mask('testabcdef')
# '######cdef'
p mask('1234')
# '1234'
p mask('123')
# '123'
p mask('x')
# 'x'

You could also adapt it for sentences :

def mask(string, all_but = 4, char = '#')
  string.gsub(/w(?=w{#{all_but}})/, char)
end

p mask('It even works for multiple words')
# "It even #orks for ####iple #ords"

Some notes about your code

string.to_s

Naming things is very important in programming, especially in dynamic languages.

string.to_s

If string is indeed a string, there shouldn't be any reason to call to_s.

If string isn't a string, you should indeed call to_s before gsub but should also rename string to a better description :

object.to_s
array.to_s
whatever.to_s

join

puts array.join(", ").delete(", ").inspect

What do you want to do exactly? You could probably just use join :

[1,2,[3,4]].join(", ").delete(", ")
# "1234"
[1,2,[3,4]].join
# "1234"

delete

Note that .delete(", ") deletes every comma and every whitespace, in any order. It doesn't only delete ", " substrings :

",a b,,,   cc".delete(', ')
# "abcc"
["1,2", "3,4"].join(', ').delete(', ')
# "1234"

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...