Simple grammar checker program - optimal data structure - javascript

I want to created a simple game for English that checks someone's sentences. They are able to construct their sentence using a fixed set of words from a word bank.
The word bank might be something scrambled like:
[want, eat, I, hi, to]
Then they'd create their sentence in the correct order:
hi I want to eat.
I asked this question on English SO as it originally pertained to grammatical questions- the question has evolved into more of a data structures question. You can read more about it at that link. My original thought to check sentence grammar using a set of generic English rules seemed like it could quickly grow too complex. It was recommended I just match using hard coded checks, shown below.
Before I further define these checks, I was wondering if a better data structure/method was known to check grammar for this purpose.
if (input === the_answer) {
msg = correct!
} else {
msg = 'Try again: ' + this.grammarRules(input, the_answer));
}
Language_System.prototype.grammarRules = function(input, answer) {
var grammar_hints = {
quest1 : {
task1 : [
'The subject, Bob, needs to be first',
'The phrase is Hello there'
]
}
};
var grammar_rules = {
quest1 : {
task1 : function (input, answer) {
var error = -1;
if (input[0] !== answer[0]) {
error = 0;
} else if (input.indexOf('hello') > input.indexOf('there')) {
error = 1;
}
return grammar_hints.quest1.task1[error];
}
}
};
return grammar_rules.Lee.quest1.task1(input, answer);
};

It would be much easier if you'd consider a more declarative approach:
- define a standard quest structure
- define a standard task structure with generic input formats
- define generic validators and re-use them
You started on the right path with the grammar_hints object, but I would actually put all the properties portraying to one task in the same object.
Suggestion:
var quests = [
{
name: 'Quest 1',
tasks: [
{
name: 'Task 1',
solution: 'hi I want to eat',
validators: [
validators.first('hi'),
validators.verbAfterNoun('want', 'I'),
]
}
],
},
];
You will be able to re-use a lot of the validators in multiple tasks so you want them to be as generic as possible, here is one example:
var validators = {
first: function (input, term) {
if (input[0] !== term) {
return 'The sentence needs to start with: ' + term;
}
},
verbAfterNoun: function (input, verb, noun) {
if (input.indexOf(verb) < input.indexOf(noun)) {
return 'The verb, ' + verb + ', needs to come after the noun ' + noun;
}
}
};
Now because you want to have a declarative format (I went with actually initializing the validators with their input and passing the result in the validators array), we would need a validator factory that takes a generic validator and returns a helper method that can be re-used with only the input. This will help us down the line so our testing framework won't need to know how many inputs to pass to each of the validator callbacks
// This is a factory method that applies the given callback (with the given arguments)
function makeValidator (fn) {
return function inputFN () {
var args = [].slice.call(arguments);
return function validate (input) {
return fn.apply(null, [input].concat(args));
}
}
}
// Apply the makeValidator() method on all the validators
for (var key in validators) {
validators[key] = makeValidator(validators[key]);
}
And finally we also want a standard way of checking our tasks against input:
// This method provides the generic validation framework for any task given any input
function validate (task, input) {
var wordList = input.split(' ');
if (input === task.solution) return {success: true, errors: []};
var errors = [];
task.validators.forEach(function (fn) {
var error = fn(wordList);
if (error) errors.push(error);
});
return {success: false, errors: errors};
}
And some examples:
var task = quests[0].tasks[0];
console.log(validate(task, 'hi I want to eat'));
console.log(validate(task, 'I want to eat hi'));
console.log(validate(task, 'hi want I to eat'));
console.log(validate(task, 'want I to eat hi'));
Putting it all together:
// This is a factory method that applies the given callback (with the given arguments)
function makeValidator (fn) {
return function inputFN () {
var args = [].slice.call(arguments);
return function validate (input) {
return fn.apply(null, [input].concat(args));
}
}
}
var validators = {
first: function (input, term) {
if (input[0] !== term) {
return 'The sentence needs to start with: ' + term;
}
},
verbAfterNoun: function (input, verb, noun) {
if (input.indexOf(verb) < input.indexOf(noun)) {
return 'The verb, ' + verb + ', needs to come after the noun ' + noun;
}
}
};
// Apply the makeValidator() method on all the validators
for (var key in validators) {
validators[key] = makeValidator(validators[key]);
}
var quests = [
{
name: 'Quest 1',
tasks: [
{
name: 'Task 1',
solution: 'hi I want to eat',
validators: [
validators.first('hi'),
validators.verbAfterNoun('want', 'I'),
]
}
],
},
];
// This method provides the generic validation framework for any task given any input
function validate (task, input) {
var wordList = input.split(' ');
if (input === task.solution) return {success: true, errors: []};
var errors = [];
task.validators.forEach(function (fn) {
var error = fn(wordList);
if (error) errors.push(error);
});
return {success: false, errors: errors};
}
function printTask (input) {
var task = quests[0].tasks[0];
var result = validate(task, input);
document.body.innerHTML += '<div><b>checking:</b> ' + input + '<pre>' + JSON.stringify(result, null, 4) + '</pre><hr />';
}
// Lets look at some examples
printTask('I want to eat hi');
printTask('hi want I to eat');
printTask('want I to eat hi');
printTask('hi I want to eat');

