'JS Find the missing letter (alphabet - array)

Write a function that takes an array of consecutive (increasing) letters as input and that returns the missing letter in the array. It will be always exactly one letter be missing. The array will always contain letters in only one case. Example:

["a","b","c","d","f"] -> "e"
["O","Q","R","S"] -> "P"

Why don't my functions work?

function findMissingLetter(array)
{
  let alphabetArr = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
  let alphabetSlice = alphabetArr.slice(alphabetArr.indexOf(array[0]), alphabetArr.indexOf(array[array.length - 1]) + 1);

    let missingLetter = alphabetSlice.forEach((e, i) => {
         if (e !== array[i]) {
            return e;
        }
    });

return missingLetter;
}


function findMissingLetter(array)
{
  let alphabetArr = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
  let alphabetSlice = alphabetArr.slice(alphabetArr.indexOf(array[0]), alphabetArr.indexOf(array[array.length - 1]) + 1);

    let missingLetter = alphabetSlice.map((e, i) => {
         if (e !== array[i]) {
            return e;
        }
    })[0];

return missingLetter;
}


Solution 1:[1]

For searching for one element in an array use for loop, like this:

for(let i = 0; i < array.length; i++) {
    if(array[i] !== alphabetSlice[i])
        return alphabetSlice[i];
}

In this approach you will search for first difference between two arrays. And the difference will be your missing letter :)


Why map doesn't work in this example?

map function takes data and replaces it with "re-arranged" data. This:

alphabetSlice.map((e, i) => {
    if (e !== array[i]) {
        return e;
    }
}); // for your example will create array 

will create [undefined, undefined, undefined, undefined, 'e', 'f'], so the first element is undefined.

Why the result array has undefined?

Because you have return statement in if. So when the expression in if is false, you won't return any value, so the value is undefined.

More about map you can read on mdn.

Solution 2:[2]

Actually just replace forEach with find and change what you're returning, and it will do what's needed.

function findMissingLetter(array)
{
    let alphabetArr = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
    let alphabetSlice = alphabetArr.slice(alphabetArr.indexOf(array[0]), alphabetArr.indexOf(array[array.length - 1]) + 1);

    let missingLetter = alphabetSlice.find((e, i) => e !== array[i]);

    return missingLetter;
}

console.log(findMissingLetter(["a","b","c","d","f"]))
console.log(findMissingLetter(["O","Q","R","S"]))

Array.prototype.forEach() executes a provided function once for each array element.

Array.prototype.find() returns the value of the first element in the provided array that satisfies the provided testing function.

Solution 3:[3]

There is no way to return from a forEach loop before it iterates through each element. And forEach always returns undefined.

You can find the letter with const missingLetter = alphabetSlice.find((e, i) => e !== array[i]);

Solution 4:[4]

  1. Iterate over the array from 0 till length - 1
  2. At each iteration, get the UTF-16 code of the current and next characters using String#charCodeAt, which should have a difference of 1 if they are after each other (given that they're of the same case)
  3. If the code of the next element doesn't equal to the current one + 1, compute the missing character using String#fromCharCode on current code + 1

const findMissingLetter = (array = []) => {
  for(let i = 0; i < array.length - 1; i++) {
    const current = array[i].charCodeAt(), next = array[i+1].charCodeAt();
    const expected = current+1;
    if(next !== expected) {
      return String.fromCharCode(expected);
    }
  }
}

console.log( findMissingLetter(["a","b","c","d","f"]) );
console.log( findMissingLetter(["O","Q","R","S"]) );

Solution 5:[5]

The answers and comments already described, why it's not working and why you are getting an unexpected result. I just wanna show a different way to solve this.

You can make use of the fact, that the unicode chars are already sorted and increasing the code point of any value by one, will give you the following letter/char/number.

All you have to do is getting the code point from the lowest and the highest value in your array and test if any char inbetween is inside the array you are testing.

Using this method, you can test within any range, if a letter/char/emoji is missing in the passed list.

function missingChars(arr) {
  if (Array.isArray(arr)) {
    // generate a sorted copy of the array passed as argument
    arr = arr.slice().sort();
    // length won't be 1 on emojis and so on, so just test for > 0
    if (!arr.every(val => (typeof val == 'string' || val instanceof String) && val.length)) {
        throw new Error("Every value must be a string with length > 0.");
    }
    // lowest item will be at pos 0 after sort()
    const first = arr[0].codePointAt(0),
      // highest item will be at the last position
      last = arr[arr.length-1].codePointAt(0),
      resultArr = [];
    // for every char in between the lowest and the highest
    for (let i = first; i < last;i++) {
      // create a string using his code point value
      const charToTest = String.fromCodePoint(i);
      // test if it is inside the array
      if (!arr.includes(charToTest)) {
        // if not, add it to  the result
        resultArr.push(charToTest);
      }
    }
    return resultArr;
  }
  return null;
}

console.log(missingChars(["?", "?"])); // ['?', '?', '?', '?", '?', '?']
console.log(missingChars(["e", "c", "a"])); // ['b', 'd']
console.log(missingChars(["0", "2"])); // ['1']
console.log(missingChars(["a", "b"])); // []

// ascii range
console.log(missingChars(['\u0000', '\u00FF'])); 

console.log(missingChars(['', 'a'])); // throws error

Solution 6:[6]

Array.forEach does not return anything - like others already answered. forEach just performs a (lambda) function on all elements of an array.

You may want to inspect Array.find, as also noticed by others.

Here's a bit more generic solution (check missing from alfabet or some other given string):

const findFirstMissing = (arr, from) => [...from
  .slice(from.indexOf(arr[0]), from.indexOf(arr[arr.length - 1]) + 1) ]
  .find((v, i) => v !== arr[i]) || `complete`;

let alfabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
console.log(findFirstMissing(["a", "b", "c", "d", "f"], alfabet));
console.log(findFirstMissing(["O", "Q", "R", "S"], alfabet));

let someRowOfCharacters = `Charlotte`;
console.log(findFirstMissing(`Charltte`.split(``), someRowOfCharacters));
console.log(findFirstMissing(`Charlotte`.split(``), someRowOfCharacters));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Christopher
Solution 2
Solution 3 Nikita Skrebets
Solution 4
Solution 5 Christopher
Solution 6