1
2 Comments

How to suggest email typo corrections?

I have forms that get 100s of submissions with peoples name and email every day. They all get a confirmation email after submitting.

I'm surprised lot of people can't seem to spell their own email address: almost 1% of the submissions have a non-existing email address because people make a typo. Think joohn.doe@gmail.com instead of john.doe@gmail.com.

I added mailcheck to check typos in popular domains like gmail.com.
That helps, although there are still people who make mistakes this way.

However, most of the non-existing emails are people who misspell their name, like joohn.doe@gmail.com. Since they also enter their name, I could suggest to John Doe that his email might be john.doe@gmail.com instead.

Does anything exist for this yet?
If not, how could I implement something like this?

Thanks!

on October 11, 2020
  1. 2

    I don't know anything doing this.

    But as you have the name asked in the same form, you can try to search for it in the email address and compute the distance between substrings of the email address and the name.

    If you find a word that is only one letter away, just warn the user about a possible typo.

    But I would not block the form submission, just a small warning.

  2. 1

    I put something quick and dirty together that seems to be doing the trick:

    const splitCharsRegex = /[_. ,-]/;
    const split = (str) => str.split(splitCharsRegex).map(s => s.toLowerCase());
    
    // Source: https://gist.github.com/andrei-m/982927/0efdf215b00e5d34c90fdc354639f87ddc3bd0a5
    const levenshteinDistance = function(a, b) {
    	if(a.length == 0) return b.length;
    	if(b.length == 0) return a.length;
    
    	// swap to save some memory O(min(a,b)) instead of O(a)
    	if(a.length > b.length) {
    		var tmp = a;
    		a = b;
    		b = tmp;
    	}
    
    	var row = [];
    	// init the row
    	for(var i = 0; i <= a.length; i++){
    		row[i] = i;
    	}
    
    	// fill in the rest
    	for(var i = 1; i <= b.length; i++){
    		var prev = i;
    		for(var j = 1; j <= a.length; j++){
    			var val;
    			if(b.charAt(i-1) == a.charAt(j-1)){
    				val = row[j-1]; // match
    			} else {
    				val = Math.min(row[j-1] + 1, // substitution
    						prev + 1,     // insertion
    						row[j] + 1);  // deletion
    			}
    			row[j - 1] = prev;
    			prev = val;
    		}
    		row[a.length] = prev;
    	}
    
    	return row[a.length];
    }
    
    // Source: https://stackoverflow.com/questions/40654895/javascript-generating-all-permutations-of-an-array
    const combo = (c) => {
    	let r = [],
    			len = c.length,
    			tmp = [];
    
    	function nodup() {
    		var got = {};
    		for (var l = 0; l < tmp.length; l++) {
    			if (got[tmp[l]]) return false;
    			got[tmp[l]] = true;
    		}
    		return true;
    	}
    	function iter(col) {
    		let l, rr;
    		if (col === len) {
    			if (nodup()) {
    				rr = [];
    				for (l = 0; l < tmp.length; l++)
    					rr.push(c[tmp[l]]);
    				r.push(rr);
    			}
    		} else {
    			for (l = 0; l < len; l ++) {
    				tmp[col] = l;
    				iter(col +1);
    			}
    		}
    	}
    	iter(0);
    	return r;
    }
    
    const getSuggestion = (email = '', name = '') => {
    	const username = email.split('@')[0];
        const emailWords = split(username) || [];
        const nameWords = split(name) || [];
    
        // Check complete username <> name, in different orders
    	if (!username.match(splitCharsRegex)) {
    		const allPossibleOrdersOfNameWords = combo(nameWords);
    		for (let words of allPossibleOrdersOfNameWords) {
    			if (levenshteinDistance(username.toLocaleLowerCase(), words.join('')) === 1) {
    				return email.replace(username, words.join(''));
    			}
    		}
    	}
    
        // Check each part of the username
        for (let emailWord of emailWords) {
            const nameWord = nameWords.find(w => levenshteinDistance(emailWord, w) === 1);
            if (nameWord) return email.replace(emailWord, nameWord);
        }
    }
    
    console.log(getSuggestion('john.dooe@gmail.com', 'John Doe')); // returns "john.doe@gmail.com"