Related

Conditional validation using single piece of code - AngularJS

The code contains two functions. First one is defined as follows
scope.validateContactName = function() {
scope.address.invalidName = false;
if (!scope.address.name) {
scope.address.invalidName = true;
}
}
which is invoked by the function validateContactName();
Now i have another function
scope.validateContactPhone = function() {
scope.address.invalidPhone = false;
if (!scope.address.phone) {
scope.address.invalidPhone = true;
}
}
which is invoked by the function validateContactPhone();
Instead of two functions, is there a way i can use a single function and do conditional validation?
Something like
validateContactInfo('name');
function validateContactInfo(attr) {
//do validation based on the attribute
// There is only one single piece of code for both conditions
}
Maybe smth like this could work:
scope.validateField = function(field, errorField) {
scope.address[errorField] = false;
if (!scope.address[field]) {
scope.address[errorField] = true;
}
}
Or a shorter version:
scope.validateField = function(field, errorField) {
scope.address[errorField] = !scope.address[field];
}
I would suggest something like this(ES6):
scope.address = [
{
type: "phone",
invalid: false
},
{
type: "name",
invalid: false
}
];
const validate = type => {
let data = scope.address.find(a => a.type === type);
if(!data.type) {
data.invalid = true;
}
};
validate("phone");
validate("name");
Assuming contact information is used in a form to get input from the user. I would recommend to use angular's own form validation
If it is not the case, here is a generic way of checking if values exists in a object. Which you can add in project''s utilities
const contactInfo = {
name: 'My name',
phone: '123123123',
address: ''
}
function validateExistence(obj){
const emptyKeys = [];
for(let key in obj){
if(!obj[key]) emptyKeys.push(key)
}
return emptyKeys
}
console.log(validateExistence(contactInfo));

How to test a function with different inputs

