How to access next object in an array by incrementing - javascript

let myQuestions = [
{
question: "What's 2+2?",
answers: [
{ text: "4", correct: true },
{ text: "2", correct: false },
{ text: "10", correct: false },
{ text: "1", correct: false },
],
},
{
question: "What's 10+10?",
answers: [
{ text: "20", correct: true },
{ text: "2", correct: false },
{ text: "18", correct: false },
{ text: "0", correct: false },
],
},
];
function startQuiz() {
//container.style.visibility = "visible";
//btn_start.style.visibility = "hidden";
showQuestion(myQuestions[0]);
}
function showQuestion(questionAndAnswers) {
alert(questionAndAnswers.question);
//const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
//questionTag.innerText = questionAndAnswers.question;
//answerTag[0].innerText = shuffledAnswers[0].text;
//answerTag[1].innerText = shuffledAnswers[1].text;
//answerTag[2].innerText = shuffledAnswers[2].text;
//answerTag[3].innerText = shuffledAnswers[3].text;
}
function nextQuestion() {
showQuestion(myQuestions[0]);
}
<input type='button' value='Start Quiz' onclick=startQuiz() />
<input type='button' value='Next question' onclick=nextQuestion() />
First question gets called when I press start quiz and it access my first object in my array, how do I make the function next question work which basically access the next object by incrementing so lets say I add question 3 it keeps on going.

Take a global variable let currentQuestionIndex = 0; & update your nextQuestion function as below. Also update your startQuiz() function and set currentQuestionIndex = 0; & replace showQuestion(myQuestions[0]); with nextQuestion();
function nextQuestion() {
// Add condition so at last question it will stop at last question
if (myQuestions.length > currentQuestionIndex) {
showQuestion(myQuestions[currentQuestionIndex]);
currentQuestionIndex++;
}
}
function startQuiz() {
container.style.visibility = "visible";
btn_start.style.visibility = "hidden";
currentQuestionIndex = 0;
nextQuestion();
}
Your complete code will look like below. I have commented some of your code just for testing purpose.
let currentQuestionIndex = 0;
let myQuestions = [
{
question: "What's 2+2?",
answers: [
{ text: "4", correct: true },
{ text: "2", correct: false },
{ text: "10", correct: false },
{ text: "1", correct: false },
],
},
{
question: "What's 10+10?",
answers: [
{ text: "20", correct: true },
{ text: "2", correct: false },
{ text: "18", correct: false },
{ text: "0", correct: false },
],
},
];
function startQuiz() {
//container.style.visibility = "visible";
//btn_start.style.visibility = "hidden";
currentQuestionIndex = 0;
nextQuestion();
}
function showQuestion(questionAndAnswers) {
// temp code for alert
alert(questionAndAnswers.question);
//const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
//questionTag.innerText = questionAndAnswers.question;
//answerTag[0].innerText = shuffledAnswers[0].text;
//answerTag[1].innerText = shuffledAnswers[1].text;
//answerTag[2].innerText = shuffledAnswers[2].text;
//answerTag[3].innerText = shuffledAnswers[3].text;
}
function nextQuestion() {
// Add condition so at last question it will stop at last question
if (myQuestions.length > currentQuestionIndex) {
showQuestion(myQuestions[currentQuestionIndex]);
currentQuestionIndex++;
}
}
<input type='button' value='Start Quiz' onclick=startQuiz() />
<input type='button' value='Next question' onclick=nextQuestion() />

You can store your current question index and use that to step through the list:
let currentQuestionIndex = 0;
let myQuestions = [
{
question: "What's 2+2?",
answers: [
{ text: "4", correct: true },
{ text: "2", correct: false },
{ text: "10", correct: false },
{ text: "1", correct: false },
],
},
{
question: "What's 10+10?",
answers: [
{ text: "20", correct: true },
{ text: "2", correct: false },
{ text: "18", correct: false },
{ text: "0", correct: false },
],
},
];
function startQuiz() {
container.style.visibility = "visible";
btn_start.style.visibility = "hidden";
showQuestion(myQuestions[0]);
}
function showQuestion(questionAndAnswers) {
const shuffledAnswers = _.shuffle(questionAndAnswers.answers);
questionTag.innerText = questionAndAnswers.question;
answerTag[0].innerText = shuffledAnswers[0].text;
answerTag[1].innerText = shuffledAnswers[1].text;
answerTag[2].innerText = shuffledAnswers[2].text;
answerTag[3].innerText = shuffledAnswers[3].text;
}
function nextQuestion() {
const nextIndex = currentQuestionIndex + 1;
if (nextIndex < myQuestions.length - 1) {
showQuestion(myQuestions[nextIndex]);
currentQuestionIndex = nextIndex;
} else {
console.log('end of question list');
}
}

