Removing an object from an array without knowing the index - javascript

I need to remove an element from an array used the splice method, but I don't know the index of the object that I wish to remove. I've tried adding an ID that mimicks the index to remove the item but this doesn't appear to be working.
RandomiseNextQuestion: function(player1qs) {
// Pick a random question
this.Questions = player1qs;
var rand = Math.floor(Math.random() * player1qs.length);
function findQ(q) {
return q.qId === rand;
}
this.CurrentQuestion = player1qs.find(findQ);
if(this.CurrentQuestion) {
// Process question
}
// Remove that question so it can't be used again
this.Questions.splice(this.CurrentQuestion.qId, 1);
}
I've also tried using the 'rand' value to remove the item but that doesn't work either.

You can map to find the index of your element
var yourArray = ['bla','bloe','blie'];
var elementPos = yourArray.indexOf('bloe');
console.log(elementPos); // this will show the index of the element you want
yourArray.splice(elementPos,1); // this wil remove the element
console.log(yourArray);
you can do it like this I suppose
getRandomQuestion: function(playerQuestions) {
// Pick a random question and return question and index of question
this.questions = playerQuestions;
var rand = Math.floor(Math.random() * playerQuestions.length);
return this.questions[rand];
}
removeQuestion: function(question, playerQuestions){
playerQuestions.splice(playerQuestions.indexOf(question), 1);
return playerQuestions; // removes question and gives back the remaining questions
}
processQuestion: function(question){
//do something with the question
}
// you would call it like this
var questionsPlayer1 = ["the meaning of life", "the answer to everything"]
var question = getRandomQuestion(questionsPlayer1);
processQuestion(question);
removeQuestion(question, questionsPlayer1);

All you can do with a single command is a filter
arr.filter((value) => value !== removeValue)
Otherwise, if you want to keep using your array (aka mutable), you will have to use something like:
const i = arr.indexOf('value')
arr.splice(i, 1)

Related

Javascript Array.Splice() doesn't remove entry

I'm trying to remove an entry from an array if a certain condition is true, but when I console.log the array of objects, the object has not been removed and I'm confused why. Am I using the Splice() function correctly?
var itemsProcessed;
people.forEach(async function(peep, index, object){
var d = new Date();
var currenttime = d.getTime();
if (peep.endtime < currenttime){
var rolesub = guild.roles.find(r => r.name == roleNameSub);
var user2 = await client.fetchUser(peep.id);
var member = await guild.fetchMember(user2);
member.removeRole(rolesub);
object.splice(index, 1); //This seems to go wrong...
console.log(peep.id + " Lost subscription!");
user2.send("Your subscription ended!");
}
itemsProcessed++;
if (itemsProcessed === object.length){
SaveJson(people, "users.json");
}
});
Your problem is the fact you're splicing the same array you're iterating hence why the indexes will not be correct.
You should create a copy of the array before iterating it and remove elements from the original array by retrieving the index of the element you want to remove, take a look below.
arr.slice(0).forEach(function(item) {
arr.splice(arr.indexOf(item), 1);
});
var arr = [{0:0},{i:1},{i:"test"},{i:"Something else"},{i:"Test"},5];
arr.slice(0).forEach(function(item) {
if(item != 5)
arr.splice(arr.indexOf(item), 1);
});
console.log(arr);
One thing you can consider and change is that when you are iterating the array, don't delete from it while iterating. Just mark the entry for deletion. Then, when you are done, filter out the ones that need to be removed.
For example:
people.forEach( (person,i) => {
if( /* person needs to be removed from people */ ) {
people[i] = null; // use use array from 3rd parameter if you want to pass it in
}
});
// then remove unwanted people
people = people.filter( person => person != null );
Or, if you don't want to set the person to null, then you can set a flag in the object instead to mark it for deletion.
Example:
people.forEach( (person,i) => {
if( /* person needs to be removed from people */ ) {
person.toBeDeleted = true;
}
});
people = people.filter( person => person.toBeDeleted !== true );
What is probably an even better or cleaner approach is to not use forEach but just use filter only.
Example:
people = people.filter( p_person => {
if( /* person stays */ ) {
return true;
} else {
return false;
}
});