function addTwo (a, b) {
return a + b;
}
//Leave the function call
addTwo(50, 100);
I'm learning React and I'm trying to create a codecademy type site as a 'learning project', but have run into a JS problem.
Say you have the function above, how do you test it for more than one case? So far I'm testing with:
eval(CODE PULLED IN HERE) === 150 ? alert('Correct!') : alert('Wrong!');
which is obviously going to alert Correct, which is ok for this case. But for other questions (and even this one) I'm going to want more than one test case and that's where I'm stuck.
So, how can I test for multiple test cases, or is there just a whole other way of doing what I'm trying to achieve?
Any help/tips greatly appreciated,
For those who know React here's some code to see a bit of what I currently have :
const CodeEditor = React.createClass({
getInitialState () {
var initialValue = [
"function addTwo () {",
" ",
"}",
"//Leave the function call",
"addTwo(50, 100);"
].join("\n");
return {
kataValue: initialValue
}
},
onChange (newValue) {
this.setState({kataValue: newValue});
},
evalCode () {
var val = this.state.kataValue
eval(val) === 150 ? alert('Correct!') : alert('Wrong!');
},
render () {
return (
<div className="code-editor-wrapper">
<AceEditor
name="editor"
mode="sh"
theme="chaos"
onChange={this.onChange}
value={this.state.kataValue}
editorProps={{$blockScrolling: true}}
/>
<button onClick={this.evalCode} className="spec-btn submit-code-btn">Evaluate</button>
</div>
)
}
})
Don't include the function call in the user's code. Only require the function to be named in a certain way. Instead of directly evaling the user's code, embed into a function that returns the user's function:
function getUserFunction(code, functionName) {
var userCode = new Function(code + '; return ' + functionName + ';');
return userCode();
}
After calling getUserFunction you have a reference to the function the user wrote and you can execute it as often as you want. How you structure your test cases and how much feedback you want to give to the user is up to you.
Here is small example:
var userFn = getUserFunction(this.state.kataValue, 'addTwo');
var testCases = [
[[50, 100], 150],
[[1, 2], 3],
];
var passes = testCases.every(
([input, output]) => userFn(...input) === output
);
if (passes) {
// all test cases pass
}
You can iterate over a bunch of inputs like so:
function addTwo(a, b) {
return a + b
}
for (var i = 0, j; i < 100; i++) {
for (j = 0; j < 100; j++) {
if (addTwo(i, j) !== i + j) console.error('Wrong output for inputs ' + i + ' and ' + j)
}
}

Mapping JSON to ES6 Classes

