[Javascript Regex] Recommending Alternative ID

Task Requirements

The Rules for the ID

Length of the ID must be 3 to 15 characters.

Only alphanumeric(lowercase) characters and [ -] , [ _ ], [ . ] are allowed.

[ . ] can not be used at the beginning and the end, and cannot be used consecutively.

Step 1. Change all the uppercase letters to lowercase.
Step 2. Remove all 'not allowed' characters.
Step 3. Replace all consecutive periods to a single period.
Step 4. Remove periods at the beginning and at the end.
Step 5. Fill the ID with a, if the above steps return an empty string.
Step 6. Cut the ID in to 15 characters if longer, and if necessary, repeat step 4.
Step 7. Repeat the last character at the end of the ID, if too short.

Comparing My Solution with the Regex Solution

Step 1.

Both my code and the regex code had the same solution for step 1, which was very obvious.

Code

let s1 = new_id.toLowerCase();

Step 2.

My Code

let s2 = "";
for(let i = 0; i < s1.length; i++){
    if (s1[i].match(/^[A-Za-z0-9\-\.\_]+$/)){
        s2 += s1[i]
    }
}

I knew that using regex at this point was obvious, but wasn’t sure how exactly.

The method I chose was to loop through every letter to compare it to the regex of allowed characters, which was then added to the second string.

Regex Code

.replace(/[^\w-_.]/g, '')

Apparently, ^ inside and in front of character set, was called “negated character set”. This meant that above expression was saying “from the answer string, replace all charcters other than those in the set with empty value(other words, remove)”

Step 3.

My Code

let re = /\.\./
do{
    s2 = s2.replace(re,".")
} while (s2.match(re));

After a long time of researching, I couldn’t get the regex working better for me. So I replaced all double dots with a single dot, until there was none left in the string.

Regex Code

.replace(/\.+/g, '.')

My attempt was seemed rather foolish after seeing this code. By adding the plus sign at the end it refers to all the repetitions of the period, which will be replaced by a single period. Additionally, by using the g flag(/g), this is done to all the matches found in the string.

Step 4.

My Code

let s4 = (s2[0] == ".") ? s2.slice(1) : s2
s4 = (s4.slice(-1) == ".") ? s4.slice(0, -1) : s4

I split the process into two, each for slicing out the period at the beginning and the end.

Regex Code

.replace(/^\.|\.$/g, '')

The above expression refers to the period at the beginning or the end, which will be removed.

Step 5.

My Code

if (s4 == ""){s4 = 'a'}

My code was also quite simple for this step.

Regex Code

.replace(/^$/, 'a')

This blew my mind. The above expression refers to a string that begins and ends with nothing, in other words an empty string. If there is any character in the answer until now, this will replace nothing, but if it is empty, it will be filled with a.

Step 6.

My code

let s6 = (s4.length > 15) ? s4.slice(0, 15) : s4
s6 = (s6.slice(-1) == ".") ? s6.slice(0, -1) : s6

The first line checks if it is over 15 characters, and the second line removes the possible period at the end.

Regex Code

.slice(0, 15).replace(/\.$/, '');

The Regex code simply slices the answer into 15 characters (which I didn’t know you could do), and repeats the replacement similar to Step 4.

Step 7.

My Code

let s7 = s6;
while(s7.length < 3){
    s7 += s7.slice(-1)
}

I check if the string is shorter than 3 and if so, adds the last letter at the end until it is so.

Regex Code

const len = answer.length;
return len > 2 ? answer : answer + answer.charAt(len - 1).repeat(3 - len);

This is not much of regex code, and has basically the same logic as mine, but by using repeat, the process is much simpler and shorter.

The Final Codes

My Code

function solution(new_id) {
    let s1 = new_id.toLowerCase();
    let s2 = "";
    for(let i = 0; i < s1.length; i++){
        if (s1[i].match(/^[A-Za-z0-9\-\.\_]+$/)){
            s2 += s1[i]
        }
    }

    let re = /\.\./
    do{
        s2 = s2.replace(re,".")
    } while (s2.match(re));

    let s4 = (s2[0] == ".") ? s2.slice(1) : s2
    s4 = (s4.slice(-1) == ".") ? s4.slice(0, -1) : s4

    if (s4 == ""){
        s4 = 'a'
    }

    let s6 = (s4.length > 15) ? s4.slice(0, 15) : s4

    s6 = (s6.slice(-1) == ".") ? s6.slice(0, -1) : s6

    let s7 = s6;
    while(s7.length < 3){
        s7 += s7.slice(-1)
    }

    let answer = s7;
    return answer;
}

Regex Code

function solution(new_id) {
const answer = new_id
    .toLowerCase() // 1
    .replace(/[^\w-_.]/g, '') // 2
    .replace(/\.+/g, '.') // 3
    .replace(/^\.|\.$/g, '') // 4
    .replace(/^$/, 'a') // 5
    .slice(0, 15).replace(/\.$/, ''); // 6
const len = answer.length;
return len > 2 ? answer : answer + answer.charAt(len - 1).repeat(3 - len);

I should definately learn more Regex.

[The Regex Version Code belongs to Alex_choi on programmers]