update one list instead of duplicating it - javascript

I have a list of names with points assigned to each one, each time i click on a button related to that name it adds a point then it ranks them in the html file when pressing the rank button, everything works fine but when i make changes and click rank again it adds another list instead of updating the existing one:
let characters = [
{name: document.getElementById("name 1").textContent, points: 0},
{name: document.getElementById("name 2").textContent, points: 0},
{name: document.getElementById("name 3").textContent, points: 0}
];
function choice (button) {
const buttonCharacterObject = characters.find(obj => obj.name === button.textContent);
buttonCharacterObject.points += 1;
const ranking = characters.sort((a, b) => (a.points < b.points ? 1 : -1));
console.log(ranking);
}
function rank() {
characters.forEach(function (character) {
document.getElementById('rank').innerHTML += '<li>' + character.name + '</li>
})}
<button id="name 1" onclick="choice(this)">Martin</button>
<button id="name 2" onclick="choice(this)">Sam</button>
<button id="name 3" onclick="choice(this)">John</button>
<button onclick="rank()">Rank Characters</button>
<ol id="rank">
</ol>

As discussed below the question, the problem was that list items were being added and the existing items remained. The solution was to clear the list beforehand:
function rank() {
var el = document.getElementById('rank')
el.innerHTML = ""
characters.forEach(function (character) {
el.innerHTML += '<li>' + character.name + '</li>
})}

Related

Programmatically remove an object array, JavaScript

I'm trying to programmatically remember an object array but I'm lacking skills and I don't seem to find answers on the web either.
So, I want to be able to, while drawing a cardObj, to remove its reverse(upsidedown) alternative. Note that I wanna do the same if the reverse version is drawer first - remove the upright one from the deck.
Here is the code:
Cardz Exerz!EXERZ!Past, Present & FutureSpreadPASTPRESENTFUTURE// -----------JAVASCRIPT--------------//
// CARDS OBJECT ------------------------------------------------------------------------------------>
let cardObj = [
{name: "0_Fool", imgUrl: "data/0_Fool.jpg"},
{name: "0_Fool_R", imgUrl: "data/0_Fool_R.jpg"},
//{name: "1_Magician", imgUrl: "data/1_Magician.jpg"},
//{name: "1_Magician_R", imgUrl: "data/1_Magician_R.jpg"},
];
//--- ------------------------------------------------------------------------------------>
//SHUFFLE CARDS ------------------------------------------------------------------------------------>
function shuffle(array){
let currentIndex = array.length, randomIndex;
while(currentIndex != 0){
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
}
return array;
};
shuffle(cardObj);
//--- ------------------------------------------------------------------------------------>
//DRAW ------------------------------------------------------------------------------------>
function drawCard(id){
if(cardObj.length === 0){
return;
}
let castCard = document.getElementById(id);
let card = document.createElement('img');
card.setAttribute('src', cardObj[0].imgUrl);
card.setAttribute('height', '272');
card.setAttribute('width', '185');
card.innerHTML = cardObj[0].imgUrl;
if(castCard.id === 'imgPast'){
document.getElementById("btnPast").replaceChild(card, castCard);
}else if(castCard.id === 'imgPresent'){
document.getElementById("btnPresent").replaceChild(card, castCard);
}else if(castCard.id === 'imgFuture'){
document.getElementById("btnFuture").replaceChild(card, castCard);
}
if(cardObj[0].name === cardObj[0].name){
cardObj = cardObj.filter(function(f) {return f !== cardObj.name + "_R"});
}
cardObj.shift();
return false;
}
//--- ----------------------------------------------------------------------------------</script></body></html>
I am quite confused about your question here but I'll assume what you want is to remove the "opposite" element of the array depending on the card that's drawn.
let cardObj = [
{name: "0_Fool", imgUrl: "data/0_Fool.jpg"},
{name: "0_Fool_R", imgUrl: "data/0_Fool_R.jpg"},
{name: "1_Magician", imgUrl: "data/1_Magician.jpg"},
{name: "1_Magician_R", imgUrl: "data/1_Magician_R.jpg"},
];
function drawCard(id){
if(cardObj.length === 0) return;
// assuming that all 'reverse' cards end with '_R' and that 'id' is the 'name'
const isReverse = id.endsWith('_R');
// construct opposite card ID
const oppositeCardID = isReverse ? id.substring(0, id.lastIndexOf('_')) : (id + '_R');
// Find opposite card index in the array
const oppositeCardIndex = cardObj.findIndex(card => card.name == oppositeCardID);
if (oppositeCardIndex < 0) return; // opposite card not found
// Remove opposite card from deck
cardObj.splice(oppositeCardIndex, 1);
}
console.log("Cards before drawing:", JSON.stringify(cardObj));
console.log("Drawing card 1_Magician");
drawCard("1_Magician");
console.log("Cards after drawing:", JSON.stringify(cardObj));

