【问题标题】:Randomizing quiz-answers fetched from a rest-API随机化从 rest-API 获取的测验答案
【发布时间】:2019-06-07 20:50:44
【问题描述】:

所以我创建了一个测验应用程序,它会随机抽取 10 个问题,其中三个不正确和一个正确答案可供选择。目前一切都很好,除了我无法随机弹出答案。我的意思是,正确答案总是在所提供选项的底部。

我知道答案是 Math.floor(Math.random() * ... ) 但老实说我不知道​​该放在哪里。我什么都试过了。我真的需要一些帮助。

import React, { Component } from "react";
import "./App.css";

const API =
  "https://opentdb.com/api.php?amount=10&category=20&difficulty=medium";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      results: [],
      score: []
    };
  }

  componentDidMount() {
    this.populateAppWithData();
  }

  populateAppWithData() {
    fetch(API)
      .then(response => response.json())
      .then(data => this.setState({ results: data.results }))
      .catch(error => console.error(error))
  }

  render() {
    const { results } = this.state;
    return (
      <div className="App">
        <h1>Quiz App</h1>
        <TheCounter results={results}
          Counter={this.state.score}
        />
      </div>
    );
  }
}
class MythologyAnswers extends Component {
  constructor(props) {
    super(props);
    this.state = {
      answered: "",
      isRight: null,
    };
  }
  answerClicked(answer) {
    const { hasAnswered, correct_answer } = this.props;
    return event => {
      const isRight = correct_answer === answer;
      hasAnswered(isRight);
      this.setState({
        answered: answer,
        isRight,
      });
    };
  }

  render() {
    const { question, correct_answer, incorrect_answers } = this.props;
    const { answered, isRight } = this.state;
    return (
      <div className="allAnswers">
        {question}
        {incorrect_answers && incorrect_answers
          .concat(correct_answer)
          .map(answer => (<div onClick={this.answerClicked(answer)}>{answer} </div>))}<br />
        {answered && `You answered ${answered}`} {" "} <br />
        <div className="correctAnswer"> {" "}{answered && isRight && "This is correct!"} </div> <br />
        <div className="incorrectAnswer"> {" "}{answered && !isRight && `This is incorrect. The correct answer is ${this.props.correct_answer}`} {" "}</div>
      </div>
    )
  }
}
class TheCounter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      right: 0,
      Counter: 0,
      unanswered: 0,
    };
  }
  questionAnswered = isRight => {
    this.setState(({ Counter, right }) => ({
      Counter: Counter + 1,
      right: right + isRight,
    }));
  }
  render() {
    const { results } = this.props;
    const { Counter } = this.state;
    const unanswered = this.props.results && Counter;
    if (unanswered >= 10) {
      return <div className="theUnanswered"> You got {this.state.right} right out of {this.state.Counter} </div>;
    }
    const question = results[Counter];
    return (
      <div className="newQuestion">
        <MythologyAnswers {...question} hasAnswered={this.questionAnswered} />
      </div>
    )
  }
}
export default App;

【问题讨论】:

  • 嗨 Vicky,我刚刚为您提供了一个解决方案,说明如何在没有 Knufes 算法的情况下随机化您的数组。它还应该使您的标记更清晰:)
  • 嗨@ChristopherNgo 非常感谢您的帮助和您的代码。我知道我的代码目前还不是很干净,但为了继续学习而不至于太困惑,我宁愿继续使用我已经拥有的代码。如果问的不是太多,您能否告诉我我可以用我已经拥有的东西来实现随机功能?当我第一次开始时,我将所有代码都放在一个组件中,我可以通过在调用结果状态时简单地添加 math.floor(math.random()) 来随机化,但是自从我将它分成三部分后,我就没有了线索在哪里做。
  • 让我看看是否可以修改我的答案以使用您现有的代码。同时,你可以试试我下面的答案,看看它是否有效?
  • 嗨,Vicky,整合以下内容有什么运气吗?我仍在修改代码,使其看起来与您编写的内容没有太大差异。
  • Vicky,嗯,我不太确定。我猜想这与这条线有关:{answered &amp;&amp; !isRight &amp;&amp; This is incorrect. The correct answer is ${ this.props.correct_answer }} 你从使用 MythologyAnswers 的组件中作为名为 correct_answer 的道具传入了什么?

标签: arrays json reactjs


【解决方案1】:

我们只需要在正确的地方应用随机化器。您使用.concat() 来组合两个数组,因此在我们调用.map() 之前使用随机化器是有意义的

我已经设置了一些东西来保留你已经编写的许多现有逻辑。

这将有助于创建新的 Arr 并为您的组件设置标记。

  renderChoices = () => {
    const { correct_answer, incorrect_answers } = this.props;

    let allAnswers = incorrect_answers
      ? incorrect_answers.concat(correct_answer)
      : [];

    //simple shuffle algorithm. Just inject your array and it'll pop out a new one.

    function createRandom(arr) {
       let myArr = [...arr];  //copy arr we pass in
       let randomizedArr = []; //gets popuated by loop

       while (myArr.length > 0) {
          var randomIndex = Math.floor(Math.random() * myArr.length); //create random number
          randomizedArr.push(myArr[randomIndex]); //add choice randomly to arr
          myArr.splice(randomIndex, 1); //cut out a piece of the array then resart loop
      }
       //when loop has finished, return random array
       return randomizedArr;
    }

    //call randomizer and get new Arr
    let randomizedArr = createRandom(allAnswers); 

    //use .map to create markup with randomizedArr
    return randomizedArr.map(answer => {
      return <div onClick={this.answerClicked(answer)}>{answer}</div>;
    });
  };

