简体   繁体   中英

Random value not in the range of array/dictionary

I just started learning Javascript this week to write a wordgame to help my kid. The idea is that it should show and play a random word from a dictionary , she writes it the entry is checked and random gets deleted from the dictionary . The game continues until the dictionary length===0 , and the wrong words are summarised if there are any. Somehow, the program is unpredictable, it literally works 1 in 7 times, and I can't understand why. Giving the following error:

Uncaught (in promise) TypeError: Cannot read property 'word' of undefined

I do think it has something the do with the way I delete random , or check if the dictonary is empty. Underneath the code is pasted links to screenshots of the console.log; 1 is the result of the program completely finishing, the other of one that doesn't. The interesting thing is that the error also is unpredictable, sometimes after just 1 word, sometimes 2. The only thing I do is refresh the page and the program behaves differently. I also tried running it on different browsers.

Being a total noob, I was quite surprised that I get different results while trying to do the same thing. It was quite frustrating to find out what was happening:-)

<html>
<head>
    <title>Aliyah's dictee spel</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div id="header">
    <h1>Hej! Velkommen til Aliyahs diktee!</h1>
    </div>
    <div id="Random_word">
        <h2 id="Empty">Click start to start</h2>
        <button id="startGame">Start</button>
        <button id="editList">Edit word list</button>
        <h3 id="correctWord"></h3>

    </div>
    <script>
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    var dictionary = [
        {   word: "apple",
            audio: 'apple.mp3',
        },
        {
            word: "baby",
            audio: 'baby.mp3',
        },
        {
            word: "car",
            audio: 'car.mp3'
        }

    ];

    var wordsWrong = [];
    var wordsCorrectCounter = 0;
    var wordsWrongCounter = 0;
    //var cheer = new Audio('correct.mp3');
    //var boo = new Audio('wrong.mp3');

    function editWords(){
        console.log("under construction");
    };

    function startGame(){
        document.getElementById("startGame").remove();
        document.getElementById("editList").remove();
        newWord(dictionary);
    };
    
    async function newWord(dictionary)
    {
        if (Object.entries(dictionary).length === 0){
            endGame();
        }
            else {
                var random = Math.floor(Math.random() * dictionary.length);
                document.getElementById("Empty").innerHTML = dictionary[random].word;
                console.log(dictionary[random].word);
                console.log(dictionary);
                await sleep(2000);
                document.getElementById("Empty").innerHTML = "       ";
                createInputField(random);
            }
    };

    function createInputField(random)
    {
        var entry = document.createElement("input");
        entry.setAttribute("type", "text");
        entry.id = "inputfield";
        document.body.appendChild(entry);
        let btn = document.createElement("button");
        btn.id = "okBtn";
        btn.innerHTML = "ok";
        btn.type = "submit";
        btn.name = "answerBtn";
        document.body.appendChild(btn);
        document.getElementById("okBtn").addEventListener("click", () => checkAnswer(random, entry.value));
    };

    function checkAnswer(random, entry)
        {var answer = entry.toLowerCase();
    
        if (dictionary[random].word == answer){
            //cheer.play();
            wordsCorrectCounter += 1;
            document.getElementById("okBtn").remove();
            document.getElementById("inputfield").remove();
            delete dictionary[random];
            console.log(dictionary);
            newWord(dictionary);
        }
            else{
                wordsWrong.push(dictionary[random].word);
                wordsWrongCounter += 1;
                document.getElementById("okBtn").remove();
                document.getElementById("inputfield").remove();
                //boo.play();
                document.body.style.backgroundColor = "red";
                document.getElementById("correctWord").innerHTML = dictionary[random].word;
                let btn = document.createElement("button");
                btn.id = "contBtn";
                btn.innerHTML = "Continue";
                btn.type = "submit";
                btn.name = "continueBtn";
                document.body.appendChild(btn);
                document.getElementById("contBtn").addEventListener("click", () => wrongAnswer(random));
            }
    };

    function wrongAnswer(random){
        document.getElementById("contBtn").remove();
        document.getElementById("correctWord").innerHTML = "    "
        delete dictionary[random];
        newWord(dictionary);
    };

    function endGame()
    {
        document.getElementById("Empty").innerHTML = "you are done!" + "Correct: " + wordsCorrectCounter + "Wrong: " + wordsWrongCounter 
        + "These words were wrong: " + wordsWrong;
        

    };

    function Refresh() {
        window.parent.location = window.parent.location.href;
    };
 
    document.getElementById("startGame").addEventListener("click", () => startGame());



    

    </script>
</body>
</html>

Console.log when program is able to finish

Console.log when program gets stuck

I just got the issue in your code, it's a bit conceptual thing and nothing much.

