2

I have a question about letter combinations of a phone keypad key in JavaScript. I wrote a solution using DFS recursion. But it does not work as expected. I am new to JavaScript but similarly written code in Ruby works.

The problem is about getting all possible letter combination from a phone keypad.

Input: "23"

Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

With the code below, it stops at "af". Output is ["ad", "ae", "af"]. I am not sure why this code does not move to the second letter of "2", which is "b".

const map = {
  "2": ["a", "b", "c"],
  "3": ["d", "e", "f"],
  "4": ["g", "h", "i"],
  "5": ["j", "k", "l"],
  "6": ["m", "n", "o"],
  "7": ["p", "q", "r", "s"],
  "8": ["t", "u", "v"],
  "9": ["w", "x", "y", "z"]
};

let result = [];

let letterCombinations = function(digits) {
  if (digits.length == 0) {
    return []
  };

  let stack = [];
  dfs(digits.split(''), 0, stack)

  return result
};

function dfs(digits, index, stack) {
  const currentLetters = map[digits[index]]

  for (i = 0; i < currentLetters.length; i++) {
    stack.push(currentLetters[i])

    if (index == digits.length - 1) {
      result.push(stack.join(''))
      stack.pop()
    } else {
      dfs(digits, index + 1, stack)
      stack.pop()
    }
  }
}

console.log(letterCombinations("23"));
hindmost
  • 7,125
  • 3
  • 27
  • 39

5 Answers5

6

You need to declare i in your for loop otherwise it's global and keeps getting incremented on each recursion step.

Use for (let i = 0; i < currentLetters.length; i++)

const map = {
  "2": ["a", "b", "c"],
  "3": ["d", "e", "f"],
  "4": ["g", "h", "i"],
  "5": ["j", "k", "l"],
  "6": ["m", "n", "o"],
  "7": ["p", "q", "r", "s"],
  "8": ["t", "u", "v"],
  "9": ["w", "x", "y", "z"]
};

let result = [];

let letterCombinations = function(digits) {
  if (digits.length == 0) {
    return []
  };

  let stack = [];
  dfs(digits.split(''), 0, stack)

  return result
};

function dfs(digits, index, stack) {
  const currentLetters = map[digits[index]]
  
  // declare the loop variable!
  for (let i = 0; i < currentLetters.length; i++) {
    stack.push(currentLetters[i])

    if (index == digits.length - 1) {
      result.push(stack.join(''))
      stack.pop()
    } else {
      dfs(digits, index + 1, stack)
      stack.pop()
    }
  }
}

console.log(letterCombinations("23"));
Mark
  • 90,562
  • 7
  • 108
  • 148
  • Wow thanks so much! Is declaration of i pretty much the norm when writing a for loop in JS? – Youngsun Paik Dec 18 '18 at 23:39
  • Yes, @YoungsunPaik, you almost always want to declare it so you don't pollute the global space and so don't share it between loops. – Mark Dec 18 '18 at 23:41
0

Here is a less complex implementation. I hope you find it useful!

const map = {
  "2": ["a", "b", "c"],
  "3": ["d", "e", "f"],
  "4": ["g", "h", "i"],
  "5": ["j", "k", "l"],
  "6": ["m", "n", "o"],
  "7": ["p", "q", "r", "s"],
  "8": ["t", "u", "v"],
  "9": ["w", "x", "y", "z"]
};

function letterCombinations(digits) {
  digits = digits.split('');
  
  const firstArray = map[digits[0]];
  const secondArray = map[digits[1]];
  const result = [];
  
  for (let i = 0; i < firstArray.length; i++)
  {
    for (let j = 0; j < secondArray.length; j++)
    {
      result.push(firstArray[i] + secondArray[j]);
    }
  }
  
  return result;
};

console.log(letterCombinations("23"));
Alex Bntz
  • 124
  • 9
  • 1
    It's less complex because, unlike the OP's code, it only works for input of exactly two numbers. – Mark Dec 18 '18 at 22:15
0

const DailNumbers = {
  2: ['a', 'b', 'c'],
  3: ['d', 'e', 'f'],
  4: ['g', 'h', 'i'],
  5: ['j', 'k', 'l'],
  6: ['m', 'n', 'o'],
  7: ['p', 'q', 'r', 's'],
  8: ['t', 'u', 'v'],
  9: ['w', 'x', 'y', 'z'],
}


const LoopNum =(arg, TeleNumbers)=>{
  
  // Number to String conversion and splitting the values
  let splitnum = arg.toString().split("");
  
  // If No values Just pass empty array
  if(splitnum.length < 1) return []
  
  const combinedArray = splitnum.map( (val) => TeleNumbers[val]);
  const temp = []; 
  
   // combined array is greater than one value
  if (combinedArray [1]) {
      for(let i = 0; i < combinedArray[0].length; i++){  
        for(let j = 0; j < combinedArray[1].length; j++){
          temp.push(combinedArray[0][i] +""+ combinedArray[1][j])
        }
      }
    }
  
  // combined array is greater than one value
  else {
    for(let i = 0; i < combinedArray[0].length; i++){    
          temp.push(combinedArray[0][i])
      } 
  }

  return temp
}
console.log(LoopNum('23', DailNumbers)) 

// result will ["ad", "ae", "af", "bd", "be", "bf","cd", "ce", "cf"]
caesar
  • 1
  • Welcome to StackOverflow! Code-only answers are not nearly as useful as ones that also include some explanatory text. Please [edit] to add more context. – Scott Sauyet Aug 17 '22 at 12:45
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 21 '22 at 09:39
0

Letter combination of a phone number(keypad key) Javascript Solution

Create a phone keypad dictionary first.

Combine each digit from the dictionary array using nested loops.

/**
 * @param {string} digits "23"
 * @return {string[]} ["ad","ae","af","bd","be","bf","cd","ce","cf"]
 */
var letterCombinations = function(digits) {
    let tel = {
        2:['a','b','c'],
        3:['d','e','f'],
        4:['g','h','i'],
        5:['j','k','l'],
        6:['m','n','o'],
        7:['p','q','r','s'],
        8:['t','u','v'],
        9:['w','x','y','z']
    }, arr1=tel[digits[0]],arr2=tel[digits[1]],array=[];
    if(!digits.trim().length){
        return [];
    }
    if(digits.length == 1){
        return tel[digits];
    }
    for(let d=0;d<digits.length-1;d++) {
        arr1 = array.length ? array : tel[digits[d]];
        arr2 = tel[digits[d+1]];
        if(array.length){
            array =[];
        }
        if(digits[d+1]) {
            for(let i=0;i<arr1.length;i++) {
                for(let j=0;j<arr2.length;j++) {
                    array.push(arr1[i]+arr2[j])
                }
            }
        } else {
            return array;
        }

    }
    return array;
    
};
Ashish
  • 2,026
  • 17
  • 19
0

Since this old question has been resurrected, here's an easier recursive version:

const allNbrs = (chars) => ([c, ...cs]) =>
  c == undefined
    ? ['']
    : allNbrs (chars) (cs) .flatMap (s => (chars [c] || [c]) .map (c => c + s))

const tel = {2: ['a', 'b', 'c'], 3: ['d', 'e', 'f'], 4: ['g', 'h', 'i'], 5: ['j', 'k', 'l'], 6: ['m', 'n', 'o'], 7: ['p', 'q', 'r', 's'], 8: ['t', 'u', 'v'], 9: ['w', 'x', 'y', 'z']}

const letterCombinations = allNbrs (tel)

console .log (letterCombinations ('23'))

We separate the first letter of the string (c) from the remaining ones (cs), recur on those remaining ones, then, for every result, we combine it with each entry from our table (chars).

Note that (chars || [c]) .map (...) is used for those cases in which the character supplied is no in our table. We could alternately do (chars || []) .map (...), which would return an empty array if we had any bad characters. Just chars .map (...) would throw an error if passed a zero. I prefer the version above, as it gives an interesting answer for, say, 304:

letterCombinations ('304')
//=> ["d0w","e0w","f0w","d0x","e0x","f0x","d0y","e0y","f0y","d0z","e0z","f0z"]
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103