试图在数组中查找确切单词的索引,而不仅仅是匹配的第一个单词的索引

Trying to find index of the exact word in an array instead of just the index of the first word that matches

我有 2 个问题要解决: 我正在尝试为圣经软件制作一个解读经文的游戏。

  1. 问题是我的经文多次出现相同的词(例如“起初神创造天地”有 3 次)。我现在设置它的方式适用于只在经文中出现一次的单词,但对于单词“the”,我的代码当前将找到与“the”匹配的第一个数组项并将其移动到数组中的其他位置。例如:如果我有乱序的经文——“起初上帝创造了天地 the the the”,我尝试将第一个 the in 拖到 in 和 beginning 之间,它会像预期的那样工作,但是,如果我尝试将第二个 the in 拖到在创造和天堂之间它移动第一个(在和开始之间的那个)。我怎样才能改变我的比较功能,以便它找到正确的?

  2. 我希望我的 words/list 项目数组在单词被拖到下一个项目时重新排列,而不仅仅是在单词被放下时。如果您对此有任何想法,请告诉我。

<template>
  <div>
    <!-- <div :scripture="scripture">{{ scripture }}</div> -->
    <ul :scripture="scriptureArray" id="list"></ul>
    <div :verse="verse">- {{ verse }}</div>
  </div>
</template>

<script>
let vm;
export default {
  data() {
    return {
      dragging: "",
      draggedOver: "",
      scriptureArray: [],
    };
  },
  props: {
    scripture: {
      type: String,
      required: true,
      default: "",
    },
    verse: {
      type: String,
      required: true,
      default: "",
    },
    correct: {
        type: String,
        required: true,
        default: "",
    }
  },
  methods: {
    renderDraggableItems(scriptureArr) {
      let list = document.getElementById("list");
      list.innerText = "";
      scriptureArr.forEach((word) => {
        var node = document.createElement("li");
        node.draggable = true;
        node.addEventListener("drag", vm.setDragging);
        node.addEventListener("dragover", vm.setDraggedOver);
        node.addEventListener("dragend", vm.compare);
        node.innerText = word;
        list.appendChild(node);
      });
    },
    setDragging(e) {
      this.dragging = Number.isNaN(parseInt(e.target.innerText))
        ? e.target.innerText
        : parseInt(e.target.innerText);
    },
    setDraggedOver(e) {
      e.preventDefault();
      this.draggedOver = Number.isNaN(parseInt(e.target.innerText))
        ? e.target.innerText
        : parseInt(e.target.innerText);
    },
    compare(e) {
      e.preventDefault();
      var index1 = this.scriptureArray.indexOf(this.dragging);
      var index2 = this.scriptureArray.indexOf(this.draggedOver);
      this.scriptureArray.splice(index1, 1);
      this.scriptureArray.splice(index2, 0, this.dragging);
      console.log("scriptureArray:", this.scriptureArray);
      this.renderDraggableItems(this.scriptureArray);
      // this way works as long as no 2 words in the scripture are the same (text matching), is there another way?
    },
  },
  mounted() {
    vm = this;
    this.scriptureArray = this.scripture.split(" ");
    this.renderDraggableItems(this.scriptureArray);
  },
};
</script>

<style scopped>
#list {
  list-style: none;
  font-size: 30px;
  display: flex;
  justify-content: space-evenly;
}
</style>

这个组件是这样调用的

<template>
  <!-- add a component that shows the scrptures, keeps track of time, and allows unscrabling -->
  <div>
    <Timer @GameOver="handleGameOver" />
    <DraggableSentence
      :scripture="scrambledCurrentScripture"
      :verse="currentScriptureVerse"
      :correct="correctCurrentScripture"
    />
    <!-- TODO: create a level for each key in genesis  -->
  </div>
</template>

哪里

scrambledCurrentScripture = "the begging In God created the earth heavens and the"
currentScriptureVerse = "genesis 1:1"
correctCurrentScripture = "In the beginning God created the heave and the earth"

您有很多可以改进的地方:

  • 打乱版本中的单词与正确答案中的单词不同。如果这是给一些孩子的,我可以想象他们变老了,还在玩,因为无法破解而感到沮丧
  • 你不应该在使用 Vue 时执行 DOM 操作。并不是说它不起作用,而是说 Vue 在处理和更新 DOM 方面非常高效。当您手动执行时,这有点像从驾驶座上下来并将其推到路上(只需更新源数组并让 Vue 根据值变化处理 DOM)
  • 处理拖放时,您需要元素的唯一标识符。他们目前的 index 就足够了。 (.indexOf() returns 符合条件的第一个元素 - 这就是您的 compare 方法失败的原因)。

