How do I assign a key when using split()

I’m having trouble finishing the JS course so I thought building a single-page app would help with learning and remembering a lot of the lessons. My aunt has cerebral palsy and her motor control has gotten much worse over the years and it’s also difficult to understand her when she talks. So I want to create a web page for her where he can click entire words and have them entered into a text box.

My first challenge is to use split() to get a list of the words that she most often uses. As a bonus, I wanted to have the common words with the highest count usage and have them at the top of the page. Then I’ll have a list of each letter of the alphabet.

I have a test string and split() is giving me a breakdown of the words with one issue with punctuation which I’ll address later. I’m getting an array with an index for every word in my string. Since it’s an array, I can’t assign a key at that point.

I had to search for a way to return a count of each word that I found and it returns a SINGLE object with each word as the key followed by the count as the value. I’ll paste that code below, but how would add a key of “word” with the words as values, and a key of “count” for the count value?

I’m assuming I need those keys so I can use map() and other methods. Here is the count function code:

const a = splitWords(lowerCase)

// ****** get a count for each word *****
function wordCount(a) {
  let counts = {}

  for (let i = 0; i < a.length; i++) {
    if (counts[a[i]]) {
      counts[a[i]] += 1
    } else {
      counts[a[i]] = 1
    }
  }

  console.log(counts)
  return counts;
}

wordCount(a)

wordsconsole

What is the final shape of the output you want? Something like this?

const counts = {
  a: 5,
  boat: 2,
  car: 3,
  // etc
}

[
{
word: “boat”,
count: 5
},
{
word: “car”,
count: 3
},
}

I just figure it should be in that format so that I can sort from high to low and put the most used words on the page. The remaining words will be in a dropdown menu or some other object.

Sure, you could do that with a .map(). I think the piece of secret sauce you’re missing is Object.keys():

const object1 = {
  a: 'somestring',
  b: 42,
  c: false
};

console.log(Object.keys(object1));
// expected output: Array ["a", "b", "c"]

That gives you an array of all of the keys, and you can use that array to create a brand new array of these word + count objects.

Well, one reason I’m trying to build something instead of completing the lessons is to understand all those functions and methods. But split() is giving me an array, then the code I pasted above gets one occurrence of each word with the count of how many times it appears in the string - all of that as ONE object. So Object.keys would only give me the actual words as keys which is what they are now. I need to convert, or I think or feel I need to convert { “the”: 5 } to { word: “the”, count: 5}, … Is that possible? If not, then I’ll see how far I get with each word as a key and hopefully, it doesn’t create problems.

Yes, it is possible… Object.keys() is how you can convert your single object into an array of objects…

const object1 = {
  a: 'somestring',
  b: 42,
  c: false
};

console.log(Object.keys(object1));
// expected output: Array ["a", "b", "c"]

// Use Object.keys() + map() to make a new array
console.log(
  Object.keys(object1)
    .map(function(key) {
      return {
        key: key,
        value: object1[key],
      } // put your new object here
    })
);

Hi @kernix, a project is a good way to learn. A useful one is even better.

How is your aunt going to use the site? It could probably help to understand the problem.
It would be nice to breakdown the features you want. From what I understood:

  • Sort words by frequency, higher frequency upper in the list.
  • Add single letters of alphabet at the end of the list.
  • Filter text to just account for alphabet letters and get rid of general punctuation.

For the adding part, see if this helps: JS Bin - Collaborative JavaScript Debugging

Hope this helps :slight_smile:

Ok, let me try that - thanks!

My main focus is to create at least 2 high-quality portfolio items so that maybe I could start getting interviews.

I don’t have it all figured out - I’m going one step at a time. The final test will be showing it to my aunt and see what she thinks. The overall idea is that it would be easier for her to click a word to add it to a text field than to type out each letter. I would set it up so that each word would include a single space before it and puctuation would not include any spaces.

I figured that the most common words like “the”, “and”, etc. would be visible on the page. Then there would be a list of words under each letter, so that would be 26 blocks - one for each letter. I was thinking accordians or mega-menus.

So she would click the common words at the top, or the next most common under each letter (however I end up doing that). I have no clue how to do this part, but I would have the click event add each word to a n input field. I might be able to format it to look like an input field. Then a copy link for that element so that she can paste everything into an email body or word doc.

And finally, I was thinking of having a “virtual” keyboard, maybe just a popup where she could type “uncommon” words. I would position that somewhere in a corner where clicking it would open it. I’d also like button to capitalize words midsentence and an undo option in case she clicked the wrong word.,

I think I could do the CSS part no problem, as well as adding elements to the page with the click event. No clue how to make a copy button, but I feel I need a key-value pair in the beginning to get the overall frequency of her word usage. Once I have that then I could rock and roll with a simple version to see how it works.

How about Object.entries? I found the following code on stack overflow(How to transpose a javascript object into a key/value array - Stack Overflow):

// wordCount creates the object with the words as keys and their counts as values
wordCount(a);

let input = wordCount(a);
let output = Object.entries(input).map(([word, count]) => ({ word, count }));

console.log(output);

wordcountoutput

Now I have an array of objects with each object having 2 key-value pairs. I have to figure out how to sort by the value of the 2nd key-value of “count”. But now I can use all the array methods that I need to learn anyway.

THANKS!

FYI Array.prototype.sort() - JavaScript | MDN

Will that work with something like this: arr = [ {word: “some-word”, count = 9}, {word: “nextWord”, count: 5}, …]