Search for duplicate before adding to list

I'm trying to search for duplicates in a list before adding there any information. Could you please take a look at my code and tell me what I'm doing wrong. Thank you!
Another question is how could I iterate arrayList using Object.entries(arrayList)-loop and get the same result as I got w/o using it? When I tried to use Object.entries I was only able to see 0 [object Object], 1 [object Object] instead of names and genders.
let arrayList = [{
"name": "Brandon",
"gender": "M"
},
{
"name": "Charlotte",
"gender": "F"
},
];
let btn = document.getElementById("btn");
btn.onclick = function() {
let showHere = document.getElementById("showHere");
for (let i = 0; i < arrayList.length; i++) {
let li = document.createElement('li');
let text = `${arrayList[i].name} ${arrayList[i].gender}`;
let insert = document.createTextNode(text);
if (arrayList[i].name.indexOf(li) != -1) {
alert(`${arrayList[i].name} already in list`);
} else {
li.appendChild(insert);
showHere.appendChild(li);
}
console.log(li);
}
}
<button type="button" id="btn">Show list</button>
<ul id="showHere"></ul>
Okay I'm not 100% sure what you intended but, you may consider instead of checking for duplicates, clearing out the array before re-rendering it with new values. I modified your snippet so you can see what I'm talking about.
If you have any questions feel free to make a comment!
[Edit] I added a little snippet to check if a name gender combo already exists before adding it to the list.
let arrayList = [{
"name": "Brandon",
"gender": "M"
},
{
"name": "Charlotte",
"gender": "F"
},
];
let btn = document.getElementById("btn");
const addBtn = document.getElementById('add');
addBtn.onclick = function() {
const name = document.getElementById('name').value;
const gender = document.getElementById('gender').value;
// check if value already exists
if(!arrayList.find(obj => JSON.stringify(obj) === JSON.stringify({name, gender}))){
arrayList.push({
name,
gender
});
} else {
console.log('name gender combo already exists');
}
}
btn.onclick = function() {
let showHere = document.getElementById("showHere");
showHere.innerHTML = '';
for (let i = 0; i < arrayList.length; i++) {
let li = document.createElement('li');
let text = `${arrayList[i].name} ${arrayList[i].gender}`;
let insert = document.createTextNode(text);
if (arrayList[i].name.indexOf(li) != -1) {
alert(`${arrayList[i].name} already in list`);
} else {
li.appendChild(insert);
showHere.appendChild(li);
}
console.log(li);
}
}
<input type="text" id="name" ></input>
<input type="text" id="gender" ></input>
<button type="button" id="add">Add to list</button>
<button type="button" id="btn">Show list</button>
<ul id="showHere"></ul>

How to add attribute name and attribute value dynamically in React