Related

Update the data from spliced array of object

So, I have splice the data and I want to update it's object from disabled = true to disabled = false .. I have looking for another answer and cant find one..
Any Advice is appreciated. Thank you
this is my dropdown
const newDrop = [
{ key: "1", label: "Monthly GMV", sublabel: "Max.1" },
{ key: "2", label: "AOV", sublabel: "Max.1" },
{ key: "3", label: "Monthly Order", sublabel: "Max.1" },
{ key: "4", label: "Last Purchase", sublabel: "Max.1" },
{ key: "5", label: "Has Purchase" },
{ key: "6", label: "Has Purchase Spesific Product" },
{ key: "7", label: "Located In", sublabel: "Max.1" },
];
selected function : when selected the dropdown, it will add new attribute disabled:true
newInput({ key }) {
const {
changeFormAttribute,
form: { selectedDrop, newDrop, data },
} = this.props;
itemIndex = newDrop.map((itm) => {
let x = itm.key === key;
return x && itm.key !== "6" && itm.key !== "5" ? { ...itm, disabled: true } : { ...itm };
});
changeFormAttribute({
newDrop: itemIndex,
});
}
delete function: this is onClick function, when clicked, it should splice the data, and check, if the spliced data have disabled === true change it to disabled:false . the spliced have working properly, but not change the attribute to disabled = false
deleteInput(index) {
const {
changeFormAttribute,
form: { selectedDrop },
} = this.props;
let selected = selectedDrop || [];
selected.splice(index, 1).map((e) => {return e.disabled === true ? false:true })
changeFormAttribute({ selectedDrop: selected });
}
Your splice function not returning the element. You may need to change it to
selected.splice(index, 1).map((e) => {
e.disabled === true ? false:true
return e;
})

How do I set properties on parent object if any children (recursive) match a value