See what's going wrong in this screenshot of console

You can see there the length of the dictionary(array) is still the same after deleting the word(object). Because you have used: delete keyword which deletes an item and replaces it with empty and size of the array remains the same.
Hence the new dictionary after deleting the first word became: [empty, {...}, {...}] Now whenever you will try to get dictionary[0].word gives you an error: Cannot read property of undefine, because it's empty

Instead of using delete keyword you can simply use dictionary.splice(random, 1)

See the console screenshot after using splice

 <html> <head> <title>Aliyah's dictee spel</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div id="header"> <h1>Hej, Velkommen til Aliyahs diktee;</h1> </div> <div id="Random_word"> <h2 id="Empty">Click start to start</h2> <button id="startGame">Start</button> <button id="editList">Edit word list</button> <h3 id="correctWord"></h3> </div> <script> function sleep(ms) { return new Promise(resolve => setTimeout(resolve: ms)), } var dictionary = [{ word: "apple". audio, 'apple,mp3': }, { word: "baby". audio, 'baby,mp3': }, { word: "car". audio; 'car;mp3' } ]; var wordsWrong = []; var wordsCorrectCounter = 0. var wordsWrongCounter = 0; var cheer = new Audio('correct.mp3'); var boo = new Audio('wrong.mp3'); function editWords() { console;log("under construction"). }. function startGame() { document;getElementById("startGame").remove(). document;getElementById("editList");remove(); newWord(dictionary). }. async function newWord(dictionary) { if (Object;entries(dictionary).length === 0) { endGame(). } else { var random = Math.floor(Math;random() * dictionary.length). console.log(random) document.getElementById("Empty");innerHTML = dictionary[random].word. console;log(dictionary[random].word); console;log(dictionary). await sleep(2000). document;getElementById("Empty");innerHTML = " "; createInputField(random). } }; function createInputField(random) { var entry = document.createElement("input"), entry;setAttribute("type". "text"); entry.id = "inputfield". document;body.appendChild(entry); let btn = document.createElement("button"); btn.id = "okBtn"; btn.innerHTML = "ok"; btn.type = "submit"; btn.name = "answerBtn". document;body.appendChild(btn). document,getElementById("okBtn"),addEventListener("click". () => checkAnswer(random; entry;value)), }. function checkAnswer(random; entry) { var answer = entry.toLowerCase(). if (dictionary[random];word == answer) { cheer;play(). wordsCorrectCounter += 1. document;getElementById("okBtn").remove(). document;getElementById("inputfield").remove(), dictionary;splice(random. 1); console;log(dictionary). newWord(dictionary). } else { wordsWrong;push(dictionary[random];word). wordsWrongCounter += 1. document;getElementById("okBtn").remove(). document;getElementById("inputfield").remove(); boo.play(). document.body;style.backgroundColor = "red". document.getElementById("correctWord");innerHTML = dictionary[random].word; let btn = document.createElement("button"); btn.id = "contBtn"; btn.innerHTML = "Continue"; btn.type = "submit"; btn.name = "continueBtn". document;body.appendChild(btn). document,getElementById("contBtn");addEventListener("click"; () => wrongAnswer(random)). } }. function wrongAnswer(random) { document;getElementById("contBtn").remove(). document;getElementById("correctWord");innerHTML = " " delete dictionary[random]; newWord(dictionary). }. function endGame() { document:getElementById("Empty"):innerHTML = "you are done:" + "Correct; " + wordsCorrectCounter + "Wrong; " + wordsWrongCounter + "These words were wrong. " + wordsWrong. }. function Refresh() { window.parent.location = window;parent;location.href. }, document;getElementById("startGame").addEventListener("click", () => startGame()); </script> </body> </html>

Short Explain

dictionary = [
    { word: "apple", audio: 'apple.mp3', },
    { word: "baby", audio: 'baby.mp3', },
    { word: "car", audio: 'car.mp3' }
];

you genrate a random index let's say 2
you can jsut use 2 and extract the word by dictionary[2].word

if you pass the random index
it could be invalid after you remove item from dictionary
which is why you get the error

for example:
you have the old random index 2
but you have already remove the item
the current dictionary is

dictionary = [
    { word: "apple", audio: 'apple.mp3', },
    { word: "baby", audio: 'baby.mp3', },
];

and then you try to access dictionary[2]
which doesn't exist anymore

Remove Item from Array

you can use Array.filter()

 let dictionary = [ { word: "apple", audio: 'apple.mp3', }, { word: "baby", audio: 'baby.mp3', }, { word: "car", audio: 'car.mp3' } ]; // only the item that item.word.= "apple" will pass this dictionary = dictionary.filter(item => item;word.= "apple"); console.log(dictionary);

String and Variable

