0

I'd like to pass the function a string and print the number of words, characters, spaces, and the average length of each word as object properties. I'm looking for the return to be

{"numWords": 7, "numCharacters": 30, "numSpaces": 6, "avgWordLength": 3.x}

I'm also really new, so I'll take any advice on anything that looks wrong or could be better. Thanks!

function allTheCounts(str) {

  var numWords = str.split(" ").length; 
  var numCharacters = str.length; 
  var numSpaces = numWords - 1;
  var strArray = str.split(" ");
  var avgWordLength = 0;    
    for (var i = 0; i < numWords.length; i++){
    avgWordLength += strArray[i].length;
    }
  var avgWordLength = avgWordLength/numWords;

  var answerObject = {
    numWords: numWords,
    numCharacters: numCharacters,
    numSpaces: numSpaces,
    avgWordLength: avgWordLength
  };
 return answerObject;
}
 allTheCounts("Hello world. It is a good day.");
h.and.h
  • 690
  • 1
  • 8
  • 26

6 Answers6

1

It doesn't get much simpler than this:

function allTheCounts(str) {
  var spaces = (str.match(/ /g) || []).length;
  var numWords = str.split(' ').length;
  return {
    numWords: numWords,
    numCharacters: str.length,
    numSpaces: spaces,
    avgWordLength: (str.length-spaces)/numWords
  };
}

document.getElementById('answer').innerHTML = JSON.stringify(allTheCounts("Hello world. It is a good day."));
<div id='answer'></div>
Max
  • 2,710
  • 1
  • 23
  • 34
1

So the only thing stopping your function from working as expected is numWords.length, which should be changed to simply numWords, as it's already the length of the array that you wish to iterate over. When you change that, your code works as expected.

It seems though, that you also wanted some tips on style and design of the function, so I went ahead and tweaked a few things.

function allTheCounts(str) {
  var wordArray = str.split(" ");//get array of words
  var wordLengths = 0;//initalize wordLengths to 0
  for (var i = 0; i < wordArray.length; i++) {
    wordLengths += wordArray[i].length;
  }

  return {
    numWords: wordArray.length,
    numCharacters: str.length,
    numSpaces: wordArray.length - 1,
    avgWordLength: wordLengths/wordArray.length//Use Math.round(10*wordLengths/wordArray.length)/10 to round to one decimal place
  };
}
allTheCounts("Hello world. It is a good day.");

Removing unnecessary intermediate variables

You'll notice that your function has gotten a little long, and has more local variables than it has results. We can correct this by trying to find where we've duplicated our efforts. Clearly we shouldn't need to do a split(' ') more than once on the same string, so we just store the split array in one var, and reference that array's length when we want to use the number of words in our string. This same logic is applied a few times to remove several unnecessary lines of code. This makes the function much easier to read, and even has the potential to increase efficiency.

Important note! Tabs or other whitespace characters won't show up as spaces between words. Two words that are separated by only a newline will be counted as a single word, etc. It's also important to consider that allTheCounts("Hello world. It is a good day."); will result in 22 words being detected, because, when splitting up things by spaces, several zero-length strings will be added to wordArray. If you'd like to remove these zero-length entries in the array, you can see this SO answer.

For a way more efficient way of doing the average (removing the for loop from the function entirely), see Max's solution below. Folding his clever math into this function gives this almost-one-liner:

function allTheCounts(str) {
  var wordArray = str.split(" ");//get array of words

  return {
    numWords: wordArray.length,
    numCharacters: str.length,
    numSpaces: wordArray.length - 1,
    avgWordLength: (str.length - (wordArray.length - 1))/wordArray.length
  };
}
allTheCounts("Hello world. It is a good day.");
Community
  • 1
  • 1
TheToolBox
  • 272
  • 2
  • 10
0

You could try this

var data = document.getElementById("object-data");
var button = document.getElementById("my-button");

button.addEventListener("click", function() {
  
    var userInput = prompt("Enter a string");
    var words = userInput.split(/\s+/);
    
    var wordLengths = words.map(function(word) {
        return word.length;
    });
  
    var totalWordLength = wordLengths.reduce(function(word1, word2) {
        return word1 + word2;
    });
  
    var userData = {};
    userData.words = words.length;
    userData.characters = userInput.length;
    userData.spaces = userInput.match(/\s/g).length;
    userData.avgWordLength = totalWordLength / userData.words;
  
    data.innerHTML = "<p>Words: " + userData.words + 
                     "<br>Characters: " + userData.characters + 
                     "<br>Spaces: " + userData.spaces + 
                     "<br>Average Word Length: " + userData.avgWordLength + "</p>"
    
});
<button id="my-button">Get String Data</button>

<p id="object-data"></p>

We will start by referencing two variables. The first will be the paragraph that will hold the data we're looking for. The second will be the button, which will be used for an event listener.

When the button is clicked, we will want to get user input. We do this by using JavaScript's prompt method. This will read what the user types in and store it in a variable named userInput.

Every time we click on the button, we generate a new object, which will contain the string data. We can set properties of this with either dot notation (.) or bracket notation ([])

Richard Hamilton
  • 25,478
  • 10
  • 60
  • 87
0

I have just made refactor of your code, and it's simple as..

function allTheCounts(str) {
  var words = str.split(" ");  

  var answerObject = {
    numWords: words.length,
    numCharacters: str.length,
    numSpaces: words.length - 1,
    avgWordLength: parseInt(str.length / words.length)
  };
  console.log(answerObject);
  
   return answerObject;
}
 allTheCounts("Hello world. It is a good day.");

Working example at jsbin

Miguel Lattuada
  • 5,327
  • 4
  • 31
  • 44
0

It looks like you have a typo in the following line:

for (var i = 0; i < numWords.length; i++){
// change to this
for (var i = 0; i < strArray.length; i++){

You probably want to remove periods from the words when calculating the length.

avgWordLength += strArray[i].length;
// change to this    
avgWordLength += strArray[i].replace(/\.$/, '').length;

There are still some corner cases that aren't handled such as consecutive spaces. Hopefully this gives you a good start.

A minor improvement to make the code a bit cleaner:

var numWords = str.split(" ").length; 
// move this further down and change to this to eliminate some duplication
var numWords = strArray.length;

And another - remove an unnecessary temporary variable:

var answerObject = {
    numWords: numWords,
    numCharacters: numCharacters,
    numSpaces: numSpaces,
    avgWordLength: avgWordLength
};
return answerObject;

// change to this
return {
    numWords: numWords,
    numCharacters: numCharacters,
    numSpaces: numSpaces,
    avgWordLength: avgWordLength
};
Robert Harris
  • 482
  • 2
  • 6
0

The variable numWords is declared as a number but sometimes used as an array in your code (like in the for loop).

function allTheCounts(str) {

  var numWords = str.split(" ");
  var numCharacters = str.length;
  var numSpaces = numWords.length - 1;
  var strArray = str.split(" ");
  var avgWordLength = 0;
  for (var i = 0; i < numWords.length; i++) {
    avgWordLength += strArray[i].length;
  }
  var avgWordLength = avgWordLength / numWords.length;

  var answerObject = {
    numWords: numWords.length,
    numCharacters: numCharacters,
    numSpaces: numSpaces,
    avgWordLength: avgWordLength
  };
  return answerObject;
}
console.log(allTheCounts("Hello world. It is a good day."));
Thinh Nguyen
  • 392
  • 1
  • 8