I have an array:
let testData = [
{
name: "fruit",
checked: false,
isOpen: false,
childrens: [
{
name: "apple",
checked: false,
isOpen: false,
childrens: [
{
name: "one",
checked: false
}
]
},
]
},
{
name: "animal",
checked: false,
isOpen: false,
childrens: [
{
name: "people",
checked: false
},
{
name: "123",
checked: false
}
]
}
]
I want to select one that includes its superiors.
I try to use the code myself:
function recursive(name, arr) {
arr.forEach((item) = > {
test(name, item)
});
arrData = arr
}
function test(name, item) {
if (item.childrens && item.childrens.length > 0) {
item.childrens.forEach(sub = > {
test(name, sub)
})
}
if (name == item.name) {
item.isOpen = !item.isOpen
item.checked = !item.checked
}
return item
}
var arrData = []
let name = "one"
recursive(name, testData)
But I chose one, I didn't select fiuit apple
The result I want is this:
let result = [
{
name: "fruit",
checked: true,
isOpen: true,
childrens: [
{
name: "apple",
checked: true,
isOpen: true,
childrens: [
{
name: "one",
checked: true
}
]
},
]
},
{
name: "animal",
checked: false,
isOpen: false,
childrens: [
{
name: "people",
checked: false
},
{
name: "123",
checked: false
}
]
}
]
Could you help me? thank you
You can combine your recursive and test functions into one function; checking whether the current object or any of its children matches the name, toggling the checked and isOpen flags and returning true if so:
function test(name, arr) {
found = false;
arr.forEach(o => {
if (o.name == name || o.childrens && test(name, o.childrens)) {
o.checked = !o.checked;
o.isOpen = !o.isOpen;
found = true;
}
});
return found;
}
let testData = [{
name: "fruit",
checked: false,
isOpen: false,
childrens: [{
name: "apple",
checked: false,
isOpen: false,
childrens: [{
name: "one",
checked: false
}]
}, ]
},
{
name: "animal",
checked: false,
isOpen: false,
childrens: [{
name: "people",
checked: false
},
{
name: "123",
checked: false
}
]
}
]
function test(name, arr) {
found = false;
arr.forEach(o => {
if (o.name == name || o.childrens && test(name, o.childrens)) {
o.checked = !o.checked;
o.isOpen = !o.isOpen;
found = true;
}
});
return found;
}
test('one', testData);
console.log(testData);
test('one', testData);
console.log(testData);
You've almost got it. Though you ran the function on each child, you didn't check what the result of that was.
In the code below I reordered the test function slightly and have added comments to help.
I noticed that you currently don't handle what happens if the parent was to get unchecked - would it then uncheck all children recursively?
function test(name, item) {
let closeChildren = false,
uncheckChildren = false;
// do this first so we don't modify after checking children
if(item.name === name){
if(item.isOpen){
// close item
item.isOpen = false;
// flag children to be closed
closeChildren = true;
}else{
item.isOpen = true;
}
if(item.checked){
// uncheck item
item.checked = false;
// flag children to be unchecked
uncheckChildren = true;
}else{
item.checked = true;
}
}
// make sure the current item has `childrens` property
if(item.childrens !== undefined){
for(let i = 0, l = item.childrens.length;i < l;i++){
// check what the result was of running `test` on this child
let result = test(name, item.childrens[i]);
if(closeChildren){
item.childrens[i].isOpen = false;
}else{
// set `item.isOpen` to true if the result is true
// we don't do this when it is false otherwise the parent
// would get unchecked if any of its children are unchecked
if(result.isOpen){
item.isOpen = true;
}
}
if(uncheckChildren){
item.childrens[i].checked = false;
}else{
// same as above but for `item.checked`
if(result.checked){
item.checked = true;
}
}
}
}
return item;
}
i use backtracking and should works for all occurence
and i use set and get function from Lodash
let testData = [
{
name: "fruit",
checked: false,
isOpen: false,
childrens: [
{
name: "apple",
checked: false,
isOpen: false,
childrens: [
{
name: "one",
checked: false
}
]
},
]
},
{
name: "animal",
checked: false,
isOpen: false,
childrens: [
{
name: "people",
checked: false
},
{
name: "123",
checked: false
}
]
}
]
class DeepChecker {
paths;
value = false;
backtrack = (data, name, current) => {
if(data.name === name){
this.paths.push([...current]);
return;
}
if(!data.childrens){
if(data.name === name){
this.paths.push([...current])
}
return;
}
for(let i = 0; i < data.childrens.length; i++){
const children = data.childrens[i];
current.push(i.toString())
current.push("childrens");
this.backtrack(children, name, [...current]);
current.pop();
current.pop();
}
}
find = (data, name) => {
for(let i = 0; i < data.length; i++){
this.backtrack(data[i], name, [i.toString(), "childrens"])
}
}
set = (data, path) =>{
const canBeOpen = _.get(data, [...path, "isOpen"]) !== undefined
if(canBeOpen){
data = _.set(data, [...path, "isOpen"], this.value)
}
return _.set(data, [...path, "checked"], this.value)
}
deepCheck = (data, path) => {
for(let i = 1; i < path.length; i+=2){
const subpath = path.slice(0, i);
data = this.set(data, subpath)
}
return data;
}
check = (data, name, value) =>{
this.paths = []
this.value = value;
this.find(data, name);
for(let i = 0; i < this.paths.length; i++){
data = this.deepCheck(data, this.paths[i])
}
return data;
}
}
const checker = new DeepChecker();
let checked = checker.check(testData, "one", true)
checked = checker.check(checked, "one", false)
checked = checker.check(checked, "people", true)
console.log(JSON.stringify(checked, null, "\t"));

How to get inside this if statement (JavaScript)

