Find all letter Combinations of a Phone NumberAlgorithm to Iterate All Possible Strings in ClojureLetter...

Is Dumbledore a human lie detector?

Confused with atmospheric pressure equals plastic balloon’s inner pressure

What are the unintended or dangerous consequences of allowing spells that target and damage creatures to also target and damage objects?

Can you make an identity from this product?

Why is the length of the Kelvin unit of temperature equal to that of the Celsius unit?

Rail-to-rail op-amp only reaches 90% of VCC, works sometimes, not everytime

How to get depth and other lengths of a font?

Why is long-term living in Almost-Earth causing severe health problems?

What is the reason for setting flaps 1 on the ground at high temperatures?

Should I refuse to be named as co-author of a low quality paper?

noalign caused by multirow and colors

Proving that a Russian cryptographic standard is too structured

Are the guests in Westworld forbidden to tell the hosts that they are robots?

If someone intimidates another person, does the person affected gain the Frightened condition?

Multiband vertical antenna not working as expected

Remove border lines of SRTM tiles rendered as hillshade

Find all letter Combinations of a Phone Number

Make Gimbap cutter

Three questions

Do you have to have figures when playing D&D?

Seasonality after 1st differencing

As easy as Three, Two, One... How fast can you go from Five to Four?

Grep Match and extract

ASCII Meme Arrow Generator



Find all letter Combinations of a Phone Number


Algorithm to Iterate All Possible Strings in ClojureLetter combinations of phone dial pad numberLeetcode 17. Letter Combinations of a Phone NumberFind number of combinations of stringsListing all possible names from a phone numberDecode the Morse CodeFinding all possible letter combinations from an inputted phone numberPrint out all possible letter combinations a given phone number can representFind index of the nearest larger number of a numberFind the smallest distance between any two given words in a string






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







2












$begingroup$


The task



is taken from LeetCode




Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. A
mapping of digit to letters (just like on the telephone buttons) is
given below. Note that 1 does not map to any letters.



enter image description here



Example:



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


Note:



Although the above answer is in lexicographical order, your answer could be in any order you want.




My solution



(I've been told I should provide additional information to my solution otherwise I get downvoted.)



is based on backtracking. Why I choosed that approach? Well, whenever you encounter a task where you have to combine or permutate things, then backtracking is a possible approach.



But if you know a better one, then please go ahead. Otherwise I only got generic questions to my code: Can you make it faster, cleaner, more readable, etc.?



Also I'm interested in functional programming. So, if you got a good functional approach, feel free to post it here, too. Other than that, I'm always interested in a different perspective. So, if you got a fancy or conservative approach, please feel free to post them here.



Imperative approach



/**
* @param {string} digits
* @return {string[]}
*/
var letterCombinations = function(digits) {
if (digits === '') { return []; }
const strDigits = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz',
};

if (digits.length === 1) { return [...strDigits[digits]]; }
const res = [];
const combine = (cur, n) => {
if (cur.length === digits.length) {
res.push(cur);
return;
}

[...strDigits[digits[n]]].forEach(x => {
combine(cur + x, n + 1);
});

};

combine('', 0);
return res;
};


Functional approach



/**
* @param {string} digits
* @return {string[]}
*/
var letterCombinations = function(digits) {
if (digits === '') { return []; }

const strDigits = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz',
};

const combine = (cur, n) => cur.length === digits.length
? cur
: [...strDigits[digits[n]]].flatMap(x => combine(cur + x, n + 1));

return combine('', 0);
};









share|improve this question











