最近在做一个答题系统,在交卷的时候需要判断客观题的答题情况
客观题的题型有单选题、多选题、判断题
其中判断题可以当做单选题处理,而单选题也可以当做标准答案长度为一的多选题
所以最终只需要实现多选题的判定即可
一、需求分析
将标准答案和考生回答分别记为字符串数组 standard 和 answer
经过分析,多选题可能出现的情况有:
1. 完全正确
answer 中的所有元素都能在 standard 中找到,且 answer 和 standard 长度相等
2. 部分正确
answer 中的所有元素都能在 standard 中找到,但 answer 的长度小于 standard
3. 回答错误
answer 中至少有一个元素不在 standard 中
为了处理以上情况,可以设计一个排除 standard 和 answer 中重复项的函数,并基于各自剩下的元素来判断结果
/** * 去除两个数组的重复项, 并返回处理后的两个数组 * ```ts * 输入: [ ['A', 'B'], ['A', 'C'] ] * 输出: [ ['B'], ['C'] ] * ``` * @param data [string[], string[]] * @returns [string[], string[]] */ function compareArray(data: [string[], string[]]): [string[], string[]] { const [standard, answer] = data || []; // ... }
二、函数实现
这个 compareArray 函数内部逻辑和数组去重的过程很类似
// 关于数组去重的方案可以参考《JavaScript 高性能数组去重》
需要遍历数组 A, 拿每个元素去和数组 B 对比, 如果重复则在两个数组中都删除该元素
1. 基础版:
使用 indexOf 在 B 数组中查找重复项
若存在重复项,则将元素置空,最后使用 filter 过滤
不建议使用 splice 删除元素,因为它会修改原数组,不易维护遍历的过程
function compareArray(data: [string[], string[]]): [string[], string[]] { const [standard, answer] = data || []; // 拷贝原数组 const _standard = [...standard]; const _answer = [...answer]; for (let i = 0; i < _standard.length; i++) { const k = _standard[i]; const answerIndex = _answer.indexOf(k); if (answerIndex < 0) continue; // 如果存在重复元素, 则将对应的元素标记为空 _standard[i] = ''; _answer[answerIndex] = ''; } // 过滤空元素 return [ _standard.filter((x) => !!x), _answer.filter((x) => !!x), ]; }
2. 进阶版
上面的基础版使用了 indexOf 来查找重复项,会存在重复遍历的情况
可采用空间换时间的思路,改用对象来判断重复项
另外还可以使用增量存储的方式,记录 standard 中未重复的元素,这样就可以省略最后一步的 filter
function compareArray(data: [string[], string[]]): [string[], string[]] { const [standard, answer] = data || []; // 用于记录 standard 数组的处理结果(增量存储) const _standard: string[] = []; // 将 answer 转为对象便于查询, 需要手动删除重复值 const _answerMap: Record<string, true> = answer.reduce( (res, x) => ({ ...res, [x]: true }), {}, ); for (let i = 0; i < standard.length; i++) { const k = standard[i]; // 如果存在重复值, 则删除 answerMap 中的键 if (_answerMap[k]) { Reflect.deleteProperty(_answerMap, k); } else { // 如果存在不重复, 则记录到结果数组中 _standard.push(k); } } return [_standard, Object.keys(_answerMap)]; }
三、结果判断
文章的开头已经提到,多选题的结果存在这三种情况
/** 答题结果 */ export enum ANSWER_RESULT_ENUM { /** 完全正确 */ CORRECT = '1', /** 部分正确 */ NOTBAD = '2', /** 回答错误 */ WRONG = '3', }
结合上面的 compareArray 函数,可以这么来判断答题结果:
type AnswerItem = string[]; /** 检查单个问题的答题情况 */ function markingPapers( answer: AnswerItem, standard: AnswerItem, ): ANSWER_RESULT_ENUM { // 未作答 if (!answer?.length) { return ANSWER_RESULT_ENUM.WRONG; } const [_answer, _standard] = compareArray([answer, standard]); // 完全正确 if (!_standard.length && !_answer.length) { return ANSWER_RESULT_ENUM.CORRECT; } // 部分正确 if (!_answer.length && _standard.length > 0) { return ANSWER_RESULT_ENUM.NOTBAD; } // 回答错误 return ANSWER_RESULT_ENUM.WRONG; }
标签:const,string,重复,standard,answer,数组,字符串,交卷 From: https://www.cnblogs.com/wisewrong/p/17251501.html