I have our staff in a json file, and had the idea to use that data with ES6 classes. The more I work with this, the more I feel as though I may be missing something. I had this working in coffeescript as such:
fetch = require('node-fetch')
domain = 'domain.com'
apiSrc = 'api.domain.com'
slug = 'people'
class Person
constructor: (json) -> {name: #name, title: #title, school: #school, bio: #bio} = json
email: (username) ->
username.replace(/\s/, '.').toLowerCase() + '#' + domain
profile: ->
content = []
if this.name then content.push("#{#name}")
if this.title then content.push("#{#title}")
if this.school then content.push(school) for school in "#{#school}"
if this.bio then content.push("#{#bio}")
if this.name then content.push(this.email("#{#name}"))
content.join('')
fetch('http://' + apiSrc + '/' + slug + '.json')
.then((response) -> response.json())
.then((api) ->
content = []
group = []
group.push(new Person(them)) for them in api[slug]
for them, index in group
content.push(them.profile())
console.log(content.join(''))
)
But then I thought it would be even better if I could convert it to ES6. I know the use case is simple, and classes certainly aren't necessary, since I'm just using the data for templating, however, for the sake of learning, I was attempt to do this. Am I going about this the wrong way? Right now, I feel like there should be a way to return all of the "people" that I put into the Person class. However, the only way I could figure out how to do that was to run a for loop and then write it to the document.
class Person {
constructor(data) { ({name: this.name, title: this.title, school: this.school, bio: this.bio, email: email(this.name)} = data); }
email(username) {
return username.replace(/\s/, '.').toLowerCase() + '#' + location.hostname.replace(/[^\.\/\#]+\.[^\.\/]+$/, '');
}
profile() {
return `${this.name} ${this.title}`;
}
}
var apiSrc = 'api.domain.com';
var slug = 'people';
fetch(`http://${apiSrc}/${slug}.json`)
.then(function(response) { return response.json() }) // .then(response => response.json())
.then(function(api) {
var content = [];
var group = [];
for (var i = 0; i < api[slug].length; i++) { var them = api[slug][i]; new Person(them); }
for (var i = 0; index < group.length; i++) {
var them = group[i];
content.push(them.profile());
console.log(content.join(''));
}
});
My ES6 conversion actually isn't even working right now. API returns the JSON but after that it gets messed up. Any suggestions would be really helpful as I'm trying to better myself as a coder and hope this sort of example could help others learn Classes in a real use case.
You might try using the reviver parameter of JSON.parse. Here is a simplified example:
class Person {
// Destructure the JSON object into the parameters with defaults
constructor ({name, title, school=[]}) {
this.name = name
this.title = title
this.school = school
}
}
var api = JSON.parse(a, function (k,v) {
// Is this a new object being pushed into the top-level array?
if (Array.isArray(this) && v.name) {
return new Person(v)
}
return v
})
var group = api["people/administration"]

pg-promise create custom filters for select query

The function that I am working on is getting an input object that has 7 different key-values and each of them could be undefined or not. I want to filter my database based on those key-values that exists in the input. For example if only input.userID exists I want to run this query:
db.query("...WHERE userID = ${userID}", {userID: input.userID});
else if both input.userID and input.startTime exist, I want to do this:
db.query("...WHERE userID = ${userID} and startTime= ${startTime}", {userID: input.userID, startTime: input.startTime});
What I have done is I created a params and keys object like this:
if(input.userID) {
keys.push('userID');
params.push(input.userID);
query = addFilterToTheQuery(query, 'userID', input.userID, filteredItemsCount);
filteredItemsCount = filteredItemsCount +1;
}
addFilterToTheQuery is a simple function I implemented myself. I basically make 7 if cases. Then I have to use those keys and param values to pass to the query function in a way that might need another huge switch case code.
Is this the only way to do this? Is there a better way to get rid of the redundancies in this code?
Custom Type Formatting is the most suitable here.
For example, if we want to convert an object with properties - filter values, we could do it like this:
var pgp = require('pg-promise')(/* initialization options */);
function FilterSet(filters) {
if (!filters || typeof filters !== 'object') {
throw new TypeError('Parameter \'filters\' must be an object.');
}
this._rawDBType = true; // property renamed later - see UPDATE below
this.formatDBType = function () {
var keys = Object.keys(filters);
var s = keys.map(function (k) {
return pgp.as.name(k) + ' = ${' + k + '}';
}).join(' AND ');
return pgp.as.format(s, filters);
};
}
TEST
var filter = new FilterSet({
first: 1,
second: 'two'
});
var test = pgp.as.format('WHERE $1', filter);
console.log(test);
This outputs:
WHERE "first" = 1 AND "second" = 'two'
If your filters are to be used as %value% with LIKE or ILIKE, then you would need to change your custom type accordingly.
See related questions:
42, 49, 89, 90,
UPDATE
Below is the same example re-written for the latest pg-promise (version 8.x or newer):
const pgp = require('pg-promise')(/* initialization options */);
class FilterSet {
constructor(filters) {
if (!filters || typeof filters !== 'object') {
throw new TypeError('Parameter \'filters\' must be an object.');
}
this.filters = filters;
this.rawType = true; // do not escape the result from toPostgres()
}
toPostgres(/*self*/) {
// self = this
const keys = Object.keys(this.filters);
const s = keys.map(k => pgp.as.name(k) + ' = ${' + k + '}').join(' AND ');
return pgp.as.format(s, this.filters);
}
}
See Custom Type Formatting.

How safe is sending in functions to String.replace?

I am writing a string resolver function and I want it to take an object to resolve all "<%=variable%>" occurrences. I have the ability to set some of the object properties to functions and have those functions return values automatically. Is this safe? I originally planned on parsing out the tokens and getting return values from functions based on what I parsed from the token. Having String.replace do it seems a lot easier but I am not sure what the security risks are. Any ideas or comments? This will mainly be used in node.js so I don't imagine any user access to the function.
var data = {
name: "Mike",
job: "programmer"
},
funcs = {
date: function () {return new Date;},
timestamp: function () {return new Date().getTime();},
guid: function () {return "4FGGX3g";}
};
function resolve (str,resolvers) {
var regex,result;
result = str;
str.match(/(<%=.+?%>)/g).forEach(function (item) {
var token,itr,resolver;
token = item.split(/(\w+)/)[1];
for (itr = 0; itr < resolvers.length; itr += 1) {
resolver = resolvers[itr][token];
if (resolver) {
regex = new RegExp(item,'g');
result = result.replace(regex,resolver);
break;
}
}
});
return result;
}
console.log(resolve("My name is <%=name%> and I am a <%=job%>.\nThe date is: <%=date%>\nTimestamp: <%=timestamp%>\nUniqieID: <%=guid%>",[data,funcs]));

Categories

Resources