因此,如果您要在 render 中调用上述函数,它将为您创建答案集。

  render() {
    const { question } = this.props;
    const { answered, isRight } = this.state;
    return (
      <div className="allAnswers">

        {question}

        { this.renderChoices()}

        <br />

        {answered && `You answered ${answered}`} <br />

        <div className="correctAnswer">
          {answered && isRight && "This is correct!"}
        </div>

        <br />

        <div className="incorrectAnswer">
          {answered &&
            !isRight &&
            `This is incorrect. The correct answer is ${
              this.props.correct_answer
            }`}
        </div>
      </div>
    );
  }

干净且不太复杂:)

编辑:所以不要过多更改原始代码:

  createRandom(arr) {
    let myArr = [...arr]; //copy arr we pass in
    let randomizedArr = []; //gets popuated by loop

    while (myArr.length > 0) {
      var randomIndex = Math.floor(Math.random() * myArr.length); //create random number
      randomizedArr.push(myArr[randomIndex]); //add choice randomly to arr
      myArr.splice(randomIndex, 1); //cut out a piece of the array then resart loop
    }
    //when loop has finished, return random array
    return randomizedArr;
  }

  render() {
    const { question, correct_answer, incorrect_answers } = this.props;
    const { answered, isRight } = this.state;
    const allAnswers =
      incorrect_answers ? incorrect_answers.concat(correct_answer) : [];
    const randomizedAnswers = this.createRandom(allAnswers)

    return (
      <div className="allAnswers">
        {question}
        {randomizedAnswers
            .map(answer => (
              <div onClick={this.answerClicked(answer)}>{answer} </div>
            ))}
        <br />
        {answered && `You answered ${answered}`} <br />
        <div className="correctAnswer">
          {answered && isRight && "This is correct!"}
        </div>
        <br />
        <div className="incorrectAnswer">

          {answered &&
            !isRight &&
            `This is incorrect. The correct answer is ${
              this.props.correct_answer
            }`}
        </div>
      </div>
    );
  }

所以在编辑后的版本中我们做了几件事:

  1. 定义了一个名为createRandom()的函数......它所做的只是 随机选择你的答案。
  2. render 中,我们创建了一个名为 allAnswers 的新变量,它只是 concat() incorrect_answerscorrect_answer 就像你做的那样 之前。如果没有定义incorrect_answers,我们将使用 一个空数组 [] 作为默认值。
  3. 创建一个名为randomizedAnswers 的新变量。我们称之为 createRandom() 并传入 allAnswers 作为参数。它返回 供我们使用的随机数组。
  4. 然后只需.map() 超过randomizedAnswers 即可创建您的 答案选择标记。

【讨论】:

    【解决方案2】:

    在下面的代码中,我想您正在尝试将不正确的答案数组与正确的答案连接

    incorrect_answers && incorrect_answers
              .concat(correct_answer)
    

    所以数组变成[incorrect_answers,incorrect_answers,incorrect_answers,correct_answer] 即正确答案在最后 因此,如果您想在随机位置插入正确答案并假设有 4 个选项,那么首先生成一个介于 0 和 3 之间的随机数

    let randonIndex = Math.floor(Math.random() * 4)
    

    然后在随机索引处插入正确答案

    incorrect_answers && incorrect_answers
              .splice(randomIndex, 0, correct_answer);
    

    【讨论】:

    • 嗨@Akash。非常感谢你的回复。当我用 .splice(...) 替换 .concat(correct_answer) 时,答案都从屏幕上消失了,我只得到了实际的问题,点击它时也没有任何反应
    【解决方案3】:

    我会在获取数据后立即添加它。使用像 Fisher-Yales 这样的内联 shuffle,您根本不需要修改结构。

    const fisherYalesShuffle = (a) => {
        let j, x, i;
        for (i = a.length - 1; i > 0; i--) {
            j = Math.floor(Math.random() * (i + 1));
            x = a[i];
            a[i] = a[j];
            a[j] = x;
        }
    }
    

    但是,由于您通常希望将尽可能多的数据逻辑移动到后端,因此您还应该考虑将它们移到那里。

    【讨论】:

    • 嗨@Berouminum。感谢你的回复。我按照你写的方法试过了,什么也没发生。而且我对Fisher-Yales一点也不熟悉,所以我现在更喜欢我能理解的线索(在反应方面我仍然认为自己是新手)至于我之前写的第二条建议目前根本不知道将该代码放在哪里。
    【解决方案4】:

    你可以定义一个洗牌函数(这个是 Fisher-Yates (aka Knuth) 洗牌):

    function shuffle(array) {
      var currentIndex = array.length, temporaryValue, randomIndex;
    
      // While there remain elements to shuffle...
      while (0 !== currentIndex) {
    
        // Pick a remaining element...
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;
    
        // And swap it with the current element.
        temporaryValue = array[currentIndex];
        array[currentIndex] = array[randomIndex];
        array[randomIndex] = temporaryValue;
      }
    
      return array;
    }
    

    然后在像这样设置状态时应用该函数:

    this.setState({ results: this.shuffle(data.results) })

    【讨论】:

    • 嗨@Sergio。非常感谢,但正如我之前所说,我认为目前这对我来说有点太复杂了。我真的很感激一些更容易的事情。但是非常感谢!
    • 嗯,我能想到的唯一更简单的选择可能是使用 lodash shuffle (lodash.com/docs/#shuffle) 只需安装 lodash,像 import _ from 'lodash' 一样导入它并执行 this.setState({ results: _.shuffle(data.results) }))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-10
    • 2012-08-11
    相关资源
    最近更新 更多