i try to build a simple javasciprt random quote app but in the very first test of my code i saw this in console : Uncaught TypeError: quotesData[currentQuote] is undefined
showquote http://127.0.0.1:5500/js/main.js:31
http://127.0.0.1:5500/js/main.js:37
this is js code source :
quotesData = [{
quote: `There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.`,
name: 'Albert Einstein '
},
{
quote: `Good friends, good books, and a sleepy conscience: this is the ideal life.`,
name: 'Mark Twain'
},
{
quote: `Life is what happens to us while we are making other plans.`,
name: 'Allen Saunders '
},
{
quote: `It's not the load that breaks you down, it's the way you carry it.`,
name: 'Lou Holt'
},
{
quote: `Try not to become a man of success. Rather become a man of value.`,
name: 'Albert Einstein '
},
]
/* important variables */
const currentQuote = quotesData[0];
const quoteText = document.getElementById('quote');
const quotebtn = document.getElementById('q-btn');
const quotepan = document.getElementById('q-span');
/* this function is for show the quote and author name */
function showquote() {
quoteText.innerText = quotesData[currentQuote].quote;
quotespan.innerText = quotesData[currentQuote].name;
currentQuote++;
};
/* this function is for change the quote and author name with evrey click */
quotebtn.addEventListener('click', showquote())
currentQuote isn't an array index, it's an element of the array.
You need to set it to 0, and it can't be const if you want to increment it.
let currentQuote = 0;
Also, the second argument to addEventListener should be a reference to a function. You're calling the function immediately instead of saving it as a listener.
quotebtn.addEventListener('click', showquote);
After you increment currentQuote, you need to check if you've reached the end of the array and wrap around. You can do this using the modulus operator.
function showquote() {
quoteText.innerText = quotesData[currentQuote].quote;
quotespan.innerText = quotesData[currentQuote].name;
currentQuote = (currentQuote + 1) % quotesData.length;
};
A couple problems with your code -
Replace quotebtn.addEventListener('click', showquote()) with quotebtn.addEventListener('click', showquote) because otherwise you are passing the return value of showquote to the function.
currentQuote is an object which cannot be passed as an index. You need to set currentQuote to 0 so you can increment it.
This is still not random quotes, but it solves your problems.
currentQuote is a constant variable - which means you can't increment it because ++ is actually just syntactic sugar for += 1 which in itself is syntactic sugar for currentQuote = currentQuote + 1. Change it to let.
TIP:
Do not mix ES5 and ES6. Old functions should only be used when access to the this keyword is needed. Otherwise, stick to one version for semantic purposes.
class SomeClass {
x: 5;
y = 10;
}
const c = new SomeClass();
alert(c.x + ' : ' + c.y);
Why is the code compilable but the value of c.x is undefined?
What is the effect of declaring a class property with :?
Regarding the x: 5 part, although this is a valid javascript code, there is no much use for it.
This is a javascript label and it used (if any) mostly within loops context.
So to answer your questions:
Why is the code compilable
Because technically this is a valid javascript code (yet not a valid class field).
but the value of c.x is undefined
Because the x is a label and not a class field.
What is the effect of declaring a class property with :
You get a label instead of a class field.
Addendum
Another common mistake, is this code of block:
class SomeClass {
z = () => {
x: 5;
};
}
You would think that z() will return an object with an x key:
`{x:5}`
But actually you have a function with a label of x that just run an expression of 5.
Just for completeness sake, the fix will be either to add an explicit return and another set of curly braces
() => {return {x: 5}}
Or just wrap the whole thing with parentheses
() => ({x: 5})
Edit
As a followup to the comments below:
Just to be clear, your code compiles on several environments that i tested as well as stack-snippets as can be seen below:
class SomeClass {
x: 5;
y = 10;
}
const c = new SomeClass();
console.log(c.x + ' : ' + c.y);
The code is not valid ES6.
You seem to be "compiling" with babel, and have inadvertently enabled the flow syntax extension (and also class properties for the second line). In flow, x: 5 is a class field type annotation. Of course, 5 as a type doesn't make sense, but apparently they allow pretty arbitrary expressions.
I want to do something relatively simple, I think anyways.
I need to compare the pathname of page with an object's kv pairs. For example:
if("pathname" === "key"){return value;}
That's pretty much it. I'm not sure how to do it in either regular Javascript or jQuery. Either are acceptable.
You can see my fiddle here: http://jsfiddle.net/lz430/2rhds1x3/
JavaScript:
var pageID = "/electrical-electronic-tape/c/864";
var pageList = [{
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
}];
var label = pageID in pageList;
$('.el').html(label);
First, your "pageList" should just be a plain object, not an object in an array:
var pageList = {
"/electrical-electronic-tape/c/864": "ElectronicTape",
"/industrial-tape/c/889": "IndustrialTape",
"/sandblasting-tape/c/900": "SandblastingTape",
"/Foam-Tape/c/875": "FoamTape",
"/double-coated-d-c-dhesive-tape/c/872": "DCTape",
"/Adhesive-Transfer-Tape/c/919": "ATTape",
"/Reflective-Tape/c/884": "ReflectiveTape",
"/custom-moulding": "CustomMoulding",
"/request-a-quote": "RequestQuote"
};
Then you can set "label" to the value from the mapping:
var label = pageList[pageID] || "(not found)";
That last bit of the statement above will set the label to "(not found)" if the lookup fails, which may or may not be applicable to your situation.
It depends kinda on the logic you want to implement. If you want to say "if object has the key, then do X, and if not, then do Y", then you handle that differently than "set label to the object's key's value if the key is there, or else set it to undefined or something else".
For the first case you do:
if (pageList.hasOwnProperty(pageID) ) {
label = pageList[pageID];
}
else {
// do whatever, maybe some error?
}
For the second case, you can just say
var label = pageList[pageID] || 'notFound';
As indicated by #Pointy, either get rid of the array or subsiture pageList[0] for pageList and pageList[0][pageID] for pageList[pageID] above, if you need to keep the array.
I've read a lot of articles on JavaScript formatting and the rules etc. But my question here is how to format core JavaScript elements (i.e. functions, arrays, and objects)
Usually for functions I use the following:
function myFunction(argument1 , argument2 , optionalargument3) {
optionalargument3 === undefined ? optionalargument3 : "default value";
return optionalargument1;
}
or:
var myFunction = function(argument1 , argument2 , optionalargument3) {
optionalargument3 === undefined ? optionalargument3 : "default value";
return optionalargument1;
}
but this is controversial. For objects I use:
var Car = {
model : undefined
make : "Sudan"
MPG : 7.5
highway-mpg : 11.5
};
of course, this is highly controversial and most people use different methods.
and for for loops, I use:
for(var i = 0; i < (array.length + 1); i++) {
console.log(array[i]);
}
People also seem to have devolved the idea to write out global in a list before objects or functions.
eg:
var winMessage = "You Win!";
var loseMessage = "Loser!";
var cash = 0;
var intrest = null;
function randomInteger(low , high) {
return Math.floor((Math.random())*(high-low))+low
}
var Car = {
model : undefined
make : "Sudan"
MPG : 7.5
highway-mpg : 11.5
};
Are there any universal accepted, or a generically "proper" way to format your JavaScript code?
It's mostly down to preference but it is important to keep a consistent style throughout your code base particularly if you are working in a team to increase readability of your code.
You can either define your own style or adopt an existing one such as the Google style guide (https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml), or Crockfords (http://javascript.crockford.com/code.html).
JavaScript has automatic semi-colon insertion which means that some types of formatting can lead to code that doesn't work as intended such as the following where the interpreter will ignore the object literal because it will insert a semi-colon directly after the return keyword:
return
{
name: "Foo"
};
In your example
optionalargument3 === undefined ? optionalargument3 : "default value";
doesn't actually do anything because there is no assignment, but a nice clean way to deal with default arguments is instead of using
arg = arg === undefined ? "default value" : arg;
you can use the semantically identical and cleaner version:
arg = arg || "default value";
i've been trying the last couple days to convert this js script to python code.
My implementation (blindfull cp mostly, some minor fixes here and there) so far:
import random
class markov:
memory = {}
separator = ' '
order = 2
def getInitial(self):
ret = []
for i in range(0, self.order, 1):
ret.append('')
return ret
def breakText(self, txt, cb):
parts = txt.split(self.separator)
prev = self.getInitial()
def step(self):
cb(prev, self.next)
prev.shift()#Javascript function.
prev.append(self.next)
#parts.forEach(step) # - step is the function above.
cb(prev, '')
def learn(self, txt):
mem = self.memory
def learnPart(key, value):
if not mem[key]:
mem[key] = []
mem[key] = value
return mem
self.breakText(txt, learnPart)
def step(self, state, ret):
nextAvailable = self.memory[state] or ['']
self.next = nextAvailable[random.choice(nextAvailable.keys())]
if not self.next:
return ret
ret.append(next)
nextState = state.slice(1)
return self.step(nextState, ret)
def ask(self, seed):
if not seed:
seed = self.genInitial()
seed = seed + self.step(seed, []).join(self.separator)
return seed
Issues:
I have absolutely no knowledge of javascript.
When i try to "learn" some text to a "markov" class object [e.g.: a=markov(); a.learn("sdfg");] i get the following error: "TypeError: unhashable type: 'list'", for the "mem" dictionary at the "learnPart" function, member of the "learn" function.
So my question so far is why does this exception [TypeError for a list object, falsely referring to a dictionary object (which is hashable)] occur?
thanks in advance for any suggestions, directions, points, help in general :D
Guy who wrote the article speaking. Glad you found it useful! Now, my first implementation of a Markov chain was actually in Python, so this answer will focus on how to write it in a more Pythonic way. I'll show how to go about making an order-2 Markov chain, since they're easy to talk about, but you can of course make it order-N with some modifications.
Data Structures
In js, the two prominent data structures are the generic object and the array (which is an extension to the generic object). In Python however, you have other options for more finely-grained control. Here're the major differences in the two implementations:
A state in our chain is really a tuple - an immutable, ordered structure, with a fixed amount of elements. We always want n elements (in this case, n=2) and their order has meaning.
Manipulating the memory will be easier if we use a defaultdict wrapping a list, so we can skip the "checking if a state doesn't exist, and then doing X", and instead just do X.
So, we stick a from collections import defaultdict at the top and change how markov.memory is defined:
memory = defaultdict(list)
Now we change markov.getInitial to return a tuple (remember this explains an order-2 chain):
def getInitial(self):
return ('', '')
(if you want to expand it further, you can use a really neat Python trick: tuple([''] * 2) will return the same thing. Instead of empty strings, you can use None)
We'll get to changing what uses genInitial in a bit.
Yield and iteration
A strong concept which doesn't exist in js (yet) but does exist in Python is the yield operator (see this question for great explanations).
Another feature of Python is its generic for loop. You can go over nearly anything quite easily, including generators (functions which use yield). Combining the two, and we can redefine breakText:
def breakText(self, txt):
#our very own (ε,ε)
prev = self.getInitial()
for word in txt.split(self.separator):
yield prev, word
#will be explained in the next paragraph
prev = (prev[1], word)
#end-of-sentence, prev->ε
yield prev, ''
The magic part above, prev = (prev[1], word) can be explained best by example:
>>> a = (0, 1)
>>> a
(0, 1)
>>> a = (a[1], 2)
>>> a
(1, 2)
That's how we advance through the word list. And now we move up to what uses breakText, to the redefinition of markov.learn:
def learn(self, txt):
for part in self.breakText(txt):
key = part[0]
value = part[1]
self.memory[key].append(value)
Because our memory is a defaultdict, we don't have to worry about the key not existing.
A pee break on the side of the road
OK, we have half of the chain implemented, time to see it in action! What we have so far:
from collections import defaultdict
class Markov:
memory = defaultdict(list)
separator = ' '
def learn(self, txt):
for part in self.breakText(txt):
key = part[0]
value = part[1]
self.memory[key].append(value)
def breakText(self, txt):
#our very own (ε,ε)
prev = self.getInitial()
for word in txt.split(self.separator):
yield prev, word
prev = (prev[1], word)
#end-of-sentence, prev->ε
yield (prev, '')
def getInitial(self):
return ('', '')
(I changed the class name from markov to Markov because I cringe every time a class begins with a lowercase letter). I saved it as brain.py and loaded up Python.
>>> import brain
>>> bob = brain.Markov()
>>> bob.learn('Mary had a little lamb')
>>> bob.memory
defaultdict(<class 'list'>, {('had', 'a'): ['little'], ('Mary', 'had'): ['a'], ('', ''): ['Mary'], ('little', 'lamb'): [''], ('a', 'little'): ['lamb'], ('', 'Mary'): ['had']})
Success! Let's look at the result more carefully, to see that we got it right:
{ ('', ''): ['Mary'],
('', 'Mary'): ['had'],
('Mary', 'had'): ['a'],
('a', 'little'): ['lamb'],
('had', 'a'): ['little'],
('little', 'lamb'): ['']}
zips up Ready to drive on? We still have to use this chain!
Changing the step function
We've already met what we need to remake step. We have the defaultdict, so we can use random.choice right away, and I can cheat a bit because I know the order of the chain. We can also get rid of the recursion (with some sorrow), if we see it as a function which takes a single step through the chain (my bad in the original article - a badly named function).
def step(self, state):
choice = random.choice(self.memory[state] or [''])
if not choice:
return None
nextState = (state[1], choice)
return choice, nextState
I regretfully added the or [''] because random.choice moans about empty lists. Finally, we move a larger portion of the logic to ask (the actual construction of the sentence):
def ask(self, seed=False):
ret = []
if not seed:
seed = self.getInitial()
while True:
link = self.step(seed)
if link is None:
break
ret.append(link[0])
seed = link[1]
return self.separator.join(ret)
Yes, a bit yucky. We could have given step a better name and made it a generator, but I'm late for a meeting with my pregnant wife who's about to give birth to a baby who left the stove on fire in my car that's being towed! I better hurry!
The grand finale
But first, a talk with bob:
from collections import defaultdict
import random
class Markov:
memory = defaultdict(list)
separator = ' '
def learn(self, txt):
for part in self.breakText(txt):
key = part[0]
value = part[1]
self.memory[key].append(value)
def ask(self, seed=False):
ret = []
if not seed:
seed = self.getInitial()
while True:
link = self.step(seed)
if link is None:
break
ret.append(link[0])
seed = link[1]
return self.separator.join(ret)
def breakText(self, txt):
#our very own (ε,ε)
prev = self.getInitial()
for word in txt.split(self.separator):
yield prev, word
prev = (prev[1], word)
#end-of-sentence, prev->ε
yield (prev, '')
def step(self, state):
choice = random.choice(self.memory[state] or [''])
if not choice:
return None
nextState = (state[1], choice)
return choice, nextState
def getInitial(self):
return ('', '')
And loading it up:
>>> import brain
>>> bob = brain.Markov()
>>> bob.learn('Mary had a little lamb')
>>> bob.ask()
'Mary had a little lamb'
>>> bob.learn('Mary had a giant crab')
>>> bob.ask(('Mary', 'had'))
'a giant crab'
There is, of course, room for improvement and expanding on the concept. But it wouldn't be any fun if if I just gave you the answer.
Hopefully this will still help after 4 months.
The complex answer
The issue here is that learnPart is trying to use the return value of getInitial, which is a list, as a key to a dictionary. Lists are mutable, and hence not hashable, which means they can't be used as keys to a dictionary.
You could try adding this line to learnPart:
def learnPart(key, value):
key = tuple(key) #<-----Try adding this line
if not mem[key]:
mem[key] = []
mem[key] = value
return mem
But I do not think that will solve all the problems.
The simple answer
There are plenty of Markov Chain implementations written in Python out there. A quick search on Github yielded 168 projects:
https://github.com/search?l=Python&q=markov+chain
I kind of made a simplified version of the code:
import re
class Brain():
H = ''
def learn(self, txt):
self.H = txt
def ask(self,ask):
H=self.H
ask = re.compile(r"%s(.*)"%(ask),re.I|re.DOTALL)
m = ask.search(H)
print m.group(1)
Here's the execution:
>>> import brain
>>> bob = brain.Brain()
>>> bob.learn('Mary had a little lamb' )
>>> bob.ask('Mary had')
'a little lamb'
I agree that this isn't exactly a Markov chain algorithm.But it has a few advantages:
i. You can supply ask() with raw text as shown above.
ii. It has a fewer lines of code.
iii. And hopefully, it is easier to understand.