I'm creating this simple quiz with 6 questions that I'm storing in an array. What I'm stuck with is that when you've reached the last question and you click 'Next', I'd like to show your results along with a text and a 'Restart' button.
What happens now though is that it show the last question again but without any answer-butons (I'm not getting into that if statement).
I'm pretty new to js, here's my code.
const startQuiz = document.querySelector('.container__start-btn');
const theQuestion = document.querySelector('.container__question');
const fourAnswers = document.querySelector('.container__answers');
const counter = document.querySelector('.container__counter__correct-answer');
const questionCounter = document.querySelector('.container__counter__question-nr');
const containerCounter = document.querySelector('.container__counter');
let currentQuestion = 0;
let count = 0;
const showQuestion = () => {
startQuiz.innerText = 'NEXT';
while (fourAnswers.firstChild) {
fourAnswers.removeChild(fourAnswers.firstChild);
}
theQuestion.innerText = questions[currentQuestion].question;
checkCounters();
}
const checkCounters = () => {
counter.textContent = `Score: ${count}`;
questionCounter.textContent = `Question: ${currentQuestion + 1}/${questions.length}`;
generateAnswers();
}
const generateAnswers = () => {
questions[currentQuestion].answers.forEach(answer => {
const button = document.createElement('button');
button.innerText = answer.answer;
if (answer.correct) {
button.classList.add('right');
}
button.classList.add('btn');
fourAnswers.appendChild(button);
fourAnswers.classList.remove('hide');
})
fourAnswers.onclick = () => {
const selected = event.target;
if (selected.classList.contains('right')) {
selected.classList.add('selectedRight');
count++;
counter.textContent = `Score: ${count}`;
}
if (!selected.classList.contains('right')) {
selected.classList.add('selectedWrong');
}
const rightA = document.querySelector('.right');
rightA.classList.add('selectedRight');
}
currentQuestion++;
if (currentQuestion > questions.length) {
console.log('inside if');
startQuiz.innerText = 'START';
theQuestion.innerText = `Good job! You got ${count} correct answers out of ${questions.length}. Press 'START' to go again.`;
count = 0;
currentQuestion = 0;
}
}
const questions = [
{
question: 'What is the capital of Greece?',
answers: [
{ answer: 'Athens', correct: true },
{ answer: 'Gothenburg', correct: false },
{ answer: 'Madrid', correct: false },
{ answer: 'Berlin', correct: false }
]
},
{
question: 'What is the capital of Sweden?',
answers: [
{ answer: 'Stockholm', correct: true },
{ answer: 'Lisboa', correct: false },
{ answer: 'Paris', correct: false },
{ answer: 'New York', correct: false }
]
},
{
question: 'What is the capital of Portugal?',
answers: [
{ answer: 'Lisboa', correct: true },
{ answer: 'Valencia', correct: false },
{ answer: 'Porto', correct: false },
{ answer: 'London', correct: false }
]
},
{
question: 'What is the capital of Argentina?',
answers: [
{ answer: 'Buenos Aires', correct: true },
{ answer: 'Santiago', correct: false },
{ answer: 'Amsterdam', correct: false },
{ answer: 'Beijing', correct: false }
]
},
{
question: 'What is the capital of Thailand?',
answers: [
{ answer: 'Bangkok', correct: true },
{ answer: 'Manila', correct: false },
{ answer: 'Rome', correct: false },
{ answer: 'Nicosia', correct: false }
]
},
{
question: 'What is the capital of Denmark?',
answers: [
{ answer: 'Copenhagen', correct: true },
{ answer: 'Oslo', correct: false },
{ answer: 'Beirut', correct: false },
{ answer: 'Los Angeles', correct: false }
]
}
]
startQuiz.addEventListener('click', showQuestion);
You should change the line 51 as follows.
if (currentQuestion >= questions.length) {
This is because the questions.length is 6 and the counter currentQuestion is 6 at the last question.
-- edit --
OK, try to modify like the following.
const showQuestion = () => {
if (currentQuestion >= questions.length) {
console.log('inside if');
startQuiz.innerText = 'START';
theQuestion.innerText = `Good job! You got ${count} correct answers out of ${questions.length}. Press 'START' to go again.`;
count = 0;
currentQuestion = 0;
return;
}
startQuiz.innerText = 'NEXT';
while (fourAnswers.firstChild) {
fourAnswers.removeChild(fourAnswers.firstChild);
}
theQuestion.innerText = questions[currentQuestion].question;
checkCounters();
currentQuestion++;
}
And delete currentQuestion++; from generateAnswers()
Difficult to be sure without a full reproducible jsfiddle, but I think it should be:
if (currentQuestion >= questions.length)
instead of:
if (currentQuestion > questions.length)
Put your if in the beginning of the function and return inside of it so that you exit the function and do nothing else once you go into the if.
Currently what's happening is that the foreach is triggered even when the currentQuestion has passed the questions.length.
Also add the check to be more or equal.
const generateAnswers = () => {
if (currentQuestion >= questions.length) {
console.log('inside if');
startQuiz.innerText = 'START';
theQuestion.innerText = `Good job! You got ${count} correct answers out of ${questions.length}. Press 'START' to go again.`;
count = 0;
currentQuestion = 0;
return; // end the function here so the other logic doesn't trigger.
}
questions[currentQuestion].answers.forEach(answer => {
const button = document.createElement('button');
button.innerText = answer.answer;
if (answer.correct) {
button.classList.add('right');
}
button.classList.add('btn');
fourAnswers.appendChild(button);
fourAnswers.classList.remove('hide');
})
fourAnswers.onclick = () => {
const selected = event.target;
if (selected.classList.contains('right')) {
selected.classList.add('selectedRight');
count++;
counter.textContent = `Score: ${count}`;
}
if (!selected.classList.contains('right')) {
selected.classList.add('selectedWrong');
}
const rightA = document.querySelector('.right');
rightA.classList.add('selectedRight');
}
currentQuestion++;
}

JS Split array into X chunks, then Y chunks and so on

How exactly could I split an array into 6 chunks, then 3 chunks, then 6 chunks, then 3 chunks and so on?
So say I have this dataset:
const datasaet = [
{ text: 'hi1' },
{ text: 'hi2' },
{ text: 'hi3' },
{ text: 'hi4' },
{ text: 'hi5' },
{ text: 'hi6' },
{ text: 'hi7' },
{ text: 'hi8' },
{ text: 'hi9' },
{ text: 'hi10' },
{ text: 'hi11' },
{ text: 'hi12' },
{ text: 'hi13' },
{ text: 'hi14' },
{ text: 'hi15' },
{ text: 'hi16' },
]
and I need to split it into an array like this:
const expected = [
[
{ text: 'hi1' },
{ text: 'hi2' },
{ text: 'hi3' },
{ text: 'hi4' },
{ text: 'hi5' },
{ text: 'hi6' },
],
[
{ text: 'hi7' },
{ text: 'hi8' },
{ text: 'hi9' },
],
[
{ text: 'hi10' },
{ text: 'hi11' },
{ text: 'hi12' },
{ text: 'hi13' },
{ text: 'hi14' },
{ text: 'hi15' },
{ text: 'hi16' },
]
]
Essentially what I'm doing is splitting the array into 6 chunks if it's event and 3 chunks if it's odd.
However I don't know exactly how to go about doing this. My current attempt looks like this, I can split it into 6 chunks perfectly but how do I go about doing the next 3, and then the next 6 and so on:
const grouped = datasaet.reduce(
(initital: any[], current, index, items) => {
const isFirstOfSix = index % 6 === 0
if (isFirstOfSix) {
const nextSix = items.slice(index, index + 6)
initital.push(nextSix)
}
return initital
},
[]
)
You might consider creating a copy of the array (to avoid mutating the original), then splicing out items until it's empty, checking and toggling a boolean that indicates whether to remove 6 or 3 items on the current iteration:
const datasaet = [
{ text: 'hi1' },
{ text: 'hi2' },
{ text: 'hi3' },
{ text: 'hi4' },
{ text: 'hi5' },
{ text: 'hi6' },
{ text: 'hi7' },
{ text: 'hi8' },
{ text: 'hi9' },
{ text: 'hi10' },
{ text: 'hi11' },
{ text: 'hi12' },
{ text: 'hi13' },
{ text: 'hi14' },
{ text: 'hi15' },
{ text: 'hi16' },
]
const tempArr = datasaet.slice();
const output = [];
let removeSix = true;
while (tempArr.length) {
output.push(tempArr.splice(0, removeSix ? 6 : 3));
removeSix = !removeSix;
}
console.log(output);
You can create a function that takes an array, and array of chunk sizes. The function iterates the array, cycles between the chunk sizes, and uses slice to take the current chunk size from the original array:
const chunks = (arr, chunkSize) => {
const result = [];
let current = -1;
for (let i = 0; i < arr.length; i += chunkSize[current]) {
current = (current + 1) % chunkSize.length;
result.push(arr.slice(i, i + chunkSize[current]));
}
return result;
}
const dataset = [{"text":"hi1"},{"text":"hi2"},{"text":"hi3"},{"text":"hi4"},{"text":"hi5"},{"text":"hi6"},{"text":"hi7"},{"text":"hi8"},{"text":"hi9"},{"text":"hi10"},{"text":"hi11"},{"text":"hi12"},{"text":"hi13"},{"text":"hi14"},{"text":"hi15"},{"text":"hi16"}];
const result = chunks(dataset, [6, 3]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to traverse in a tree and filter matching nodes in javascript?

I have tried to filter nodes, but succeed to filter only root nodes.
My implementation is below.
How can I use a methodology to filter also sub-nodes?
function getMatchedValues(nodes)
{
var leafContainsText = false;
for (var i = 0; i < nodes.length; i++)
{
if (nodes[i].items && nodes[i].items.length > 0)
{
leafContainsText = getMatchedValues(nodes[i].items);
if (leafContainsText)
break;
}
else
{
if (nodes[i].text.toLowerCase().indexOf($scope.filterReports.toLowerCase()) !== -1)
{
leafContainsText = true;
break;
}
}
}
return leafContainsText;
}
$scope.temp_reports = [];
for (var i = 0; i < $scope.reports.length; i++)
{
if ($scope.reports[i].items && $scope.reports[i].items.length > 0)
{
if (getMatchedValues($scope.reports[i].items))
$scope.temp_reports.push($scope.reports[i]);
else if ($scope.reports[i].text.toLowerCase().indexOf($scope.filterReports.toLowerCase()) !== -1)
$scope.temp_reports.push($scope.reports[i]);
}
else
{
if ($scope.reports[i].text.toLowerCase().indexOf($scope.filterReports.toLowerCase()) !== -1)
$scope.temp_reports.push($scope.reports[i]);
}
}
The tree object($scope.reports) looks likes below:
[
{
text:"abc",
items:[
{
text:"abf"
},
{
text:"abd",
items:[
{
text:"bbb"
},
{
text:"dba"
}
]
}
]
},
{
text:"def",
items:[
{
text:"ddf"
},
{
text:"ddd",
items:[
{
text:"dfg"
},
{
text:"dba",
items:[
{
text:"qqq"
},
{
text:"www"
}
]
}
]
}
]
}
]
For example, If want to filter nodes that contain 'd' then result tree should look like this;
[
{
text:"abc",
items:[
{
text:"abd",
items:[
{
text:"dba"
}
]
}
]
},
{
text:"def",
items:[
{
text:"ddf"
},
{
text:"ddd",
items:[
{
text:"dfg"
},
{
text:"dba",
items:[]
}
]
}
]
}
]
You could filter the array by filtering the items array as well.
This solution mutates the original data.
function filter(array, search) {
return array.filter(function f(o) {
var t;
if (o.items) {
t = o.items.filter(f);
}
if (o.text.includes(search) || t && t.length) {
if (t) {
o.items = t;
}
return true;
}
});
}
var array = [{ text: "abc", items: [{ text: "abf" }, { text: "abd", items: [{ text: "bbb" }, { text: "dba" }] }] }, { text: "def", items: [{ text: "ddf" }, { text: "ddd", items: [{ text: "dfg" }, { text: "dba", items: [{ text: "qqq" }, { text: "www" }] }] }] }],
result = filter(array, 'd');
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources