Search code examples
javascriptstringforeachclosuresternary

Javascript ternary operator inside forEach returns undefined


I’m trying to write a function that takes a string and capitalizes the first letter in each word that is not included in the “minorWords” string. What is missing in my code that causes the return value to be “undefined”? After writing this function a few different ways I am now thinking that I am just using .forEach incorrectly. I am fairly certain that I am using the ternary operator appropriately, but I tried substituting an if statement and got the same result ( undefined ). I am also not sure why undefined is being returned twice . . .

function titleCase1(title, minorWords) {
  var titleArray = title.split(" ");
  var minorArray = minorWords.split(" ");
  var newArray = titleArray.forEach(function(word){
    (word in minorArray)? word : 
       word[0].toUpperCase() + word.slice(1);
  })
  console.log(newArray);
}

titleCase1("the wind in the willows", "the in");
// -> undefined undefined

I realize that if this works the first "the" will not be capitalized, but I will figure that out once I am no longer misusing the tools I have here . . .


Solution

  • There're two problems with your code:

    1. The only thing forEach does is execute a callback for each element in an Array and does NOT return anything, therefore newArray will always be undefined. For reference check how forEach works here.

      If you want to create a new Array with values like how you're trying to do with newArray. You need to use map, but you actually require to return a value from the callback. For reference check how map works here.

    2. You can't use the in operator to see if a word is present in your Array. The in operator only checks if a specified property is present in a specified Object. Therefore it will always return false when used for checking an element inside of an Array. Because an Array in javascript is actually an Object under the hood!

      var a = [ 'a', 'b', 'c' ];

      is actually

      var a = { 0: 'a', 1: 'b', 2: 'c' };

      Therefore 'a' in [ 'a', 'b', 'c' ] will always return false and for example 0 in [ 'a', 'b', 'c' ] will return true.

      Because of this caveat you should change your approach and for example use indexOf. For reference check how indexOf works here.

    With this in mind you could modify your code to the following to get the desired behaviour:

    function titleCase1(title, minorWords) {
      var titleArray = title.split(' ');
      var minorArray = minorWords.split(' ');
      var newArray = titleArray.map(function (word) {
    
        // indexOf returns the elements index on match or -1.
        var match = minorArray.indexOf(word) != -1;
    
        // If there's a match, return the (lowercased) word, otherwise uppercase it.      
        return match ? word : (word[0].toUpperCase() + word.slice(1));
      });
    
      console.log(newArray);
    }
    
    titleCase1("the wind in the willows", "the in"); // [ 'the', 'Wind', 'in', 'the', 'Willows' ]