I want to set value="a" depending on the condition it meets. Button has no value attribute currently. How do I write this?I want like this <button value="a"> One </button>
const buttonValues = ["a", "b"]
const addingValuesToButtons = () => {
for (var i = 0; i < buttonValues.length; i++) {
if(buttonValues[i] === "a") {
//add attribute name and value to first two buttons
}
if(buttonValues[i] === "b"){
//add attribute name and value to first three buttons
}
};
return(
<div>
<button> One </button>
<button> Two </button>
<button> Three </button>
<button> Four </button>
</div>
)
}
const buttonValues = ["a", "b"]
const addingValuesToButtons = () => {
const buttons = [];
for (var i = 0; i < buttonValues.length; i++) {
if(buttonValues[i] === "a") {
buttons.push({attr: 'foo', name: 'bar'});
}
if(buttonValues[i] === "b"){
buttons.push({attr: 'baz', name: 'bar2'})
}
};
return(
<div>
{buttons.map(button => {
return <button attr={button.attr}>{button.name}</button>;
})}
</div>
)
}
It will go like this:
<button value={this.buttonValues[0] == 'a'?this.buttonValues[0]:null}> One </button>
another netter approach is :
const buttonValues = [{value:"a",name:"One"}, {value:"b",name:"two"}]
this.buttonValues.map(value => {<button value={value.value == 'a'? value.value:null}>{value.name}</button>}) ;

How to avoid repeats after drawing in javascript?

After clicking on the button "go" one of eight words appears. I was wondering how to write a function which prohibits making repeats, so no word will be shown twice. Any ideas? Thanks for all help :)
<!DOCTYPE html>
<html>
<head></head>
<body>
<button id= "go">go</button>
<div id="word"></div>
<script type="text/javascript">
var words = ["Michael", "Simon", "Peter", "Mark", "Jason", "Paul", "Steve", "George"];
var btn1 = document.getElementById("go");
btn1.addEventListener("click", fill_in);
function getRandomItem(array) {
return array[Math.floor(Math.random() * array.length)]
}
function fill_in(){
var randomWord = getRandomItem(words);
document.getElementById('word').innerHTML += randomWord + " ";
}
</script>
</body>
</html>
You should write method getRandomItem this way.
function getRandomItem(array) {
return array.splice(Math.floor(Math.random() * array.length), 1)[0];
}
Basically, you need to remove the element after displaying it. Fortunately, Array#splice perfectly fits your case since it will both remove the element and return an array containing it.
To avoid repetition, you can remove the words from the array each time it is returned. Lets say getRandomItem returns 'Peter', so remove 'Peter' from the array 'words' before calling getRandomItem again.
To remove the element, you can use below code:
var words = ["Michael", "Simon", "Peter", "Mark", "Jason", "Paul", "Steve", "George"];
var index = words.indexOf("Peter");
if (index > -1) {
words.splice(index, 1);
}
To avoid repetition you should remove the name from the array after it has been displayed on the screen. To remove the name from the array you should find the index of the word, and then remove one item from the array at this index.
To do this you would use the following code
var word = words.indexOf(randomWord);
if(word != -1) {
words.splice(i, 1);
}
You should add this code to your fill_in function and remove the word, after the randomWord has been generated from the array. Once the array is empty you should stop printing to the screen. To check if the array is empty you should check the length of the words array.
function fill_in(){
var randomWord = getRandomItem(words);
var word = words.indexOf(randomWord);
if(word != -1) {
words.splice(i, 1);
}
if(words.length != 0){
document.getElementById('word').innerHTML += randomWord + " ";
}
}
Please see below to see the complete code in action.
var words = ["Michael", "Simon", "Peter", "Mark", "Jason", "Paul", "Steve", "George"];
var btn1 = document.getElementById("go");
btn1.addEventListener("click", fill_in);
function getRandomItem(array) {
return array[Math.floor(Math.random() * array.length)]
}
function fill_in() {
var randomWord = getRandomItem(words);
var i = words.indexOf(randomWord);
if (i != -1) {
words.splice(i, 1);
}
if(words.length != 0) {
document.getElementById('word').innerHTML += randomWord + " ";
}
}
<button id="go">go</button>
<div id="word"></div>

generate html of inifnite depth menu without recursion