$endgroup$



















    2












    $begingroup$


    The task



    is taken from LeetCode




    Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. A
    mapping of digit to letters (just like on the telephone buttons) is
    given below. Note that 1 does not map to any letters.



    enter image description here



    Example:



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


    Note:



    Although the above answer is in lexicographical order, your answer could be in any order you want.




    My solution



    (I've been told I should provide additional information to my solution otherwise I get downvoted.)



    is based on backtracking. Why I choosed that approach? Well, whenever you encounter a task where you have to combine or permutate things, then backtracking is a possible approach.



    But if you know a better one, then please go ahead. Otherwise I only got generic questions to my code: Can you make it faster, cleaner, more readable, etc.?



    Also I'm interested in functional programming. So, if you got a good functional approach, feel free to post it here, too. Other than that, I'm always interested in a different perspective. So, if you got a fancy or conservative approach, please feel free to post them here.



    Imperative approach



    /**
    * @param {string} digits
    * @return {string[]}
    */
    var letterCombinations = function(digits) {
    if (digits === '') { return []; }
    const strDigits = {
    '2': 'abc',
    '3': 'def',
    '4': 'ghi',
    '5': 'jkl',
    '6': 'mno',
    '7': 'pqrs',
    '8': 'tuv',
    '9': 'wxyz',
    };

    if (digits.length === 1) { return [...strDigits[digits]]; }
    const res = [];
    const combine = (cur, n) => {
    if (cur.length === digits.length) {
    res.push(cur);
    return;
    }

    [...strDigits[digits[n]]].forEach(x => {
    combine(cur + x, n + 1);
    });

    };

    combine('', 0);
    return res;
    };


    Functional approach



    /**
    * @param {string} digits
    * @return {string[]}
    */
    var letterCombinations = function(digits) {
    if (digits === '') { return []; }

    const strDigits = {
    '2': 'abc',
    '3': 'def',
    '4': 'ghi',
    '5': 'jkl',
    '6': 'mno',
    '7': 'pqrs',
    '8': 'tuv',
    '9': 'wxyz',
    };

    const combine = (cur, n) => cur.length === digits.length
    ? cur
    : [...strDigits[digits[n]]].flatMap(x => combine(cur + x, n + 1));

    return combine('', 0);
    };









    share|improve this question











    $endgroup$















      2












      2








      2





      $begingroup$


      The task



      is taken from LeetCode




      Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. A
      mapping of digit to letters (just like on the telephone buttons) is
      given below. Note that 1 does not map to any letters.



      enter image description here



      Example:



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


      Note:



      Although the above answer is in lexicographical order, your answer could be in any order you want.




      My solution



      (I've been told I should provide additional information to my solution otherwise I get downvoted.)



      is based on backtracking. Why I choosed that approach? Well, whenever you encounter a task where you have to combine or permutate things, then backtracking is a possible approach.



      But if you know a better one, then please go ahead. Otherwise I only got generic questions to my code: Can you make it faster, cleaner, more readable, etc.?



      Also I'm interested in functional programming. So, if you got a good functional approach, feel free to post it here, too. Other than that, I'm always interested in a different perspective. So, if you got a fancy or conservative approach, please feel free to post them here.



      Imperative approach



      /**
      * @param {string} digits
      * @return {string[]}
      */
      var letterCombinations = function(digits) {
      if (digits === '') { return []; }
      const strDigits = {
      '2': 'abc',
      '3': 'def',
      '4': 'ghi',
      '5': 'jkl',
      '6': 'mno',
      '7': 'pqrs',
      '8': 'tuv',
      '9': 'wxyz',
      };

      if (digits.length === 1) { return [...strDigits[digits]]; }
      const res = [];
      const combine = (cur, n) => {
      if (cur.length === digits.length) {
      res.push(cur);
      return;
      }

      [...strDigits[digits[n]]].forEach(x => {
      combine(cur + x, n + 1);
      });

      };

      combine('', 0);
      return res;
      };


      Functional approach



      /**
      * @param {string} digits
      * @return {string[]}
      */
      var letterCombinations = function(digits) {
      if (digits === '') { return []; }

      const strDigits = {
      '2': 'abc',
      '3': 'def',
      '4': 'ghi',
      '5': 'jkl',
      '6': 'mno',
      '7': 'pqrs',
      '8': 'tuv',
      '9': 'wxyz',
      };

      const combine = (cur, n) => cur.length === digits.length
      ? cur
      : [...strDigits[digits[n]]].flatMap(x => combine(cur + x, n + 1));

      return combine('', 0);
      };









      share|improve this question











      $endgroup$




      The task



      is taken from LeetCode




      Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. A
      mapping of digit to letters (just like on the telephone buttons) is
      given below. Note that 1 does not map to any letters.



      enter image description here



      Example:



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


      Note:



      Although the above answer is in lexicographical order, your answer could be in any order you want.




      My solution



      (I've been told I should provide additional information to my solution otherwise I get downvoted.)



      is based on backtracking. Why I choosed that approach? Well, whenever you encounter a task where you have to combine or permutate things, then backtracking is a possible approach.



      But if you know a better one, then please go ahead. Otherwise I only got generic questions to my code: Can you make it faster, cleaner, more readable, etc.?



      Also I'm interested in functional programming. So, if you got a good functional approach, feel free to post it here, too. Other than that, I'm always interested in a different perspective. So, if you got a fancy or conservative approach, please feel free to post them here.



      Imperative approach



      /**
      * @param {string} digits
      * @return {string[]}
      */
      var letterCombinations = function(digits) {
      if (digits === '') { return []; }
      const strDigits = {
      '2': 'abc',
      '3': 'def',
      '4': 'ghi',
      '5': 'jkl',
      '6': 'mno',
      '7': 'pqrs',
      '8': 'tuv',
      '9': 'wxyz',
      };

      if (digits.length === 1) { return [...strDigits[digits]]; }
      const res = [];
      const combine = (cur, n) => {
      if (cur.length === digits.length) {
      res.push(cur);
      return;
      }

      [...strDigits[digits[n]]].forEach(x => {
      combine(cur + x, n + 1);
      });

      };

      combine('', 0);
      return res;
      };


      Functional approach



      /**
      * @param {string} digits
      * @return {string[]}
      */
      var letterCombinations = function(digits) {
      if (digits === '') { return []; }

      const strDigits = {
      '2': 'abc',
      '3': 'def',
      '4': 'ghi',
      '5': 'jkl',
      '6': 'mno',
      '7': 'pqrs',
      '8': 'tuv',
      '9': 'wxyz',
      };

      const combine = (cur, n) => cur.length === digits.length
      ? cur
      : [...strDigits[digits[n]]].flatMap(x => combine(cur + x, n + 1));

      return combine('', 0);
      };






      javascript algorithm programming-challenge functional-programming






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 8 hours ago







      thadeuszlay

















      asked 8 hours ago









      thadeuszlaythadeuszlay

      1,612718




      1,612718






















          2 Answers
          2






          active

          oldest

          votes


















          3












          $begingroup$


          whenever you encounter a task where you have to combine or permute things, then backtracking is a possible approach.




          True. It is not necessarily the best though (in fact it is rarely the best).



          In this particular case, the problem naturally maps to finding the next combination. In turn, it is no more than an increment of a number in some fancy numbering system. In your example, the minimal possible string is ad. Subsequent increments yield ae, then af, then ('f' + 1 is d and a carry bit does carry) bd, etc.



          Consider implementing the increment/next method directly. The space complexity will definitely benefit; and it is trivial to convert into a generator. The time complexity is likely to also benefit, depending on the use case.



          PS: thank you for posting your train of thoughts.






          share|improve this answer









          $endgroup$





















            0












            $begingroup$

            I think your functional approach is very nice, and scores high points for readability. As long as the recursion wasn't causing performance issues, that's how I'd approach it.



            However, it's not very efficient, and as vnp points out, listing permutations is really just a matter of counting from 1 to "the number of combinations" in a mixed base numbering system.



            mixed base counting



            These are easy implement, and it's worth going through the exercise, because assuming you have a utility that does the mixed base counting for you, the solution to the original problem will be both straightforward and efficient:



            function mixedBaseCounter(bases) {
            let cnt = 0
            let maxCnt = bases.length ? [...bases].reduce((m, x) => m * x, 1) : 0
            let digits = bases.map(() => 0)

            const increment = (i = 0) => {
            digits[i] = (digits[i] + 1) % bases[i]
            if (digits[i] == 0) increment(i + 1)
            }

            return {
            [Symbol.iterator]: function* () {
            while (cnt++ < maxCnt) {
            yield digits.join('')
            increment()
            }
            }
            }
            }


            This uses ECMA script's iterable interface. Note the above implementation has the least significant bit on the left (easily changed, if you need to).



            Let's verify it counts correctly in binary:



            [...mixedBaseCounter([2, 2, 2])]
            // [ '000', '100', '010', '110', '001', '101', '011', '111' ]


            And that it handles mixed bases:



            console.log([...mixedBaseCounter([2, 3])])
            // [ '00', '10', '01', '11', '02', '12' ]


            Try it online!



            applying it to the problem



            Now the solutions becomes:



            function letterCombinations(digits) {

            const strDigits = {
            '2': 'abc',
            '3': 'def',
            '4': 'ghi',
            '5': 'jkl',
            '6': 'mno',
            '7': 'pqrs',
            '8': 'tuv',
            '9': 'wxyz',
            }

            const letterOptions = [...digits].map(x => strDigits[x])
            const bases = [...letterOptions].map(x => x.length)
            const masks = mixedBaseCounter(bases)

            return [...masks].map(m =>
            [...m].map((x, i) => letterOptions[i][x]).join('')
            )
            }


            where each "mask" (or number, within the mixed base numbering system) chooses one combination.



            Note also we no longer need to treat the empty string as a special case.



            Try it online!





            share









            $endgroup$














              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "196"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: false,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: null,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f221976%2ffind-all-letter-combinations-of-a-phone-number%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              3












              $begingroup$


              whenever you encounter a task where you have to combine or permute things, then backtracking is a possible approach.




              True. It is not necessarily the best though (in fact it is rarely the best).



              In this particular case, the problem naturally maps to finding the next combination. In turn, it is no more than an increment of a number in some fancy numbering system. In your example, the minimal possible string is ad. Subsequent increments yield ae, then af, then ('f' + 1 is d and a carry bit does carry) bd, etc.



              Consider implementing the increment/next method directly. The space complexity will definitely benefit; and it is trivial to convert into a generator. The time complexity is likely to also benefit, depending on the use case.



              PS: thank you for posting your train of thoughts.






              share|improve this answer









              $endgroup$


















                3












                $begingroup$


                whenever you encounter a task where you have to combine or permute things, then backtracking is a possible approach.




                True. It is not necessarily the best though (in fact it is rarely the best).



                In this particular case, the problem naturally maps to finding the next combination. In turn, it is no more than an increment of a number in some fancy numbering system. In your example, the minimal possible string is ad. Subsequent increments yield ae, then af, then ('f' + 1 is d and a carry bit does carry) bd, etc.



                Consider implementing the increment/next method directly. The space complexity will definitely benefit; and it is trivial to convert into a generator. The time complexity is likely to also benefit, depending on the use case.



                PS: thank you for posting your train of thoughts.






                share|improve this answer









                $endgroup$
















                  3












                  3








                  3





                  $begingroup$


                  whenever you encounter a task where you have to combine or permute things, then backtracking is a possible approach.




                  True. It is not necessarily the best though (in fact it is rarely the best).



                  In this particular case, the problem naturally maps to finding the next combination. In turn, it is no more than an increment of a number in some fancy numbering system. In your example, the minimal possible string is ad. Subsequent increments yield ae, then af, then ('f' + 1 is d and a carry bit does carry) bd, etc.



                  Consider implementing the increment/next method directly. The space complexity will definitely benefit; and it is trivial to convert into a generator. The time complexity is likely to also benefit, depending on the use case.



                  PS: thank you for posting your train of thoughts.






                  share|improve this answer









                  $endgroup$




                  whenever you encounter a task where you have to combine or permute things, then backtracking is a possible approach.




                  True. It is not necessarily the best though (in fact it is rarely the best).



                  In this particular case, the problem naturally maps to finding the next combination. In turn, it is no more than an increment of a number in some fancy numbering system. In your example, the minimal possible string is ad. Subsequent increments yield ae, then af, then ('f' + 1 is d and a carry bit does carry) bd, etc.



                  Consider implementing the increment/next method directly. The space complexity will definitely benefit; and it is trivial to convert into a generator. The time complexity is likely to also benefit, depending on the use case.



                  PS: thank you for posting your train of thoughts.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 5 hours ago









                  vnpvnp

                  41.5k234106




                  41.5k234106

























                      0












                      $begingroup$

                      I think your functional approach is very nice, and scores high points for readability. As long as the recursion wasn't causing performance issues, that's how I'd approach it.



                      However, it's not very efficient, and as vnp points out, listing permutations is really just a matter of counting from 1 to "the number of combinations" in a mixed base numbering system.



                      mixed base counting



                      These are easy implement, and it's worth going through the exercise, because assuming you have a utility that does the mixed base counting for you, the solution to the original problem will be both straightforward and efficient:



                      function mixedBaseCounter(bases) {
                      let cnt = 0
                      let maxCnt = bases.length ? [...bases].reduce((m, x) => m * x, 1) : 0
                      let digits = bases.map(() => 0)

                      const increment = (i = 0) => {
                      digits[i] = (digits[i] + 1) % bases[i]
                      if (digits[i] == 0) increment(i + 1)
                      }

                      return {
                      [Symbol.iterator]: function* () {
                      while (cnt++ < maxCnt) {
                      yield digits.join('')
                      increment()
                      }
                      }
                      }
                      }


                      This uses ECMA script's iterable interface. Note the above implementation has the least significant bit on the left (easily changed, if you need to).



                      Let's verify it counts correctly in binary:



                      [...mixedBaseCounter([2, 2, 2])]
                      // [ '000', '100', '010', '110', '001', '101', '011', '111' ]


                      And that it handles mixed bases:



                      console.log([...mixedBaseCounter([2, 3])])
                      // [ '00', '10', '01', '11', '02', '12' ]


                      Try it online!



                      applying it to the problem



                      Now the solutions becomes:



                      function letterCombinations(digits) {

                      const strDigits = {
                      '2': 'abc',
                      '3': 'def',
                      '4': 'ghi',
                      '5': 'jkl',
                      '6': 'mno',
                      '7': 'pqrs',
                      '8': 'tuv',
                      '9': 'wxyz',
                      }

                      const letterOptions = [...digits].map(x => strDigits[x])
                      const bases = [...letterOptions].map(x => x.length)
                      const masks = mixedBaseCounter(bases)

                      return [...masks].map(m =>
                      [...m].map((x, i) => letterOptions[i][x]).join('')
                      )
                      }


                      where each "mask" (or number, within the mixed base numbering system) chooses one combination.



                      Note also we no longer need to treat the empty string as a special case.



                      Try it online!





                      share









                      $endgroup$


















                        0












                        $begingroup$

                        I think your functional approach is very nice, and scores high points for readability. As long as the recursion wasn't causing performance issues, that's how I'd approach it.



                        However, it's not very efficient, and as vnp points out, listing permutations is really just a matter of counting from 1 to "the number of combinations" in a mixed base numbering system.



                        mixed base counting



                        These are easy implement, and it's worth going through the exercise, because assuming you have a utility that does the mixed base counting for you, the solution to the original problem will be both straightforward and efficient:



                        function mixedBaseCounter(bases) {
                        let cnt = 0
                        let maxCnt = bases.length ? [...bases].reduce((m, x) => m * x, 1) : 0
                        let digits = bases.map(() => 0)

                        const increment = (i = 0) => {
                        digits[i] = (digits[i] + 1) % bases[i]
                        if (digits[i] == 0) increment(i + 1)
                        }

                        return {
                        [Symbol.iterator]: function* () {
                        while (cnt++ < maxCnt) {
                        yield digits.join('')
                        increment()
                        }
                        }
                        }
                        }


                        This uses ECMA script's iterable interface. Note the above implementation has the least significant bit on the left (easily changed, if you need to).



                        Let's verify it counts correctly in binary:



                        [...mixedBaseCounter([2, 2, 2])]
                        // [ '000', '100', '010', '110', '001', '101', '011', '111' ]


                        And that it handles mixed bases:



                        console.log([...mixedBaseCounter([2, 3])])
                        // [ '00', '10', '01', '11', '02', '12' ]


                        Try it online!



                        applying it to the problem



                        Now the solutions becomes:



                        function letterCombinations(digits) {

                        const strDigits = {
                        '2': 'abc',
                        '3': 'def',
                        '4': 'ghi',
                        '5': 'jkl',
                        '6': 'mno',
                        '7': 'pqrs',
                        '8': 'tuv',
                        '9': 'wxyz',
                        }

                        const letterOptions = [...digits].map(x => strDigits[x])
                        const bases = [...letterOptions].map(x => x.length)
                        const masks = mixedBaseCounter(bases)

                        return [...masks].map(m =>
                        [...m].map((x, i) => letterOptions[i][x]).join('')
                        )
                        }


                        where each "mask" (or number, within the mixed base numbering system) chooses one combination.



                        Note also we no longer need to treat the empty string as a special case.



                        Try it online!





                        share









                        $endgroup$
















                          0












                          0








                          0





                          $begingroup$

                          I think your functional approach is very nice, and scores high points for readability. As long as the recursion wasn't causing performance issues, that's how I'd approach it.



                          However, it's not very efficient, and as vnp points out, listing permutations is really just a matter of counting from 1 to "the number of combinations" in a mixed base numbering system.



                          mixed base counting



                          These are easy implement, and it's worth going through the exercise, because assuming you have a utility that does the mixed base counting for you, the solution to the original problem will be both straightforward and efficient:



                          function mixedBaseCounter(bases) {
                          let cnt = 0
                          let maxCnt = bases.length ? [...bases].reduce((m, x) => m * x, 1) : 0
                          let digits = bases.map(() => 0)

                          const increment = (i = 0) => {
                          digits[i] = (digits[i] + 1) % bases[i]
                          if (digits[i] == 0) increment(i + 1)
                          }

                          return {
                          [Symbol.iterator]: function* () {
                          while (cnt++ < maxCnt) {
                          yield digits.join('')
                          increment()
                          }
                          }
                          }
                          }


                          This uses ECMA script's iterable interface. Note the above implementation has the least significant bit on the left (easily changed, if you need to).



                          Let's verify it counts correctly in binary:



                          [...mixedBaseCounter([2, 2, 2])]
                          // [ '000', '100', '010', '110', '001', '101', '011', '111' ]


                          And that it handles mixed bases:



                          console.log([...mixedBaseCounter([2, 3])])
                          // [ '00', '10', '01', '11', '02', '12' ]


                          Try it online!



                          applying it to the problem



                          Now the solutions becomes:



                          function letterCombinations(digits) {

                          const strDigits = {
                          '2': 'abc',
                          '3': 'def',
                          '4': 'ghi',
                          '5': 'jkl',
                          '6': 'mno',
                          '7': 'pqrs',
                          '8': 'tuv',
                          '9': 'wxyz',
                          }

                          const letterOptions = [...digits].map(x => strDigits[x])
                          const bases = [...letterOptions].map(x => x.length)
                          const masks = mixedBaseCounter(bases)

                          return [...masks].map(m =>
                          [...m].map((x, i) => letterOptions[i][x]).join('')
                          )
                          }


                          where each "mask" (or number, within the mixed base numbering system) chooses one combination.



                          Note also we no longer need to treat the empty string as a special case.



                          Try it online!





                          share









                          $endgroup$



                          I think your functional approach is very nice, and scores high points for readability. As long as the recursion wasn't causing performance issues, that's how I'd approach it.



                          However, it's not very efficient, and as vnp points out, listing permutations is really just a matter of counting from 1 to "the number of combinations" in a mixed base numbering system.



                          mixed base counting



                          These are easy implement, and it's worth going through the exercise, because assuming you have a utility that does the mixed base counting for you, the solution to the original problem will be both straightforward and efficient:



                          function mixedBaseCounter(bases) {
                          let cnt = 0
                          let maxCnt = bases.length ? [...bases].reduce((m, x) => m * x, 1) : 0
                          let digits = bases.map(() => 0)

                          const increment = (i = 0) => {
                          digits[i] = (digits[i] + 1) % bases[i]
                          if (digits[i] == 0) increment(i + 1)
                          }

                          return {
                          [Symbol.iterator]: function* () {
                          while (cnt++ < maxCnt) {
                          yield digits.join('')
                          increment()
                          }
                          }
                          }
                          }


                          This uses ECMA script's iterable interface. Note the above implementation has the least significant bit on the left (easily changed, if you need to).



                          Let's verify it counts correctly in binary:



                          [...mixedBaseCounter([2, 2, 2])]
                          // [ '000', '100', '010', '110', '001', '101', '011', '111' ]


                          And that it handles mixed bases:



                          console.log([...mixedBaseCounter([2, 3])])
                          // [ '00', '10', '01', '11', '02', '12' ]


                          Try it online!



                          applying it to the problem



                          Now the solutions becomes:



                          function letterCombinations(digits) {

                          const strDigits = {
                          '2': 'abc',
                          '3': 'def',
                          '4': 'ghi',
                          '5': 'jkl',
                          '6': 'mno',
                          '7': 'pqrs',
                          '8': 'tuv',
                          '9': 'wxyz',
                          }

                          const letterOptions = [...digits].map(x => strDigits[x])
                          const bases = [...letterOptions].map(x => x.length)
                          const masks = mixedBaseCounter(bases)

                          return [...masks].map(m =>
                          [...m].map((x, i) => letterOptions[i][x]).join('')
                          )
                          }


                          where each "mask" (or number, within the mixed base numbering system) chooses one combination.



                          Note also we no longer need to treat the empty string as a special case.



                          Try it online!






                          share











                          share


                          share










                          answered 5 mins ago









                          JonahJonah

                          3,651719




                          3,651719






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Code Review Stack Exchange!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              Use MathJax to format equations. MathJax reference.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f221976%2ffind-all-letter-combinations-of-a-phone-number%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Taj Mahal Inhaltsverzeichnis Aufbau | Geschichte | 350-Jahr-Feier | Heutige Bedeutung | Siehe auch |...

                              Baia Sprie Cuprins Etimologie | Istorie | Demografie | Politică și administrație | Arii naturale...

                              Nicolae Petrescu-Găină Cuprins Biografie | Opera | In memoriam | Varia | Controverse, incertitudini...