i have a object inside objects that want to generate randomly, is a questions quiz. i have tried var
index = Math.floor(Math.random() *currentQuestion.answers[letter]);
no luck with it, can anyone help and explain why i cant generate the item randomly ?
i need to generate random answers item inside my createQuiz function, now is fixed exp: A: MJ, B:Pippen, C:Magic and if refresh it will randomly generate A:Pippen B:Magic C:MJ and so on.
my objects variable
const myQuestions = [
{
question: "What's my name ?",
answers: {
item1: "Chris",
item2: "Leborn",
item3: "Webber"
},
correctAnswer: "Chris",
button: "Next"
},
{
question: "What's my age ?",
answers: {
item1: "31",
item2: "30",
item3: "29"
},
correctAnswer: "31",
button: "Next"
},
{
question: "What's my favor NBA star ?",
answers: {
item1: "MJ",
item2: "Pippen",
item3: "Magic"
},
correctAnswer: "MJ",
button: "Done"
}
]
functions
function createQuiz() {
//clear the contents of questions div first
document.getElementById('questionsBox').innerHTML = "";
//clear answers box
document.getElementById('answersBox').innerHTML = "";
//set answer sting
answersCaptcha = [];
//output
output = [];
// for each question...
myQuestions.forEach(
(currentQuestion, questionNumber) => {
// we'll want to store the list of answer choices
const answers = [];
const option = ["A","B","C"];
let count = -1;
for(letter in currentQuestion.answers){
count++;
var index = Math.floor(Math.random() * currentQuestion.answers[letter]);
// i need to generate random answers item here, now is fixed
// exp: A: MJ, B:Pippen, C:Magic and if refresh it will randomly generate A:Pippen B:Magic C:MJ and so on.
answers.push(
`<label>
<input type="radio" name="question${questionNumber}" value="${letter}">
${option[count]} :
${currentQuestion.answers[letter]}
</label>`
);
}
// add this question and its answers to the output
output.push(
`<div class="question"> ${currentQuestion.question} </div>
<div class="answers"> ${answers.join('')} </div>`
);
})
document.getElementById('answersBox').innerHTML = output.join('');
console.log(answersCaptcha);
}
if i understand your question correctly... all you need is shuffle(currentQuestion.answers)
The problem is that letter is not a number - it cannot be used to randomize that way. Here's a solution:
const myQuestions = [{
question: "What's my name ?",
answers: {
item1: "Chris",
item2: "Leborn",
item3: "Webber"
},
correctAnswer: "Chris",
button: "Next"
},
{
question: "What's my age ?",
answers: {
item1: "31",
item2: "30",
item3: "29"
},
correctAnswer: "31",
button: "Next"
},
{
question: "What's my favor NBA star ?",
answers: {
item1: "MJ",
item2: "Pippen",
item3: "Magic"
},
correctAnswer: "MJ",
button: "Done"
}
]
createQuiz()
function createQuiz() {
//clear the contents of questions div first
document.getElementById('questionsBox').innerHTML = "";
//clear answers box
document.getElementById('answersBox').innerHTML = "";
//set answer sting
answersCaptcha = [];
//output
output = [];
// for each question...
myQuestions.forEach(
(currentQuestion, questionNumber) => {
// we'll want to store the list of answer choices
const option = ["A", "B", "C"];
let count = -1;
const answers = shuffleArray(Object.entries(currentQuestion.answers)).map((e, i) => {
return `<label>
<input type="radio" name="question${questionNumber}" value="${e[0]}">
${option[i]} :
${currentQuestion.answers[e[0]]}
</label>`
})
// add this question and its answers to the output
output.push(
`<div class="question"> ${currentQuestion.question} </div>
<div class="answers"> ${answers.join('')} </div>`
);
})
document.getElementById('answersBox').innerHTML = output.join('');
console.log(answersCaptcha);
}
function shuffleArray(arr) {
let array = arr
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array
}
<div id="questionsBox"></div>
<div id="answersBox"></div>
Related
I am having difficulties formatting some data. Currently, I receive data in the following structure.
[
{
"q1":"5",
"q2":[
"13",
"12",
],
"q3":"test",
}
]
I essentially need to modify this or even create a new object, that takes the following structure.
[
{
id: 1, //q1
answers: [
{
answer: '5',
},
],
},
{
id: 2, //q2
answers: [
{
answer: '13',
},
{
answer: '12',
},
],
},
{
id: 3, //q3
answers: [
{
answer: 'test',
},
],
},
];
So the id in the above would be obtained by remove the q and getting the number in the first data object. It would then have an answers array that would have an object for each answer.
I have been attempting this but have gotten lost. I don't know if I should use loops, mapping, filters etc. To be honest, the furthest I have got so far is obtaining the keys
var modified = data.map(function(item) {
return Object.keys(item)
})
I have created a JSFiddle where I have been attempting to do this.
Is there any way I can achieve the data I am after?
Many thanks
Please use map function.
const data = {
"q1":"5",
"q2":[
"13",
"12",
],
"q3":"test",
};
const result = Object.keys(data).map(key => {
let item = {id: key.substring(1), answers: []};
if(typeof data[key] === "string")
item.answers.push({answer: data[key]});
else
item.answers = data[key].map(val => ({answer: val}));
return item;
});
console.log(result)
const inputData = [
{
"q1":"5",
"q2":[
"13",
"12",
],
"q3":"test",
}
]
function answerMapper(objVal, id){
return Array.isArray(objVal)
?
{ id, answers: objVal.map(answer => ({ answer }))}
:
{ id, answers: [{answer: objVal }] }
}
function formatObject(obj){
return Object.keys(obj).map((k, i) => answerMapper(obj[k], i+1));
}
const result = inputData.map(obj => formatObject(obj));
// remove flatMap if your inputData has more than one entry
console.log(result.flatMap(x => x));
map over the first element of the data with Object.entries, grab the key and value, create a new answers array and return a new object.
const data = [{
"q1": "5",
"q2": [
"13",
"12",
],
"q3": "test",
}];
const out = Object.entries(data[0]).map(obj => {
const [ key, value ] = obj;
const id = Number(key[1]);
// If the the value is an array
// return a new array of mapped data
// Otherwise return an array containing
// one object
const answers = Array.isArray(value)
? value.map(el => ({ answer: el }))
: [{ answer: value }];
// Return the new object
return { id, answers };
});
console.log(out);
lets create a pure function which accepts the object in the array like so
const processObject = obj => Object.keys(obj).map(id => {
const answer = obj[id];
const answers = Array.isArray(answer) ? answer : [answer]
const answerObjectArray = answers.map(ans => ({
answer: ans
}));
return {
id: +id.substring(1),
answers: answerObjectArray
}
});
const dataArray = [{
"q1": "5",
"q2": [
"13",
"12",
],
"q3": "test",
}];
const output = processObject(dataArray[0]);
console.log(output);
This is my Quiz code for javascript:
"use strict"
const questions = [
{
question: "whats the full form of HTML",
answers: {
a: "Hello Text My Language",
b: "Hyper text Main Language",
c: "Hyper Text Markup Language",
d: "Hi There My Luck",
},
correctAnswer:"ent-c",
},
{
question: "whats the full form of CSS",
answers: {
a: "Cascading Style Sheet",
b: "City Site Section",
c: "Cyber Section Stand",
d: "CycleStand Section",
},
correctAnswer:"ent-a",
},
{
question: "whats the full form of JSON",
answers: {
a: "Jest Oriented Note",
b: "JavaScript Object Notation",
c: "Javascript Organised Node",
d: "Joomla Of Node",
},
correctAnswer:"ent-b",
},
{
question: "whats the full form of SQL",
answers: {
a: "Super Query Language",
b: "Sorted Queue Line",
c: "Superior Query Language",
d: "Structured Query Language",
},
correctAnswer:"ent-d",
},
];
let quest = document.querySelector('.question');
let quizLIst = document.querySelector('.quiz');
const btn = document.querySelector('.submitBtn');
const scoreDiv = document.querySelector('.scoreCard');
let currentQuestion = 0;
let score=0;
const loadQuestion = () =>{
quest.innerText="";
quizLIst.innerHTML="";
console.log(questions[currentQuestion].question);
quest.innerText = questions[currentQuestion].question;
const ansEntries = Object.entries(questions[currentQuestion].answers);
for (const [getQ, getA] of ansEntries) {
quizLIst.innerHTML += `
<li><input type="radio" class="ansOptions" id="ent-${getQ}" name="ans" value="${getA}"/><label for="${getA}">${getA}</label></</li>
`;
}
}
loadQuestion();
const allAnss = document.querySelectorAll('.ansOptions');
let getCheckedAnswer = () =>{
let answer="";
allAnss.forEach((currAns) =>{
if(currAns.checked){
answer = currAns.id;
}
});
return answer;
}
btn.addEventListener('click', ()=> {
let checkedAnswer = getCheckedAnswer();
console.log("checked answer is ", checkedAnswer);
if(checkedAnswer === questions[currentQuestion].correctAnswer){
console.log("right");
score++;
}
else{
console.log("wrong");
}
currentQuestion++;
if(currentQuestion <= questions.length){
loadQuestion();
}
})
But somehow checkedAnswer variable is not getting updated. After first question, checkedAnswer value is not getting empty, therefore my next answers are stuck with the first answer. where can i empty the checkedAnswer value in the code, i tried doing it, but nothing seemed to be working
You need to get the current options contained in your .ansOptions. So you need to update getCheckedAnswer() for each question, as allAnss is still containing the old NodeList from the first question's options.
"use strict"
const questions = [
{
question: "whats the full form of HTML",
answers: {
a: "Hello Text My Language",
b: "Hyper text Main Language",
c: "Hyper Text Markup Language",
d: "Hi There My Luck",
},
correctAnswer:"ent-c",
},
{
question: "whats the full form of CSS",
answers: {
a: "Cascading Style Sheet",
b: "City Site Section",
c: "Cyber Section Stand",
d: "CycleStand Section",
},
correctAnswer:"ent-a",
},
{
question: "whats the full form of JSON",
answers: {
a: "Jest Oriented Note",
b: "JavaScript Object Notation",
c: "Javascript Organised Node",
d: "Joomla Of Node",
},
correctAnswer:"ent-b",
},
{
question: "whats the full form of SQL",
answers: {
a: "Super Query Language",
b: "Sorted Queue Line",
c: "Superior Query Language",
d: "Structured Query Language",
},
correctAnswer:"ent-d",
},
];
let quest = document.querySelector('.question');
let quizLIst = document.querySelector('.quiz');
const btn = document.querySelector('.submitBtn');
const scoreDiv = document.querySelector('.scoreCard');
let currentQuestion = 0;
let score=0;
const loadQuestion = () =>{
quest.innerText="";
quizLIst.innerHTML="";
console.log(questions[currentQuestion].question);
quest.innerText = questions[currentQuestion].question;
const ansEntries = Object.entries(questions[currentQuestion].answers);
for (const [getQ, getA] of ansEntries) {
quizLIst.innerHTML += `
<li><input type="radio" class="ansOptions" id="ent-${getQ}" name="ans" value="${getA}"/><label for="${getA}">${getA}</label></</li>
`;
}
}
loadQuestion();
let getCheckedAnswer = () =>{
const allAnss = document.querySelectorAll('.ansOptions')
let answer="";
allAnss.forEach((currAns) =>{
if(currAns.checked){
answer = currAns.id;
}
});
return answer;
}
btn.addEventListener('click', ()=> {
let checkedAnswer = getCheckedAnswer();
if(checkedAnswer === questions[currentQuestion].correctAnswer){
console.log("right");
++score;
} else {
console.log("wrong");
}
currentQuestion++;
if(currentQuestion < questions.length){
loadQuestion();
}
document.querySelector('.scoreCard').innerText = 'Your score is ' + score + '.';
})
<div class="scoreCard"></div>
<div class="question"></div>
<div class="quiz"></div>
<button class="submitBtn">answer</button>
I'm creating this code to create a quiz.
The undefined error shows up when I'm trying to setData to give my buttons key strings. I'm doing so to try use that to check whether the answer buttons get assigned certain classes "correct" or "incorrect".
These buttons aren't fully fleshed out yet. Because I'm stuck on this one part.
But, I get an undefined error in the selectAnswer function loop. I'm stuck and don't know what I'm doing wrong. Any pointers would be nice.
// This document, when the start button is clicked, shows questions, & starts the timer
var startButton = document.getElementById("start-btn");
var questionContainerEl = document.getElementById("question-container");
var startScreen = document.getElementById("start-screen");
var questionTitle = document.getElementById("question");
var choiceButtons = document.getElementById("choice-buttons");
//var buttonVal = 0;
var score = 0;
var shuffleQuestions, currentQindex;
var i = 0;
//Object Container to hold questions and answers
const questions = [{
question: "What is 2 + 2?",
answers: [
{text: "4", correct: "true"},
{text: "2", correct: "false"},
{text: "3", correct: "false"},
{text: "5", correct: "false"}
]
},
{
question: "What is 4*4?",
answers: [{text: '8', correct: "false"},
{text: '16',correct: "false"},
{text: '2', correct: "false"},
{text: '6', correct: "false"},
]
},
{
question: "Solve for x. y = 3x - 6",
answers: [{text: '6', correct: "false"},
{text: '3', correct: "false"},
{text: '2', correct: "false"},
{text: "idk", correct: "false"}]
}];
startButton.addEventListener('click',startGame);
//Start game function shows Question Cont. Shuffles Questions on an Arr.
function startGame() {
startScreen.classList.add('hide');
shuffleQuestions = questions.sort(() => Math.random() - .5);
currentQindex = 0;
questionContainerEl.classList.remove('hide');
setNextQuestion();
}
//Erases question and finds next one on Arr.
function setNextQuestion() {
resetState();
showQuestion(shuffleQuestions[currentQindex])
}
//Shows question as well as answers.
function showQuestion(questions) {
questionTitle.innerText = questions.question;
console.log(questionTitle);
var ansLength = Object.keys(questions.answers).length;
var ans;
for(i = 0; i < ansLength; i++) {
//When the answer button is selected questions goes to next object element
ans = questions.answers[i];
console.log(ans);
var button = document.createElement("button");
button.innerText = ans.text;
button.classList.add('btn');
if(ans.correct === "true") {
button.correct = ans.correct;
console.log("button:" + button.correct);
}
button.addEventListener("click", selectAnswer);
choiceButtons.appendChild(button);
}
}
//removes buttons from choices.
function resetState() {
while(choiceButtons.firstChild) {
choiceButtons.removeChild(choiceButtons.firstChild)
}
}
function selectAnswer(e) {
var selectedButton = e.target;
var isCorrect = selectedButton.dataset.correct;
setStatusClass(document.body, isCorrect);
var ar = Array.from(choiceButtons.children)
for (buttons in ar) {
setStatusClass(buttons, buttons.dataset.isCorrect);
}
}
function setStatusClass(element, correct) {
clearStatusClass(element);
if (correct) {
element.classList.add("correct");
} else {
element.classList.add("incorrect");
}
}
function clearStatusClass(element){
element.classList.remove("correct");
element.classList.remove("incorrect");
}'''
I have as a result from an input form a couple of strings and I want them to convert them, so they fit as data for my ajax-request. I am looking for an easy way, but I can't get it right. Basically I want to convert/map this array:
[
{ name: "[1][apples]", value: "2" }
{ name: "[1][melons]", value: "1" }
{ name: "[2][apples]", value: "2" }
{ name: "[2][melons]", value: "4" }
{ name: "[3][apples]", value: "3" }
{ name: "[3][melons]", value: "2" }
]
into
[{"id": 1, "apples": 2, "melons": 1}, {"id": 2, "apples": 2, "melons": 4}, {...}]
Any idea? I would appreciate some hint? I could't not find an easy solution via html though.
Thanks
you can use a for loop to access each element and display them.
Refer to this link. For loop in multidimensional javascript array
Firstly, I have replaced the square brackets using a regular expression and formed a new array. After that, I have merged object having same ID using spread operator.
You can refer to the code below which solves this problem.
let array = [
{ name: "[1][apples]", value: "2" },
{ name: "[1][melons]", value: "1" },
{ name: "[2][apples]", value: "2" },
{ name: "[2][melons]", value: "4" },
{ name: "[3][apples]", value: "3" },
{ name: "[3][melons]", value: "2" }];
let newArray = [];
let result = [];
array.forEach((obj, i) => {
let nameArray = obj.name.replace(/[\[\]']+/g, ' ').trim().split(' ');
let o = {};
o['id'] = parseInt(nameArray[0]);
o[nameArray[1]] = obj.value;
newArray.push(o);
});
for(let i = 0; i< newArray.length; i++) {
for(let j = i+1; j < newArray.length; j++) {
if(newArray[i].id === newArray[j].id) {
let o = {...newArray[i], ...newArray[j]};
result.push(o);`enter code here`
}
}
}
console.log('Final result', result);
Thanks for the input. I think my question needed to be more specific:
(1) Yes, they are always in order.
(2) My names of my input-tags in html appear to be an multidimensional array. This is not the case! I tried something, but it turned out to be for php.
I found the follow workaround:
function apiAdapter() {
var arrayToCopy = $("#formsteps").serializeArray();
var copiedArray = [];
for (let i = 0; i < arrayToCopy.length; i += 2) {
var id = arrayToCopy[i].name.slice(arrayToCopy[i].name.indexOf('[') + 1, arrayToCopy[i].name.indexOf(']'));
copiedArray.push({ "id": id, "apples": arrayToCopy[i].value, "melons": arrayToCopy[i + 1].value })
}
return copiedArray;
}
As I am new to JavaScript I always look for better solutions. So thanks again for your posts.
I want to filter JSON data, which I use to dynamically create a table with, by text input fields. Each table header has it's own input field.
Imagine table data like so:
var data = [{
id: "1",
A: "10",
B: "100",
C: "1000",
description: "number 1"
},
{
id: "2",
A: "20",
B: "200",
C: "2000",
description: "number 2"
},
{
id: "3",
A: "30",
B: "300",
C: "3000",
description: "number 3"
}
]
My input is stored in a separate object like so:
var filterColumnKeys = {
A: "20",
B: "43"
}
This object contains the search inputs (values) and keys based on the headers of the table (which get created dynamically by the table data).
I need to filter each object in table data with these two search inputs (20 and 43), so only objects (= rows of the table) who match the search input are shown. The filterting should be instant while typing so a partially matching phrase should be shown, too.
Example #1: nothing would be shown:
var filterColumnKeys = {
A: "20",
B: "43"
}
Example #2: the object with id 2 would be shown:
var filterColumnKeys = {
A: "20",
}
Example #3: all objects would be shown:
var filterColumnKeys = {
B: "00",
}
EDIT: I already have function to filter the whole table with ONE filter key:
// filter whole table by filter input field
if (filterKey) {
data = data.filter(function(item) {
var dataKeys = Object.keys(item);
return dataKeys.some(function(key) {
var itemValue = String(item[key]).toLowerCase();
// int (-1 if not found)
var itemFound = itemValue.indexOf(filterKey);
var shouldBeFiltered = itemFound > -1;
return shouldBeFiltered;
});
});
}
..but defining it to only check each input with the corresponding data object via the key (=column of table) doesn't work when using each key of the filterColumnKeys array.
try solution
TS:
A:any='';
B:any='';
arr: Array<any> = [{
id: "1",
A: "10",
B: "100",
C: "1000",
description: "number 1"
},
{
id: "2",
A: "20",
B: "200",
C: "2000",
description: "number 2"
},
{
id: "3",
A: "30",
B: "300",
C: "3000",
description: "number 3"
}
]
data: any;
filterData(a,b){
if(a == "00" || b == "00"){
this.data = this.arr;
} else if( a == '' || b == ''){
this.data = this.data = this.arr.find(obj => obj.A == a || obj.B == b)
} else if( a != '' && b != '' ){
this.data = this.arr.find(obj => obj.A == a && obj.B == b)
}
}
You can use Array.prototype.filter and loop through each item and check if they have corresponding keys and values in them:
var data=[{id:"1",A:"10",B:"100",C:"1000",description:"number 1"},{id:"2",A:"20",B:"200",C:"2000",description:"number 2"},{id:"3",A:"30",B:"300",C:"3000",description:"number 3"}];
var filterColumnKeys = {
A: "20"
}
function getFilteredData(data, filterColumnKeys) {
const fKeys = Object.keys(filterColumnKeys);
const filtered = data.filter(item => {
const isMatch = fKeys.every(k => {
return filterColumnKeys[k] === '00' || item[k] === filterColumnKeys[k];
})
return isMatch;
});
return filtered;
}
const result = getFilteredData(data, filterColumnKeys);
console.log(result);
Thanks for the answers and inspiration, I modified ASDFGerte's comment of my initial question and modified it a bit:
if (filterColumnKeys) {
if (Object.keys(filterColumnKeys).length !== 0) {
data = data.filter(e =>
Object.keys(filterColumnKeys).every(key =>
String(e[key])
.toLowerCase()
.includes(String(filterColumnKeys[key]).toLowerCase())
)
);
}
}
One of my main problems was the disappear of the table headers. Every time the search phrase didn't match any data with the first input, the table headers get hidden, which also hides the text input fields, so the phrase couldn't be changed.
I added checks to prevent the header hiding and the phrase-value match check is done with lower case strings.
I can also use multiple input fields to filter the data (also with partially matching phrases) at the same time with this solution.