I need to create html for treeview from array of unspecified number of nodes.
Here is an example
var array = [
{
Description: "G",
Id: 1,
guid: "c8e63b35",
parent: null,
Children: [
{
Description: "Z",
Id: 9,
guid: "b1113b35",
parent: "c8e63b35",
Children: [
{
Description: "F",
Id: 3,
guid: "d2cc2233",
parent: "b1113b35",
}
]
},
]
},
{
Description: "L",
Id: 2,
guid: "a24a3b1a",
parent: null,
Children: [
{
Description: "K",
Id: 4,
guid: "cd3b11caa",
parent: "a24a3b1a",
}
}
]
the result should be
<ul>
<li id="1" data-guid="c8e63b35">G
<ul>
<li id="9" data-guid="b1113b35">Z
<ul>
<li id="3" data-guid="d2cc2233">F
</li>
</ul>
</li>
</ul>
</li>
<li id="2" data-guid="a24a3b1a">L
<ul>
<li id="4" data-guid="cd3b11caa">K
</li>
</ul>
</li>
</ul>
I wrote recursion function which generate html correctly in this example but in other cases it works perfectly to 197 depth only. If nested nodes are more than 197 it thrown an exception
"The maximum call stack size"
Is there a way to do this without using recursive functions in JavaScript?
EDIT: Here is my recursion function
var _generateHTML = function (array, html) {
for (var i = 0; i < array.length; i++) {
html += "<li id=\"" + array[i].Id + "\" data-guid=\"" + array[i].guid + "\">" + array[i].Description +
"<ul>" + _generateHTML(array[i].Children, "") + "</ul>" +
"</li>";
}
return html;
}
I cannot use external libraries because this is for my work. I created this tree using recursive functions earlier. I am wondering if this is possible thats all.
This does the trick (edit: also does indentation):
function indent (num) {
var INDENT_SIZE = 4
return new Array(INDENT_SIZE * num + 1).join(" ");
}
function ulli(input) {
var CLOSE_IT = ['JUST PLEASE DO IT']
var queue = []
var output = ""
var depth = 0;
queue = queue.concat(input)
output += "<ul>\n"
depth++
while (queue.length > 0) {
var node = queue.shift()
if (node == CLOSE_IT) {
depth--
output += indent(depth)
output += "</ul></li>\n"
continue
}
output += indent(depth)
output += '<li id="' + node.Id + '" data-guid="' + node.guid + '">' + node.Description;
if (node.Children) {
depth++
output += "<ul>\n"
newQueue = [].concat(node.Children)
newQueue.push(CLOSE_IT)
queue = newQueue.concat(queue)
} else {
output += "</li>\n"
}
}
output += "</ul>"
return output
}
Build a queue and add your root elements in it, do a while on the queue and add every child to queue. For putting elements in the correct position, u need to find their parent in dom and then add them to it.
just for the sake of completeness i'm going to provide my solution aswell.
i just want you to know that you can do DOM manipulation to achieve this.
except one small thing that i'm going to try to optimize i like the solution of #franciscod alot.
if my guess is correct i'll just edit his answer.
even though this might not be the fastest solution, this way you are also able to register events to each node right away.
example of it running: http://codepen.io/GottZ/pen/jPKpaP
this includes your raw input from this question: Converting flat structure to hierarchical
as mentioned in this comment: generate html of inifnite depth menu without recursion
in my opinion you should not use an id for every element but thats up to you to decide.
code:
var content = document.getElementById('content');
var flatArray = [
{
Description: 'G',
Id: 1,
guid: 'c8e63b35',
parent: null
},
{
Description: 'Z',
Id: 9,
guid: 'b1113b35',
parent: 'c8e63b35'
},
{
Description: 'F',
Id: 3,
guid: 'd2cc2233',
parent: 'b1113b35'
},
{
Description: 'L',
Id: 2,
guid: 'a24a3b1a',
parent: null
},
{
Description: 'K',
Id: 4,
guid: 'cd3b11caa',
parent: 'a24a3b1a'
}
];
var container = document.createElement('ul');
var allNodes = {};
flatArray.forEach(function (v) {
var element = document.createElement('li');
allNodes[v.guid] = {
element: element
};
element.setAttribute('id', v.Id);
element.setAttribute('data-guid', v.guid);
element.appendChild(document.createTextNode(v.Description));
if (!v.parent) {
container.appendChild(element);
return;
}
var p = allNodes[v.parent];
if (!p.ul) {
p.ul = p.element.appendChild(document.createElement('ul'));
}
p.ul.appendChild(element);
});
content.appendChild(container);

Categories

Resources