I'm relatively new to Vue, so if this is a stupid question I apologize.
Is it possible to use either ES6 classes or a class function in a Vue object? I'm trying to implement an AI for my Tic-Tac-Toe project using the Minimax algorithm, and I need to make a constructor for my States. But in the Vue object, references to 'this' refer to the Vue object itself, even when wrapped in a class function it would seem. So I'm wondering how I could get around this, or if there is an easier way to accomplish this. Thanks!
Here is the link I'm using for the Minimax algo.
And the link to my Codepen.
console.clear();
const log = console.log.bind(console);
const game = new Vue({
el: '#app',
data: {
turn: 'X',
counter: 0,
winner: '',
started: false,
over: false,
header: {
X: 'color',
O: ''
},
sound() {
const tick = new Audio('https://dl.getdropbox.com/s/kgqqnei0yhv3r8n/219069__annabloom__click1.wav')
if(this.over)
tick.play();
},
board: [[{val:'',bg:''}, {val:'',bg:''}, {val:'',bg:''}],
[{val:'',bg:''}, {val:'',bg:''}, {val:'',bg:''}],
[{val:'',bg:''}, {val:'',bg:''}, {val:'',bg:''}]],
windex: [[[0,0], [0,1], [0,2]],
[[1,0], [1,1], [1,2]],
[[2,0], [2,1], [2,2]],
[[0,0], [1,0], [2,0]],
[[0,1], [1,1], [2,1]],
[[0,2], [1,2], [2,2]],
[[0,0], [1,1], [2,2]],
[[0,2], [1,1], [2,0]]],
arr() {
return this.board.map( x => x.map( y => y.val ));
},
winArr() {
return this.windex.map( x => x.map( y => this.board[y[0]][y[1]].val ));
},
check() {
const winArr = this.winArr();
const checkWindex = winArr.map( (x,ind) => {
if( x.every( y => y == 'X' )) return 'X';
if( x.every( y => y == 'O' )) return 'O';
});
if(checkWindex.includes('X'))
this.setWin('X',checkWindex)
else if(checkWindex.includes('O'))
this.setWin('O',checkWindex)
else if(this.counter == 9) {
this.winner = 'DRAW';
setTimeout(() => this.over = true, 0);
};
}
},
methods: {
mark(box) {
this.header[this.turn] = 'color';
if(this.over) return
if(box.val === ''){
box.val = this.turn;
this.turn = this.turn == 'X' ? 'O' : 'X';
}
this.counter++;
this.check();
this.header.X = this.turn == 'X' ? 'color' : '';
this.header.O = this.turn == 'O' ? 'color' : '';
},
setWin(val,arr) {
const inds = this.windex[arr.indexOf(val)];
inds.forEach( x => {
this.board[x[0]][x[1]].bg = 'active';
});
this.winner = val;
setTimeout(() => this.over = true, 0);
},
start(mode) {
this.started = true;
this.counter = 0;
this.board.map( x => x.map( y => { y.val = ''; y.bg = '' }));
this.over = false;
}
}
});
Related
For some reason I have variables outside of my function and I'm updating that variable in my function but when I call that variable in another function I get a undefined typeError
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
You are in the startRide() function not returning the result of each assignment in the .map method, so it returns undefined which why you see the array of undefined values.
This should fix it:
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
return bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue');
const bike_7 = createBike('green');
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
To lift this out of comment, the body of the map argument function in startRide is enclosed in curly braces. You could remove the braces or put return bike inside the braces to stop it returning undefined.
However, setting bike.vin to a bike "payload" object with checkout set to true, leaving bike.checkout set to false, is a bug. One solution might be to use find instead of map:
let bikeShare = []
let stations = []
function startRide(vin, start = true) {
const bike = bikeShare.find(bike=>bike.vin === vin);
if( bike) {
bike.checkOut = start;
}
return bike; // for debugging
}
function endRide(vin) {
return startRide( vin, false);
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
console.log( startRide(bike_1.vin));
console.log( endRide(bike_1.vin));
I'm creating a quiz that requires every answer to be answered. The problem is that you should be able to skip questions if you don't have an answer. I'm trying to set a default answer everytime I press next so when I try to skip one I don't have to answer for it to have a value. The default value I want is each time the last value of my array.
next and previous question
SetQuestion(question) {
if (this.questionNumber >= 0) {
let oldAnswerButton = document.querySelectorAll('.filter_anwser');
// Deletes old question when the next question is clicked
for (let answerButton of oldAnswerButton) {
answerButton.style.display = 'none';
}
}
this.questionNumber = question;
let q = this.quiz[question];
// Check if your at the last question so the next button will stop being displayed.
if (this.questionNumber === Quiz.length - 1) {
this.nextbtn.style.display = 'none';
this.prevbtn.style.display = 'block';
this.resultbtn.style.display = 'grid';
} else if (this.questionNumber === 0) {
this.nextbtn.style.display = 'block';
this.prevbtn.style.display = 'none';
this.resultbtn.style.display = 'none';
} else {
this.nextbtn.style.display = 'block';
this.prevbtn.style.display = 'block';
this.resultbtn.style.display = 'none';
}
// Displays Question
this.questionName.textContent = q.questionText;
this.questionName.id = "questionID";
return q;
console.log(this.getLink())
console.log(this.tmp)
}
IntoArray() {
const UrlVar = new URLSearchParams(this.getLink())
this.UrlArray = [...UrlVar.entries()].map(([key, values]) => (
{[key]: values.split(",")}
)
);
}
NextQuestion() {
// let quizUrl = this.url[this.questionNumber];
let question = this.SetQuestion(this.questionNumber + 1);
let pre = question.prefix;
let prefixEqual = pre.replace('=', '');
let UrlArr = this.UrlArray;
let UrlKeys = UrlArr.flatMap(Object.keys)
let answers = question.chosenAnswer.slice(0, -1);
this.clicked = true;
// Displays answers of the questions
for (let y = 0; y < answers.length; y++) {
let item = answers[y];
// Display answer buttons
if (UrlKeys.includes(prefixEqual)) {
console.log("exists");
let btn = document.querySelector('button[value="' + item.id + '"]');
btn.style.display = 'block';
} else {
let btn = document.createElement('button');
btn.value = item.id;
btn.classList.add("filter_anwser", pre)
btn.id = 'answerbtn';
btn.textContent = item.name;
this.button.appendChild(btn);
}
// let quizUrl = control.url[control.questionNumber];
// // console.log(this.tmp);
// if (quizUrl === undefined) {
// quizUrl.push(question.prefix[y] + '');
// }
// if (quizUrl === undefined){
// this.tmp.push('');
// }
}
this.IntoArray();
}
PrevQuestion() {
let question = this.SetQuestion(this.questionNumber - 1);
let answers = question.chosenAnswer.slice(0, -1);
// Displays answers of the questions
for (let y = 0; y < answers.length; y++) {
let item = answers[y];
// Display answer buttons
let btn = document.querySelector('button[value="' + item.id + '"]');
btn.style.display = 'block';
}
this.IntoArray();
}
Link creator:
/**
* Returns the parameters for the URL.
*
* #returns {string}
*/
getLink() {
this.tmp = [];
for (let i = 0; i < this.url.length; i++) {
// Check if question is from the same quiz part and adds a , between chosen answers and add the right prefix at the beginning
if (this.url[i].length > 0) {
this.tmp.push("" + Quiz[i].prefix + this.url[i].join(","))
// console.log(this.url)
}
if (this.url[i].length === 0) {
this.tmp.push("");
}
}
/// If answers are from different quiz parts add a & between answers.
return "" + this.tmp.join("&");
// console.log(this.url[i].prefix);
};
Answer click event
control.button.addEventListener("click", function (e) {
const tgt = e.target;
control.clicked = true;
// clear the url array if there's nothing clicked
if (control.url.length === control.questionNumber) {
control.url.push([]);
}
let quizUrl = control.url[control.questionNumber];
// Check if a button is clicked. Changes color and adds value to the url array.
if (quizUrl.indexOf(tgt.value) === -1) {
quizUrl.push(tgt.value);
e.target.style.backgroundColor = "orange";
// Check if a button is clicked again. If clicked again changes color back and deletes value in the url array.
} else {
quizUrl.splice(quizUrl.indexOf(tgt.value), 1);
e.target.style.backgroundColor = "white";
}
console.log(control.getLink());
console.log(quizUrl)
})
array:
class QuizPart {
constructor(questionText, chosenAnswer, prefix, questionDescription) {
this.questionText = questionText;
this.chosenAnswer = chosenAnswer;
this.prefix = prefix;
this.questionDescription = questionDescription;
}
}
class ChosenAnswer {
constructor(id, name) {
this.id = id;
this.name = name;
}
}
let Quiz = [
new QuizPart('Whats your size?', [
new ChosenAnswer('6595', '41'),
new ChosenAnswer('6598', '42'),
new ChosenAnswer('6601', '43'),
new ChosenAnswer('', ''),
], 'bd_shoe_size_ids=',
'The size of your shoes is very important. If you have the wrong size, they wont fit.'),
new QuizPart('What color would you like?', [
new ChosenAnswer('6053', 'Red'),
new ChosenAnswer('6044', 'Blue'),
new ChosenAnswer('6056', 'Yellow'),
new ChosenAnswer('6048', 'Green'),
new ChosenAnswer('', ''),
], 'color_ids=',
'Color isn t that important, It looks good tho.'),
new QuizPart('What brand would you like?', [
new ChosenAnswer('5805', 'Adidas'),
new ChosenAnswer('5866', 'Nike'),
new ChosenAnswer('5875', 'Puma'),
new ChosenAnswer('', ''),
], 'manufacturer_ids=',
'Brand is less important. Its just your own preference'),
]
I tried giving the array's in link creator and my eventlistener a default value and replacing it when I get and actual value from one of my buttons, but it just doesn't work. Can anybody help me?
I understand, that it might be a bit far from what you expect for an answer - but why don't you have a look at a reactive tool, like Vue? It has all the tools that you might need for such a task, and maybe more:
the whole quiz can be abstracted to a simple array of objects (the questions)
next, prev, set default answer becomes a breeze
easy to extend (with questions)
simple to update (template, features, etc.)
Vue.component('QuizQuestion', {
props: ['data', 'selected'],
computed: {
valSelected: {
get() {
return this.selected
},
set(val) {
this.$emit('update:selected', val)
}
},
},
template: `
<div>
{{ data.text }}<br />
{{ data.description }}<br />
<div class="quiz-options">
<label
v-for="val in data.options"
:key="val[0]"
>
<input
type="radio"
:name="data.text"
:value="val"
v-model="valSelected"
/>
{{ val[1] }}
</label>
</div>
</div>
`
})
new Vue({
el: "#app",
computed: {
currentQuestion() {
return this.questions[this.current]
},
hasPrev() {
return !!this.current
},
hasNext() {
return this.current < this.questions.length - 1
},
},
data() {
return {
current: 0,
questions: [{
text: 'Whats your size?',
description: 'The size of your shoes is very important. If you have the wrong size, they wont fit.',
options: [
['6595', '41'],
['6598', '42'],
['6601', '43'],
['', ''],
],
selected: null,
}, {
text: 'What color would you like?',
description: 'Color isn\'t that important, It looks good tho.',
options: [
['6053', 'Red'],
['6044', 'Blue'],
['6056', 'Yellow'],
['6048', 'Green'],
['', ''],
],
selected: null,
}, {
text: 'What brand would you like?',
description: 'Brand is less important. Its just your own preference',
options: [
['5805', 'Adidas'],
['5866', 'Nike'],
['5875', 'Puma'],
['', ''],
],
selected: null,
}, ],
}
},
methods: {
selectDefault() {
this.questions[this.current] = {
...this.questions[this.current],
selected: this.questions[this.current].options.slice(-1)[0],
}
},
getPrev() {
if (this.hasPrev) {
if (!this.currentQuestion.selected) {
this.selectDefault()
}
this.current -= 1
}
},
getNext() {
if (this.hasNext) {
if (!this.currentQuestion.selected) {
this.selectDefault()
}
this.current += 1
}
},
},
template: `
<div>
<quiz-question
:data="currentQuestion"
:selected.sync="currentQuestion.selected"
/><br />
<button v-if="hasPrev" #click="getPrev">PREV</button>
<button v-if="hasNext" #click="getNext">NEXT</button>
<button v-if="!hasNext">RESULT</button>
</div>
`
})
.quiz-options {
display: flex;
flex-direction: column;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
EDIT
But, if frameworks/libraries are not to be used, here's a more OOP approach:
class Quiz {
constructor(questions) {
this._current = 0
this._questions = questions
}
get current() {
return this._current
}
set current(val) {
this._current = val
}
get hasNext() {
return this.current < this.questions.length - 1
}
get hasPrev() {
return !!this.current
}
get questions() {
return this._questions
}
get next() {
this.current = this.hasNext ? this.current + 1 : this.current
return this.currentQuestion
}
get prev() {
this.current = this.hasPrev ? this.current - 1 : this.current
return this.currentQuestion
}
get currentQuestion() {
return this.questions[this.current]
}
}
class Question {
constructor({
text,
description,
options,
prefix,
}) {
this.text = text
this.desc = description
this.prefix = prefix
this._options = options.map(([key, val]) => ({
id: key,
value: [key, val],
selected: false,
}))
}
get options() {
return this._options
}
set options(newOptions) {
this._options = newOptions
}
get selected() {
return this.options.find(({
selected
}) => !!selected)
}
set selected(selectedVal) {
this.options = [...this.options.map(({
value: [key, val],
selected,
...rest
}) => {
return {
...rest,
value: [key, val],
selected: key === selectedVal
}
})]
}
get lastOption() {
return this.options.slice(-1)[0]
}
setDefault() {
if (!this.selected) {
this.selected = this.lastOption.id
}
}
}
const urlParser = (quiz) => {
return quiz.questions.map(({
prefix,
selected = {
value: ['']
}
}) => {
const s = selected.value[0] ? selected.value[0] : ''
return `${prefix}${s}`
}).join('&')
}
const qArr = [{
text: 'text1',
description: 'desc1',
options: [
['1_1', '11'],
['1_2', '12'],
['1_3', '13'],
],
prefix: 'prefix_1_',
},
{
text: 'text2',
description: 'desc2',
options: [
['2_1', '21'],
['2_2', '22'],
['2_3', '23'],
],
prefix: 'prefix_2_',
},
{
text: 'text3',
description: 'desc3',
options: [
['3_1', '31'],
['3_2', '32'],
['3_3', '33'],
],
prefix: 'prefix_3_',
},
]
const getOptionsHtml = ({
text,
options
}) => {
let html = ''
options.forEach(({
id,
value,
selected
}, i) => {
html += `
<label>
<input
class="question-input"
type="radio"
name="${text}"
value="${value[0]}"
${selected ? 'checked' : ''}
/>
${value[1]}
</label>
`
})
return html
}
const getSingleQuestionHtml = (q) => {
const optionsHtml = getOptionsHtml({
text: q.text,
options: q.options
})
return `
${q.text}<br />
${q.desc}<br />
${optionsHtml}
`
}
const registerEventHandlers = ({
container,
question
}) => {
const radioBtns = document.querySelectorAll('.question-input')
radioBtns.forEach((input, i) => {
input.addEventListener('change', function(e) {
question.selected = e.target.value
})
})
}
const updateHtml = ({
container,
question
}) => {
container.innerHTML = getSingleQuestionHtml(question)
registerEventHandlers({
container,
question
})
};
const updateContainer = (container) => (question) => updateHtml({
container,
question
});
const setElDisplay = ({
el,
display
}) => {
if (display) {
el.classList.add("d-inline-block")
el.classList.remove("d-none")
} else {
el.classList.remove("d-inline-block")
el.classList.add("d-none")
}
}
const updateBtnVisibility = ({
btnNext,
btnPrev,
btnResult,
quiz
}) => () => {
setElDisplay({
el: btnNext,
display: quiz.hasNext
})
setElDisplay({
el: btnResult,
display: !quiz.hasNext
})
setElDisplay({
el: btnPrev,
display: quiz.hasPrev
})
}
(function() {
const quiz = new Quiz(qArr.map(q => new Question(q)))
const container = document.getElementById('quiz-container')
updateQuizContainer = updateContainer(container)
updateQuizContainer(quiz.currentQuestion)
const btnPrev = document.getElementById('btn-prev')
const btnNext = document.getElementById('btn-next')
const btnResult = document.getElementById('btn-result')
const updateBtns = updateBtnVisibility({
btnPrev,
btnNext,
btnResult,
quiz
})
updateBtns()
btnPrev.addEventListener('click', function() {
if (quiz.hasPrev) {
quiz.currentQuestion.setDefault()
}
updateQuizContainer(quiz.prev)
updateBtns()
})
btnNext.addEventListener('click', () => {
if (quiz.hasNext) {
quiz.currentQuestion.setDefault()
}
updateQuizContainer(quiz.next)
updateBtns()
})
btnResult.addEventListener('click', function() {
quiz.currentQuestion.setDefault()
console.log(urlParser(quiz))
})
})();
.d-inline-block {
display: inline-block;
}
.d-none {
display: none;
}
<div id="quiz-container"></div>
<div id="quiz-controls">
<button id="btn-prev" class="d-inline-block">
PREV
</button>
<button id="btn-next" class="d-inline-block">
NEXT
</button>
<button id="btn-result" class="d-none">
RESULT
</button>
</div>
<div id="result"></div>
I have a config object. Using this config object, I populate required elements by appending a string to the key of this object.I need help updating values
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => {
var newMemberObjValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
let updateValue = Object.entries(newMemberObjValue).forEach(([key, value]) => {
[`${key}_${type}`]: value; //I'm stuck here. not sure how to proceed
delete key;
});
return updateValue;
};
updateInitialValue = ('applicant', 'SELF');
updateInitialValue = ('spouse', 'DEPENDANT');
Expected Result:
{
type: 'applicant',
dateOfBirth_applicant: '',
seekingCoverage_applicant: true
relationshipToPrimary: 'SELF'
};
{
type: 'spouse',
dateOfBirth_spouse: '',
seekingCoverage_spouse: true
relationshipToPrimary: 'DEPENDANT'
};
Since you're not updating the original object, you can simplify this greatly:
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => ({
type,
relationshipToPrimary,
[`dateOfBirth_${type}`]: MEMBER_INITIAL_VALUE.dateOfBirth_,
[`seekingCoverage_${type}`]: MEMBER_INITIAL_VALUE.seekingCoverage_
});
let updatedValue = updateInitialValue('applicant', 'SELF');
updatedValue = updateInitialValue('spouse', 'DEPENDANT');
This should do the trick:
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => {
let newMemberInitialValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
Object.keys(newMemberInitialValue).forEach((key) => {
if(!['type', 'relationshipToPrimary'].includes(key)) {
newMemberInitialValue[`${key}_${type}`] = newMemberInitialValue[key];
delete newMemberInitialValue[key];
}
});
newMemberInitialValue.type = type;
newMemberInitialValue.relationshipToPrimary = relationshipToPrimary;
console.log(newMemberInitialValue);
};
let applicantValues = updateInitialValue('applicant', 'SELF');
let spouseValues = updateInitialValue('spouse', 'DEPENDANT');
EDIT: Missed returning the value from the function and then assigning to a new variable.
Although an answer was posted, because i also solved it and my solution is a bit different (though the other answer looks way too slimmer) i would post it here.
const MEMBER_INITIAL_VALUE = {
type: "",
dateOfBirth_: "",
seekingCoverage_: true,
relationshipToPrimary: "",
};
const updateInitialValue = (type, relationshipToPrimary) => {
var newMemberObjValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
Object.entries(newMemberObjValue).forEach(([key, value]) => {
if (key === "type") {
newMemberObjValue[key] = type;
} else if (key === "dateOfBirth_") {
Object.defineProperty(
newMemberObjValue,
[`${key}_${type}`],
Object.getOwnPropertyDescriptor(newMemberObjValue, key)
);
delete newMemberObjValue[key];
newMemberObjValue[`${key}_${type}`] = value;
} else if (key === "seekingCoverage_") {
Object.defineProperty(
newMemberObjValue,
[`${key}_${type}`],
Object.getOwnPropertyDescriptor(newMemberObjValue, key)
);
delete newMemberObjValue[key];
newMemberObjValue[`${key}_${type}`] = value;
} else if (key === "relationshipToPrimary") {
newMemberObjValue[key] = relationshipToPrimary;
}
});
return newMemberObjValue;
};
const updatedValue1 = updateInitialValue("applicant", "SELF");
const updatedValue2 = updateInitialValue('spouse', 'DEPENDANT');
Though a few answers have already been posted, I would like to suggest a similar one that does the same thing in a much more clear and concise way:
function Member() {
this.type = '';
this.dateOfBirth = '';
this.seekingCoverage = true;
this.relationshipToPrimary = '';
}
function UpdateInitialValue(type, relationshipToPrimary) {
var newMember = new Member();
newMember.type = type;
newMember.relationshipToPrimary = relationshipToPrimary;
return newMember;
}
console.log(UpdateInitialValue('applicant', 'SELF'));
console.log(UpdateInitialValue('spouse', 'DEPENDANT'));
I'm trying to convert a ES6 javascript file to ES5 as I'm need to target an old browser (ie: A webview on Android 4.4.2).
I've seen that Babeljs.io provide a tool to do a conversion, but the output code don't seems valid... (see here)
Any idea how to achieve this conversion (just once) ?
The file concerned is siiimple-toast.js (a toast notification plugin)
/* success + alert + warning + message */
var setStyles = (el, styles) => {
Object.keys(styles).forEach((key) => {
el.style[key] = styles[key];
});
};
const setAttrs = (el, attrs) => {
Object.keys(attrs).forEach((key) => {
el.setAttribute(key, attrs[key]);
});
};
const getAttr = (el, attr) => el.getAttribute(attr);
const privateKeys = {
defaultOptions: Symbol('defaultOptions'),
render: Symbol('render'),
show: Symbol('show'),
hide: Symbol('hide'),
removeDOM: Symbol('removeDOM'),
};
const siiimpleToast = {
[privateKeys.defaultOptions]: {
container: 'body',
class: 'siiimpleToast',
position: 'top|center',
margin: 15,
delay: 0,
duration: 3000,
style: {},
},
setOptions(options = {}) {
return {
...siiimpleToast,
[privateKeys.defaultOptions]: {
...this[privateKeys.defaultOptions],
...options,
},
};
},
[privateKeys.render](state, message, options = {}) {
const mergedOptions = {
...this[privateKeys.defaultOptions],
...options,
};
const {
class: className,
position,
delay,
duration,
style,
} = mergedOptions;
const newToast = document.createElement('div');
// logging via attrs
newToast.className = className;
var toatsLoaded=1;
newToast.innerHTML = '<span class="toastIcon '+state+'">';
setAttrs(newToast, {
'data-position': position,
'data-state': state,
});
setStyles(newToast, style);
// use .setTimeout() instead of $.queue()
let time = 0;
setTimeout(() => {
this[privateKeys.show](newToast, mergedOptions);
}, time += delay);
setTimeout(() => {
this[privateKeys.hide](newToast, mergedOptions);
}, time += temps);
// support method chaining
return this;
},
[privateKeys.show](el, { container, class: className, margin }) {
const hasPos = (v, pos) => getAttr(v, 'data-position').indexOf(pos) > -1;
const root = document.querySelector(container);
root.insertBefore(el, root.firstChild);
// set initial position
setStyles(el, {
position: container === 'body' ? 'fixed' : 'absolute',
[hasPos(el, 'top') ? 'top' : 'bottom']: '-100px',
[hasPos(el, 'left') && 'left']: '15px',
[hasPos(el, 'center') && 'left']: `${(root.clientWidth / 2) - (el.clientWidth / 2)}px`,
[hasPos(el, 'right') && 'right']: '15px',
});
setStyles(el, {
transform: 'scale(1)',
opacity: 1,
});
// distance de départ
let pushStack = 20;
Array
.from(document.querySelectorAll(`.${className}[data-position="${getAttr(el, 'data-position')}"]`))
.filter(toast => toast.parentElement === el.parentElement)// matching container
.forEach((toast) => {
setStyles(toast, {
[hasPos(toast, 'top') ? 'top' : 'bottom']: `${pushStack}px`,
});
pushStack += toast.offsetHeight + margin;
});
},
[privateKeys.hide](el) {
const hasPos = (v, pos) => getAttr(v, 'data-position').indexOf(pos) > -1;
const { left, width } = el.getBoundingClientRect();
setStyles(el, {
[hasPos(el, 'left') && 'left']: `${width}px`,
[hasPos(el, 'center') && 'left']: `${left + width}px`,
[hasPos(el, 'right') && 'right']: `-${width}px`,
opacity: 0,
});
const whenTransitionEnd = () => {
this[privateKeys.removeDOM](el);
el.removeEventListener('transitionend', whenTransitionEnd);
};
el.addEventListener('transitionend', whenTransitionEnd);
},
[privateKeys.removeDOM](el) {// eslint-disable-line
const parent = el.parentElement;
parent.removeChild(el);
},
default(message, options) {
return this[privateKeys.render]('default', message, options);
}
};
$(document).on('click', '.toastClose', function(e){
e.preventDefault();
$(this).parent('.siiimpleToast').remove();
});
Thanks a lot for your feedbacks 🙏
Ben
If this is important, I am working in a react mob-x store. I have an object that I am converting to an array and looping through and doing various things. I need to find the last instance of the object where the value === true to then have the key to use in a comparison. (ex. if(panelName === panel (of the last instance where the value is true).
I am having trouble finding the last item where value === true. I tried using arr.length -1 but that of course just finds the last one regardless of what the value is. The object key and length are both variable, the value is either 'true or false'. Thank you.
panelsSaved = {EE: true, SS: false, RR: false, FF: true, WW: false}
#action expandNextPanel(panelName){
const panelsSaved = this.filterValuesData.panelsSaved;
const panels = Object.entries(panelsSaved);
for (const [panel, value] of panels){
if(value === true && panel !== panelName){
//do stuff
break;
}
}
I am upvoting most of the answers because it sort of took a combination of a few of them to get this working.
let panelsUsed = {EE: true, SS: false, RR: false, FF: true, WW: false};
if(panelsUsed.length !== 0) {
for (let i = 0; i <= panelsUsed.length; i++) {
if(panelsUsed[i] !== panelName){
if(panelsUsed[i] !== undefined) {
'do stuff'
break;
} else {
'do other stuff'
}
}
};
}
let panelsFiltered = panelsUsed.filter((panel) => {return panel !== panelName});
this.filterValuesData.panelsUsed = panelsFiltered;
} ```
Thank you everyone for your input!
simply :
const panelsSaved = { EE: true, SS: false, RR: false, FF: true, WW: false }
const LastTrue = obj => Object.entries(obj)
.filter(([k,v])=>v)
.reverse()[0][0]
console.log( LastTrue(panelsSaved) )
OR, with a Loop
const panelsSaved = { EE: true, SS: false, RR: false, FF: true, WW: false }
function LastTrue(obj)
{
let rep;
for (let key in obj) if (obj[key]) rep=key
return rep
}
console.log( LastTrue(panelsSaved))
Just use classical for loop in reverse order (for...of loop cannot be used in such way).
panelsSaved = {EE: true, SS: false, RR: false, FF: true, WW: false}
#action expandNextPanel(panelName){
const panelsSaved = this.filterValuesData.panelsSaved;
const panels = Object.entries(panelsSaved);
for (var i = panels.length - 1; i >= 0; i--) {
const [panel, value] = panels[i]
if(value === true && panel !== panelName){
// do stuff
break;
}
}
}
let panelsSaved = { EE: true, SS: false, RR: false, FF: true, WW: false };
function expandNextPanel(panelName) {
let lastTrue = Object.entries(panelsSaved).filter(
([panel, value]) => value === true && panel !== panelName
).pop();
console.log(lastTrue)
}
expandNextPanel("FF");
There's quite many ways to approach this, but here's a couple of ways.
You could iterate the array in reverse order and return the first one that matches true:
function lastWhere(arr, fn) {
if (arr.length === 0) return undefined;
for (let i = arr.length - 1; i >= 0; i--) {
const candidate = arr[i];
if (fn(candidate)) {
return candidate;
}
}
return undefined;
}
// ...
const panelsSaved = this.filterValuesData.panelsSaved;
const panels = Object.entries(panelsSaved);
const [panel, value] = lastWhere(panels, ([panel, value]) => value === true)
Or perhaps you could use .map() to map the values to true or false and use .lastIndexOf() to find the last one that's true:
function lastWhereTrue(arr, fn) {
const mapped = arr.map((candidate) => fn(candidate));
const matchingIndex = arr.lastIndexOf(true);
return mapped[matchingIndex];
}
// ...
const panelsSaved = this.filterValuesData.panelsSaved;
const panels = Object.entries(panelsSaved);
const [panel, value] = lastWhere(panels, ([panel, value]) => value === true)
Then, if you need to go through the entire list of panels and do something to all of them and specifically do something to the last panel, you could just compare either the panel or the value, depending on which one is unique.
const panelsSaved = this.filterValuesData.panelsSaved;
const panels = Object.entries(panelsSaved);
const last = lastWhere(panels, ([panel, value]) => value === true)
for (const [panel, value] of panels) {
if (panel === last.panel) {
// ...
}
// or
if (value === last.value) {
// ...
}
}
Example of the solution which I suggested in the comments:
const panelsSaved = this.filterValuesData.panelsSaved;
const panels = Object.entries(panelsSaved);
let lastTruePanel = null;
for (const [panel, value] of panels.reverse()) {
if (value === true && !lastTrueValueProcessed) {
// This is the entry which is the last value===true
lastTrueValueProcessed = [panel, value];
}
// ...
}