I need to sort the array of objects where each object has two properties / key-value pairs, and I need to sort by the second one. However, right now I have a string of text in my js file and I want to use a .txt file. But I can’t figure out how to import the content of the file as a variable. Using require(‘fs’) is getting an error and I can’t use export because the text is not in a separate js file.

Is there a reason why you want to use a txt file? Since you’re working with JavaScript, it would be easier to store all the words in a separate words.js file, from where you export them:

const words = [
  {word: 'the', count: 3},
  {word: 'test', count: 1}
]

export default words;

You can then import those in your script:

import words from './words.js'; //<-- if both files are in the same folder

This will only work if your script is of type module, so you have to declare it as such in your HTML:

<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>

    <script src="myscript.js" type="module"></script>
  </body>
</html>

This is one possible setup.


As for sorting the words, .sort is perfectly suited for sorting an array of objects. Get back if you need help with it.

Well, the point of the app is to get a sample of someone’s writing to use as the source to populate the page with their most used words. This is for people with motor control issues. I can try words.js but how would an end-user get a sample of their writing into that file? For now, I just want to get a sample of my writing to sort by highest word usage and start building the HTML and CSS.

Then I misunderstood, I thought the words file should constantly get updated while the user uses the app/web page.

JavaScript running in a browser cannot access a user’s file system (read a text file etc) for security reasons, unless the user explicitly chooses to upload a file. So your HTML needs an <input type="file" />. A small script how to read that file:

const input = document.querySelector('input');

input.addEventListener('change', async () => {
  let file = input.files[0];
  const fileContent = await file.text();
  console.log(fileContent)
})

Now you probably don’t want your users to upload that file again and again when using the app. So I’d again suggest that you read the file once with the above script, and store the string in a separate words.js file. But that’s ultimately up to you.

I’m definitely getting lost. I was just looking at an example using an input field of type “file” but the example I saw was adding the file contents to the page. I’ve also been trying to use export and import {} from. I just want to grab the text and start working with it so I created words.js with the export and then used import from in my main.js but I get this error: “Uncaught SyntaxError: Cannot use import statement outside a module”

I didn’t want to think about THE END until I had a lot done, but I guess I have to. And by “the end” I mean how a user would interact with the page. Each user will have to somehow include a sample block of text - the longer the better. I did not think having the sample updated as the user writes because you can’t include every word in the dictionary. I did want to have the ability to add or delete single words.

Anyway, I am only getting one thing done per day because I keep getting stuck. I assume the best option is to be able to upload a file (txt only?) and add the contents to a variable. Then I can split(), do a count and sort, then create logic to output the words onto the page. Know any good links about uploading a file and capturing the content in a variable?

I also can’t get module.exports and require(‘fs’) to work.

To sum it up again,

require('fs') and reading a file from your local file system will not work when the JavaScript is running in a browser.


How to read files from the browser through a file upload

(I’ll post a full working solution, make sure that your text file has the extension .txt)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>File Upload</title>
</head>
<body>
  <input type="file" />

  <script>
    const input = document.querySelector('input');

    input.addEventListener('change', async () => {
      let file = input.files[0];
      const fileContent = await file.text();
      console.log(fileContent)
    })
  </script>
</body>
</html>

If you copy/paste that into your editor, run the file with a live server or just open it with a browser, upload a .txt file and open your console, you should see the contents of the file logged out.


importing a words.js file into your script

(in my example called myscript.js):

HTML

<!-- make sure to add type="module" -->
<script src="myscript.js" type="module"></script>

myscript.js

import words from './words.js';

words.js (must be in the same folder as myscript.js)

const words = [
  {word: 'the', count: 3},
  {word: 'test', count: 1}
]

export default words;

That should get you started, you really just have to copy/paste.

Hi @kernix !
Another way to approach your challenge(to make an array of objects, each containing the keys word and count, with the respective values) is to loop thru the object keys directly via: for (let key in obj) { ...your code...}. I find this to be a very straight-forward and clean approach.
Here is a short snippet, assuming you already have the object with words/counts rendered:

let list=[]
for (let word in obj){
  newList.push({word: word, count: obj[word]})
}

Update - messed up. I put type=“module” on my word.js script tag but it should have been on main.js - export" and “import…from…” worked.

So I need a way to get the user supplied writing sample into word.js or to get an input file added as a variable.

I found similar code for the file upload for a txt that worked, then tried yours and it gets an error of: Uncaught TypeError: Cannot read property ‘addEventListener’ of null

Here is the code I found that outputs the file contents into a textarea element:

let input = document.querySelector('.fileupload')

let textarea = document.querySelector('.userinputarea')

input.addEventListener('change', () => {
  let files = input.files;

  if (files.length == 0) return;

  const file = files[0];

  let reader = new FileReader();

  reader.onload = (e) => {
    const file = e.target.result;
    const lines = file.split(/\r\n|\n/);
    textarea.value = lines.join('\n');
  };
  reader.onerror = (e) => alert(e.target.error.name);
  reader.readAsText(file);
});

I added the line if (files.length == 0) return; to your code thinking that was it but it wasn’t. I appreciate the help! I need to figure out how to change the reader.onload block to add the content to a variable as opposed to the textarea. Or how to add it to a variable and then use it as an input. It doesn’t look like that block of code is a Function other than the arrow function. Can I set that block to a variable then use that? Never mind, that’s the function for the event listener.