这里是:

Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
  el: '#app',
  data: () => ({
    to: "",
    from: "",
    words: [],
    verse: "Genesis 1:1",
    correctAnswer: "In the beginning God created the heavens and the earth"
  }),
  computed: {
    isCorrectOrder() {
      return this.words.join(' ') === this.correctAnswer;
    }
  },
  methods: {
    onDragEnd() {
      const word = this.words[this.from];
      this.words.splice(this.from, 1);
      this.words.splice(this.to, 0, word);
    },
  },
  mounted() {
    // basic shuffle
    this.words = this.correctAnswer.split(" ")
      .map(s => ({s, r: Math.random()}))
      .sort((a, b) => a.r > b.r ? -1 : 1)
      .map(o => o.s);
  },
})
ul {
  list-style: none;
  font-size: 24px;
  display: flex;
  justify-content: space-evenly;
  padding-left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <ul>
    <li v-for="( value, index ) in words"
        :key="index"
        draggable="true"
        @drag="from = index"
        @dragover.prevent="to = index"
        @dragend.prevent="onDragEnd"
        v-text="value" />
  </ul>
  <div :verse="verse">- {{ verse }}</div>
  
  <h4><em v-if="isCorrectOrder">You got it right!</em></h4>
</div>


就第二个请求而言,我的建议是使用 Vue.draggable, the Vue wrapper around sortable.js,一个使用拖放 HTML5 api 的小型但功能强大的 d&d 包,具有​​旧版浏览器的后备功能和触摸兼容。

它还会简化标记,因为您不再需要指定拖动事件:

Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
  el: '#app',
  data: () => ({
    words: [],
    verse: "Genesis 1:1",
    correctAnswer: "In the beginning God created the heavens and the earth"
  }),
  computed: {
    isCorrectOrder() {
      return this.words.join(' ') === this.correctAnswer;
    }
  },
  mounted() {
    this.words = _.shuffle(this.correctAnswer.split(" "));
  },
})
.drag-list {
  font-size: 24px;
  display: flex;
  justify-content: space-evenly;
  padding-left: 0;
  margin: 40px 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="//cdn.jsdelivr.net/npm/sortablejs@1.8.4/Sortable.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.20.0/vuedraggable.umd.min.js"></script>

<div id="app">
  <draggable v-model="words" class="drag-list">
    <div v-for="( value, index ) in words"
         :key="index">{{value}}</div>
  </draggable>
  <div :verse="verse">- {{ verse }}</div>
  <h4><em v-if="isCorrectOrder">You got it right!</em></h4>
</div>


忍不住加了一个计时器。太诱人了...

Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
  el: '#app',
  data: () => ({
    words: [],
    verse: "Genesis 1:1",
    correctAnswer: "In the beginning God created the heavens and the earth",
    start: performance.now(),
    current: performance.now()
  }),
  computed: {
    isCorrectOrder() {
      return this.words.join(' ') === this.correctAnswer;
    },
    timeElapsed() {
      return Math.round((this.current - this.start) / 100) / 10;
    }
  },
  methods: {
    tick() {
      this.current = performance.now();
      if (!this.isCorrectOrder) {
        setTimeout(this.tick, 100);
      }
    }
  },
  mounted() {
    this.words = _.shuffle(this.correctAnswer.split(" "));
    this.tick();
  },
})
.drag-list {
  font-size: 24px;
  display: flex;
  justify-content: space-evenly;
  padding-left: 0;
  margin: 40px 10px;
}

.flexer {
  display: flex;
  align-items: center;
}

code {
  padding-left: 2rem;
}

code:after {
  content: 's'
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="//cdn.jsdelivr.net/npm/sortablejs@1.8.4/Sortable.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.20.0/vuedraggable.umd.min.js"></script>

<div id="app">
  <draggable v-model="words" class="drag-list">
    <div v-for="( value, index ) in words" :key="index">{{value}}</div>
  </draggable>
  <div :verse="verse">- {{ verse }}</div>
  <div class="flexer">
    <h4><em v-if="isCorrectOrder">You got it right!</em></h4>
    <code v-text="timeElapsed"></code>
  </div>
</div>