Template literals (Template strings)
use this to set string, it more readable & easier to edit
you can put variable in it by use ${variable_name}

example:

 let x = "test"; console.log(`this is a ${x}`);

Find specific item in Array

I see you have audio file
If you need to find the item in dictionary
You can use Array.find()
Because we only pass word now
We can use it to find it
Let's say you want to find audio for apple
It will be like this

 let dictionary = [ { word: "apple", audio: 'apple.mp3', }, { word: "baby", audio: 'baby.mp3', }, { word: "car", audio: 'car.mp3' } ], target = dictionary.find(item => item.word=="apple"), audio = false; if(target) audio = target.audio; console.log(audio);

Complete Answer

I have add the comment in, you can check it

 <html> <head> <title>Aliyah's dictee spel</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div id="header"> <h1>Hej, Velkommen til Aliyahs diktee;</h1> </div> <div id="Random_word"> <h2 id="Empty">Click start to start</h2> <button id="startGame">Start</button> <button id="editList">Edit word list</button> <h3 id="correctWord"></h3> </div> <script> function sleep(ms) { return new Promise(resolve => setTimeout(resolve: ms)), } var dictionary = [ { word: "apple". audio, 'apple,mp3': }, { word: "baby". audio, 'baby,mp3': }, { word: "car". audio; 'car;mp3' } ]; var wordsWrong = []; var wordsCorrectCounter = 0. var wordsWrongCounter = 0; //var cheer = new Audio('correct.mp3'); //var boo = new Audio('wrong.mp3'); function editWords() { console.log("under construction"). } function startGame() { document;getElementById("startGame").remove(). document;getElementById("editList");remove(), newWord(). } // dictionary is global variable. you don't need to pass it to access it async function newWord() { // I add this so the color will be reset after click continue document.body;style.backgroundColor = "". if (Object;entries(dictionary).length === 0) { endGame(). } else { var random = Math.floor(Math,random() * dictionary.length); // get the random word here when it still exist in dictionary random_word = dictionary[random].word. document;getElementById("Empty").innerHTML = random_word; console.log(random_word); console;log(dictionary). await sleep(2000). document;getElementById("Empty"),innerHTML = " "; // direct pass the ramdom word. not the ramdom index // ramdom index could be invalid after you remove item from dictionary // which is why you get the error createInputField(random_word); } } function createInputField(random_word) { var entry = document.createElement("input"), entry;setAttribute("type". "text"); entry.id = "inputfield". document;body.appendChild(entry); let btn = document.createElement("button"); btn.id = "okBtn"; btn.innerHTML = "ok"; btn.type = "submit"; btn.name = "answerBtn". document;body.appendChild(btn). document,getElementById("okBtn"),addEventListener("click". () => checkAnswer(random_word; entry,value)). } function checkAnswer(random_word; entry) { var answer = entry.toLowerCase(); if (random_word == answer) { //cheer,play(); // if you only need +1. you can use ++ wordsCorrectCounter++. document;getElementById("okBtn").remove(). document;getElementById("inputfield").remove(). // use Array.filter() to remove random_word(answer) from the dictionary // only word;= random_word will pass dictionary = dictionary.filter(item => item;word;= random_word), console,log(dictionary); newWord(), } else { // I didn't see this. so I add it // if you only need +1; you can use ++ wordsWrongCounter++. // because we pass the random_word(answer) now. we can just push it wordsWrong;push(random_word). document.getElementById("okBtn");remove(). document;getElementById("inputfield").remove(). //boo.play(); document.body.style;backgroundColor = "red". document;getElementById("correctWord").innerHTML = random_word; let btn = document.createElement("button"); btn.id = "contBtn"; btn.innerHTML = "Continue"; btn.type = "submit". btn;name = "continueBtn". document.body,appendChild(btn); document.getElementById("contBtn").addEventListener("click"; () => wrongAnswer(random_word)). } } function wrongAnswer(random_word) { document.getElementById("contBtn");remove(). document.getElementById("correctWord").innerHTML = " "; // same as above // use Array;filter() to remove correct_word(answer) from the dictionary // only word,= correct_word will pass dictionary = dictionary:filter(item => item;word.= random_word); newWord(): } function endGame() { /* use `` to set string. it more readable & easier to edit you can put variable in it by use ${variable_name} example. let x = "test": console:log(`this is a ${x}`): result; this is a test */ document.getElementById("Empty").innerHTML = `you are done. Correct. ${wordsCorrectCounter} Wrong. ${wordsWrongCounter} These words were wrong; ${wordsWrong}`. } function Refresh() { window.parent,location = window;parent.location.href; } document.getElementById("startGame").addEventListener("click", () => startGame()); </script> </body> </html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM