0

For a Hangman game, I have some topics (eg:cities and animals).

When the user selects one of the topics, the outcome should be one of the chosen topic's random item. eg: London or Zebra etc.

Currently I only have random letter of selected topic.

const cities = ["New York", "London", "Berlin"]
const animals = ["Alligator", "Alpaca", "Zebra"]

const topicsEl = document.querySelector("#topics")

function randomTopic(){
return topicsEl.value[Math.floor(Math.random()*topicsEl.value.length)]
}

topicsEl.addEventListener("change", function(){
    console.log(randomTopic());
})
<div class="select">
   <label for="topics">Choose a topic:</label>

   <select id="topics">
   <option value=cities>Cities</option>
   <option value=animals>Animals</option>
   </select>
</div>
Daniel Beck
  • 20,653
  • 5
  • 38
  • 53
EmRe Cos
  • 3
  • 3
  • 1
    Your question is confusing; can you edit it and add an example with your expected output? – Jack Fleeting Jan 01 '23 at 17:55
  • please add ```expected output``` in your question for better understanding for reader – Nexo Jan 01 '23 at 18:49
  • @Nikkkshit thank you for warning. Just edited the question. – EmRe Cos Jan 04 '23 at 19:49
  • @JackFleeting thank you for warning. Just edited the question. – EmRe Cos Jan 04 '23 at 19:50
  • Welcome to Stack Overflow! Thanks for expanding the question to make it clearer; but I've edited the text to remove the explanation that you edited it -- that sort of thing just makes the question more confusing for future users who won't have seen the original version. – Daniel Beck Jan 04 '23 at 20:47

2 Answers2

1

You seem to have issues getting a random value of a list depending on a selection.

Currently, you are selecting a random letter of topicsEl.value instead of a random element of the associated topic's list.

You need to determine the list to choose from depending on topicsEl.value. Dynamically this can be achieved if that value can be used as a key (e.g. for a dictionary), but this can also be done statically.

But doing it statically would result in duplicate code, e.g. in an if-else-if ladder growing for each new topics list:

function randomTopic() {
  if (topicsEl.value === "cities") {
    // ... (use citites)
  } else if (topicsEl.value === "animals") {
    // ... (use animals)
  } // Etc. for every new topic
}

Doing it dynamically allows for abstracting away the list selection, keeping the function simple. As suggested before, a dictionary can be used for this.

For example, your dictionary's properties could each be a topic list, and your option values should then match their corresponding property's name:

const topics = {
  cities: [/*...*/],
  animals: [/*...*/]
};

const topicsEl = document.querySelector("#topics");

function randomTopic() {
  const list = topics[topicsEl.value];
  // ...
}

Selecting a random item of that list works analogous to how you are currently selecting a random letter:

function randomTopic() {
  const list = topics[topicsEl.value];
  return list[Math.floor(Math.random() * list.length)];
}

Personally, I find such random selections more readable if the index generation is in a separate function. Example:

const edibles = ["cheese", "ham", "bread", "banana", "peanuts"];
console.log(randomEdible());

function randomEdible() {
  return edibles[randomInt(0, edibles.length - 1)];
}

function randomInt(max = 100, min = 0) {
  if (max < min) {
    [max, min] = [min, max]; // Swap
  }
  
  return Math.floor(Math.random() * (max - min + 1) + min); // +1 to include max
}
Oskar Grosser
  • 2,804
  • 1
  • 7
  • 18
  • I have edited my question to clarify more. Now I have learnt options property from you. Thank you Oscar. – EmRe Cos Jan 04 '23 at 19:53
  • @EmReCos I must have mistunderstood the original question, but I'm glad you found my initial answer still helpful. I have rewritten it now to hopefully clear up your confusion. – Oskar Grosser Jan 04 '23 at 22:50
0

In your existing code, topicsEl.value is going to be the string "cities" or the string "animals" (because those are the value of each of the options in your <select> box.). These are not the global variables you defined in your javascript, they're just strings contained in the HTML.

You then, in randomTopic(), access that string as an array, which Javascript interprets as you wanting two treat it as an array of the characters within that string. This is why you're getting a random letter from the word: "animals"[0] is the letter a, "animals"[1] is the letter n, and so on.

What you're trying to do is choose a random item from the array variables you've named "cities" and "animals", but your functions don't try to touch those variables, they're only acting on the strings contained in the DOM.

So you need to add a step, to get from the string value from the <select> to the array you're trying to access.

You've defined the two arrays as global variables; in theory those can be accessed as window.cities or window.animals, so you could do window[topicsEl.value] which would return the array you're trying to access.... it's not great practice to depend on the window global, though, so I'd encourage you to switch that pair of separate variables in to an object for easier access:

const topics = {
  cities: ["New York", "London", "Berlin"],
  animals: ["Alligator", "Alpaca", "Zebra"]
}

const topicsEl = document.querySelector("#topics")

function randomTopic() {
  // find the desired array:
  let topicArr = topics[topicsEl.value]
  // return a random element from that array:
  return topicArr[Math.floor(Math.random() * topicArr.length)]
}

topicsEl.addEventListener("change", function() {
  console.log(randomTopic());
})
<div class="select">
  <label for="topics">Choose a topic:</label>

  <select id="topics">
    <option value=cities>Cities</option>
    <option value=animals>Animals</option>
  </select>
</div>
Daniel Beck
  • 20,653
  • 5
  • 38
  • 53