Trying to make a function to find if an array has more of one value than another

I am working on a simple project where the users are asked questions, and I am recording their answers in an array (value of 0 for yes, and a value of 1 for no). I'm just a beginner so please explain the code you send me.
I haven't tried anything but I think the best way to do it is to make a function where if the values of the array are greater than 0 it should display a certain result for the test, and if its equal to zero it should display a different result (I have no clue how to achieve this).
questionOneInputYes.addEventListener("click", function() {
if (questionTwo.style.display="none") {
questionTwo.style.display="block";
answerData.push(0);
}
})
questionOneInputNo.addEventListener("click", function() {
if (questionTwo.style.display="none") {
questionTwo.style.display="block";
answerData.push(1);
}
})
Instead of using an array, I would suggest using a dictionary with two keys: one for yes, one for no. So instead of creating an array of ones and zeroes, create a dictionary. For example:
let answers = {
'yes': 0,
'no': 0
}
Then you can increment either yes or no based on the button clicked:
questionOneInputYes.addEventListener("click", function() {
if (questionTwo.style.display="none") {
questionTwo.style.display="block";
answers['yes'] +=1;
}
})
This has the benefit of being a much more meaningful data structure (not just an array of 1's and 0's), and you already have access to the totals without any additional calculation needed, so it is trivial to check if there are more yes or no answers:
answers['yes'] > answers['no']
If you have a large number of questions, hardcoding it like above may be a pain. You could create an array of questions and loop them to generate the html. Please read the code and ask in case of doubts.
var container = document.querySelector(".questionContainer");
var questions = ["Hello?","World?","Foo?","Bar?"]; // add more questions to the array as required
var answerData = [];
/* Read on Array.prototype.reduce and Arrow Operators on MDN.
Basically it runs a for loop in the array and updates a temporary variable with some value */
var questionHTML = questions.reduce((_html,question,index) => {
/*
For each question, it creates and appends the above HTML code to the
container. For first question, set display to block and set display
none for all other question. This will be modified when someone
submits an answer. We are using the same submitAnswer function in
both buttons and changing the value based on YES/NO. ${} is used to
put the value from a variable in template strings.
*/
_html += `<div id="q-${index}" style=" display:${index===0? 'block' : 'none'}"><p> ${question} <\p>
<button onclick="submitAnswer(0,${index})"}>NO</button>
<button onclick="submitAnswer(1,${index})">YES</button>
</div>
`
return _html;
} ,"");
function submitAnswer(value, index) {
// add the value of YES/NO according to button into the answer array at the specified index
answerData[index] = value;
// console.log(answerData);
if(index < questions.length - 1){ // for all questions except the last question, -1 since the numbers are from 0...length-1 for an array , we select the currentQuestion and nextQuestion with the id we had assigned to the div during the html creation using reduce as above. Then we set current question to display none and next question to display block.
currQuestion = document.querySelector(`#q-${index}`);
nextQuestion = document.querySelector(`#q-${index+1}`);
currQuestion.style.display = "none";
nextQuestion.style.display = "block";
} else {
// after all questions are completed, show finished.
container.innerHTML = "<p>You have finished the quiz</p> answers : "+ answerData.join(',')
// do something with the answerData array
}
}
container.innerHTML = questionHTML;
<div class="questionContainer">
</div>
Let's say you have, at the end of the quiz an answerData that looks like:
[1, 1, 0, 0, 1, 0, 1, 1]
you can now use Array.prototype.reduce()
const answerData = [1, 1, 0, 0, 1, 0, 1, 1];
const noTot = answerData.reduce((acc, curr)=> acc + curr) ;
console.log( noTot ) // 5
And you'll get 5 as the result for NO answered questions
Stop repeating yourself! Programming is not meant to copy-paste code (like in your example, every question has it's own dedicated slightly-renamed copy-pasted listener and handler function...).
Instead, create a counter to keep track of the progress, you need only two buttons, and a single DIV for your questions:
const elQ = document.querySelector("#question"); // Cache elements
const elBtn = document.querySelectorAll(".answer");
const QA = [ // Array of Objects ("a:" answers will be populated during the quiz)
{q:"Do you like coding?"},
{q:"Are you tall?"},
{q:"Are you hungry?"},
{q:"Are you thirsty?"},
];
let c = 0; // Start counter at index 0 (first QA question)
const askQuestion = () => elQ.textContent = QA[c].q; // Show question
const setAnswer = (btn) => QA[c].a = parseInt(btn.value, 10); // Populate with answer
const showResult = () => {
console.log(QA);
Array.from(elBtn).forEach(el => el.disabled = true); // Disable buttons
const nopes = QA.reduce((acc, curr) => acc + curr.a, 0); // Total of NO answers
elQ.innerHTML = `
Tot questions: ${QA.length}<br>
Yes answers: ${QA.length - nopes}<br>
No answers: ${nopes}
`;
};
const handleAnswer = (ev) => {
setAnswer(ev.target);
if(c === QA.length-1 ) return showResult();
c++;
askQuestion();
};
// Events
Array.from(elBtn).forEach(el => el.addEventListener('click', handleAnswer));
// Start!
askQuestion();
<div id="question"></div>
<button class="answer" type="button" value="0">YES</button>
<button class="answer" type="button" value="1">NO</button>
If all you're trying to do is see if there are more zero's than ones in your array and vise versa, you can filter out all the 1s in your array into another array and get the length of the array containing the ones. Here I have used .filter(Boolean) which will give you an array of ones. This works because a 1 in javascript is "truthy". (This is somewhat equivalent to doing .filter(n => n===1))
Next, you can get the number of zeros in the array by subtracting the numbers of ones from the length of the array
You can then compare the number of ones and zeros using an if statement and output the associated message
See example below:
const arr = [1, 1, 1, 0, 0],
ones = arr.filter(Boolean).length, // gives length of [1, 1, 1] (3)
zeros = arr.length - ones; // gives remaining 2 ([0, 0])
console.log(ones, zeros);
if(ones > zeros) {
console.log("There are more ones in the array than zeros")
} else if(ones < zeros){
console.log("There are more zeros in the array than ones");
} else {
console.log("The same amount is in both");
}

How to make sure an item doesn't get pushed into an array twice?

I want to display four random images on my page, therefore first I am creating a random image from an initial array of images:
function randomFlag() {
var randomFlag = Math.floor(Math.random() * flags.length);
return flags[randomFlag];
}
Then I am pushing four random images into a new array:
function generateRandomFlag(num) {
var arr = [];
for (var i = 0; i < num; i++) {
arr.push(randomFlag());
}
return arr;
}
The problem I'm having is that sometimes I am getting the same image pushed twice into the array and eventually having two or more of the same image displayed instead of having four random Images.
How can I create the functionality to first check if the image already exists in the array, and push it only if it doesn't exist there already?
Thanks.
Alternative approach is to use all flags, shuffled randomly
function generateRandomFlag(num) {
return flags
.slice() // copy the flags array because .sort mutates the source array
.sort(function() { return Math.random() - 0.5; }) // shuffle the copied array
.slice(-num); // get the LAST "num" values of the shuffled array
}
In ES2015+ it's even more succinct
const generateRandomFlag = num => flags.slice().sort(() => Math.random() - 0.5).slice(-num);
You could either refactor generateRandomFlag or randomFlag. In my case I will refactor generateRandomFlag.
function generateRandomFlag(num) {
var arr = [];
var reachNum = false;
var ctr=0;
while (!reachNum) {
var flag = randomFlag();
if (arr.indexOf(flag)==-1)
{
arr.push(flag);
ctr++;
}
if (ctr==num)
reachNum = true;
}
return arr;
}

Setting up an image array using splice

I have set up a simple image array and want to ensure that each image from the array is used only once. I am new to javascript and am not sure how to implement the splice element.
A link to the full site: http://p3.katecooperuk.com
Here is my javascript array:
var calendarImg = [
"url(/images/tree.jpg)",
"url(/images/santa.jpg)",
"url(/images/stockings.jpg)",
"url(/images/snoopy.jpg)",
"url(/images/stockings2.jpg)",
"url(/images/bear.jpg)",
"url(/images/penguins.jpg)",
"url(/images/baubles.jpg)",
"url(/images/polarbear.jpg)",
"url(/images/village.jpg)",
"url(/images/village2.jpg)",
"url(/images/nativity.jpg)",
"url(/images/santa2.jpg)",
"url(/images/snowman.jpg)",
"url(/images/snow.jpg)",
]
function imgRandom(imgArr) {
return imgArr[Math.floor(Math.random() * imgArr.length)];
}
$('.doors').click(function(){
// Select Random Image
var doorImage = imgRandom(calendarImg);
// Change background image of door that was clicked
$(this).css('background-image', doorImage);
});
if you want to know more about splice, visit this MDN documentation.
function getRandomImage(arr) {
if (arr.length > 0) {
random = Math.floor(Math.random()*arr.length)
return arr.splice(random, 1)[0];
}
}
This function would return you an element from the array and delete that element from the array. So, every time you call the function, you get back an unique element until the array is empty.
If the array is empty, the function returns undefined.
If there is anything more regarding the implementation, ask away.
Your logic is almost correct. Please try:
$('.doors').bind('click',function(){
// Select Random Image
var doorImage = imgRandom(calendarImg);
// Change background image of door that was clicked
$(this).css('background-image', doorImage);
}
Also improve your random function:
function imgRandom(imgArr) {
var return_item = imgArr[Math.floor(Math.random() * imgArr.length)];
if (imgArr.length > 0) {
calendarImg = [];
var random = Math.floor(Math.random()*imgArr.length);
calendarImg = imgArr.splice(random, 1);
}
return return_item;
}

Want to add a value and remove on a single click using javascript or jquery

I am using a onclick function and adding a value to by paasing a value to it. If the value already exists den i remove that value from the list.
function send_value(str)
{
//alert(str);
var sub_id = document.getElementById('selected_sub_id').value;
//alert(sub_id.indexOf(str));
if(sub_id.indexOf(str)==-1)
{
if(sub_id=="")
{
sub_id+=str;
}
else
{
sub_id+=',';
sub_id+=str;
}
}
else
{
sub_id = sub_id.replace(str+",", "");
sub_id = sub_id.replace(","+str, "");
sub_id = sub_id.replace(str, "");
}
//alert(sub_id);
document.getElementById('selected_sub_id').value=sub_id;
}
This is the function. Suppose i have the values 1,2,3,4 in the selected_sub_id and i am passing 5 to it, it will be stored as 1,2,3,4,5
now i am passing 24 it will be stored as 1,2,3,4,5,24
No suppose i want to remove 2, so when i send 2 to the function it removes all the occurrences of 2 so i am left with only 1,3,4,54...
kindly help me with this thanks in advance..
You most probably want an array for this. It makes things much easier.
Create one first:
var arr = [];
Adding goes with .push:
arr.push(24);
Removing goes with .splice and .indexOf (the 1 means 'removing 1 element'):
arr.splice(arr.indexOf(24), 1);
Converting to a string goes with .join:
arr.join(); // 1,2,3,24 or something similar depending on elements
For example: http://jsfiddle.net/ScBNQ/.
var arr = [];
// adding
arr.push(1);
arr.push(2);
arr.push(3);
arr.push(24);
// removing
arr.splice(arr.indexOf(2), 1);
// joining
arr.join(); // 1,3,24

Categories

Resources