I want to parse a string that is generated inside a class instance by calling a peggy generated parser. One of the parsing actions needs to invoke a function defined inside the class instance, so that I have access to the environment of that instance, including this. How can I make that happen?
You can set a "global-initializer" at the start of your grammar. You should be able to set up your environment there. From the docs:
const parser = peggy.generate(`
{
// options are available in the per-parse initializer
console.log(options.validWords); // outputs "[ 'boo', 'baz', 'boop' ]"
}
validWord = #word:$[a-z]+ &{ return options.validWords.includes(word) }
`);
const result = parser.parse("boo", {
validWords: [ "boo", "baz", "boop" ]
});
console.log(result); // outputs "boo"
Related
I just want to ask how to pass an array of objects to another function.
I have a function
function btnB(weekly) {
console.log(weekly);
}
function btnA() {
const array = [{ abc : 123 }, { def : 456 }]
div.innerHTML = `<div onclick="btnB(${array[0]});"`;
divList.appendChild(div);
}
btnA();
And I'm getting an error
Uncaught SyntaxError: Unexpected identifier
You can't substitute an object like that in a template literal. It converts the object to a string, which returns [Object object].
Use JSON.stringify() to convert it to an object literal.
function btnA() {
const array = [{ abc : 123 }, { def : 456 }]
div.innerHTML = `<div onclick='btnB(${JSON.stringify(array[0])});'`;
divList.appendChild(div);
}
You also should use single quotes around the onclick value, because JSON uses double quotes around the object keys.
function btnB(weekly) {
console.log(weekly);
}
function btnA() {
const array = [{ abc : 123 }, { def : 456 }];
const div = document.createElement('div'); // delete if useless
div.addEventListener('click', () => {
btnB(array[0])
});
divList.appendChild(div);
}
I'm assuming the [0] is part of your attempt to solve this, but based on the question asking to pass an array of objects to the click handler, I'll refer to that instead.
Inline event handlers are deprecated and problematic anyway, but using them in HTML generated by JavaScript completes the circle in an absurd way. It would be a lot easier and more robust (and secure) to attach the click handler via JavaScript as well, as follows:
const array = [{ abc: 123 }, { def: 456 }]
div.innerHTML = '<div></div>' // Just the inner div without onclick
div.firstElementChild.addEventListener('click', () => btnB(array))
divList.appendChild(div)
I have a todo object
from example, this is my expected object
foo ={
name:'F',
lastName: 'JOI',
address:{
street:'where?'
}
}
and I got all these fields with more properties that I want to remove
for example, this is the object that I receive
bar ={
name:'F',
lastName: 'JOI',
address:{
street:'where?'
},
more:'temp',
prop:'should remove'
}
Does there is a way to remove the unnecessary without loop foreach property?
I am using TypeScript if it can help
The simplest thing is to write the code for it directly:
const foo: YourType = {
bar.name,
bar.lastName,
bar.address,
bar.street
};
// Use `foo`
That also has the advantage of being typesafe in TypeScript; if you tried to grab a property that isn't a member of YourType or that doesn't exist on bar, you'll get an error from TypeScript.
No.
TypeScript checks that types matches but it does not provide any utility to iterate over type properties, because all the type annotations disappear at compile time.
If you want to delete several keys, this information will have to be present on the "JS layer", because the "TS layer" (types, interfaces, etc) get compiled out, so you'll have to have somewhere a plain JS object or array with all the keys to delete (or to keep). Possible implementations:
// keep only the keys you want
const bar = {
name: bar.name,
lastName: bar.lastName,
address: bar.address,
}
// same but with a helper
const keepProps = (src, keys) => Object
.entries(src)
.reduce(
(dst, [key, value]) => {
if (keys.includes(key)) dst[key] = value;
return dst;
},
{}
);
const bar = keepProps(foo, ['name', 'lastName', 'address']);
// remove unwanted keys
const bar = { ...foo }; // spread is required to create a different object reference
delete bar.more;
delete bar.prop;
About that "no for loop" requirement, it won't be possible, if you want to do several times the same operation, it will either be an explicit loop, or loop hidden by the syntax, like { ...obj } or Array.filter.
I need to validate JSON files in following way:
const setupSchema = fs.readFileSync(schemaDir +'/setup.json');
and compiling:
const setupValidator = ajv.compile(setupSchema);
My issue is that line:
console.log( setupValidator('') );
Always returns true even as validator's parameter is empty string like above. I suppose that the way of loading is bad but... need ask smarter people than me.
From the quick start guide: (http://json-schema.org/)
The JSON document being validated or described we call the instance,
and the document containing the description is called the schema.
The most basic schema is a blank JSON object, which constrains
nothing, allows anything, and describes nothing:
{}
You can apply constraints on an instance by adding validation keywords
to the schema. For example, the “type” keyword can be used to restrict
an instance to an object, array, string, number, boolean, or null:
{ "type": "string" }
This means that if your schema is either an empty object or does not use the JSON Schema vocabulary, Ajv's compile function will always generate a validation function that always passes:
var Ajv = require('ajv');
var ajv = new Ajv({allErrors: true});
var schema = {
foo: 'bar',
bar: 'baz',
baz: 'baz'
};
var validate = ajv.compile(schema);
validate({answer: 42}); //=> true
validate('42'); //=> true
validate(42); //=> true
Perhaps your setup.json is either incorrectly loaded or isn't a schema as per the JSON Schema specification.
// You should specify encoding while reading the file otherwise it will return raw buffer
const setupSchema = fs.readFileSync(schemaDir +'/setup.json', "utf-8");
// setupSchema is a JSON string, so you need to parse it before passing it to compile as compile function accepts an object
const setupValidator = ajv.compile(JSON.parse(setupSchema));
console.log( setupValidator('') ) // Now, this will return false;
Instead of doing above, you can just simply require the json file using require.
const setupSchema = require(schemaDir +'/setup.json');
const setupValidator = ajv.compile(setupSchema);
console.log( setupValidator('') );
I've done similar things before but I can't get this to work.
I run this and get "Cannot read property '0' or undefined..."
(The randomResult() function returns a random element from a passed array and works ok elsewhere.)
var objectOfArrays = {
array1: ["apple","orange","pear","mango"],
array2: [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
]
}
console.log(randomResult(objectOfArrays.array2));
The object is not defined yet, you can't use this inside an object declaration.
To be clear, something like that:
const obj = {
a: 1,
b: this.a,
c: obj.a
}
Won't work, both b and c cannot access to the object that is currently declared.
However, if you delay the access, then it will works:
const objectOfArrays = {
array1: ["apple","orange","pear","mango"],
get array2() {
return [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
]
}
}
Defining a getter the code will be executed only when you're trying to access to objectOfArrays.array2. Keep in mind two things, however: first, it will be readonly: if you want to set also the prop array2 you have to create a setter too. Second, and more important, in this way, every time you access to array2 the getter is executed, therefore a new array is returned – with a new random result from array1. If you want to return the same, you have to cache the result somewhere:
const objectOfArrays = {
array1: ["apple","orange","pear","mango"],
_array2: null, // this could be actually omitted, see `Symbol` example
get array2() {
return this._array2 || (this._array2 = [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
])
}
}
(You could use Symbol for make the property more private, but it's better not over complicate things now)
Using Symbol
Since it was asked in the comment, even if it's outside the scope of the answer.
Caching the property to _array2 is good enough in most of the cases; however the property is easily accessible directly:
console.log(objectOfArrays._array2) // null
And can be tampered easily:
objectOfArrays._array2 = [1, 2];
Changing the behavior of the getter in array2.
A Symbol could be used to make _array2 more private. Assuming we are in a module:
const _array2 = Symbol("array2"); // the string is just for debug
export default objectOfArrays = {
array1: ["apple","orange","pear","mango"],
get array2() {
return this[_array2] || (this[_array2] = [
"I want no fruit!",
"I will have one " + randomResult(this.array1) + "."
])
}
}
In this way, outside the module there is no a direct way to refer the "private" _array2 property, since the Symbol is needed – and we do not export that.
It won't also appear in iteration (for example, with Object.keys(objectOfArrays)). Of course there is still a way to access to it, using getOwnPropertySymbol, but that means usually a more deliberate code than an accident one – e.g. iterate the keys of an object.
I mentioned module, but this is also applying if we're using a block scope:
// create a block
{
// const is block scoped, therefore is not accessible outside
const _array2 = Symbol("array2");
// `var` is not block scoped, so it will be accessible outside
// also `global.objectOfArrays` would be valid, where `global`
// is a reference to the global object (there is a spec to have
// it by default but is not implemented yet.
var objectOfArrays = ... // same code
}
Or in a closure:
(function(exports) {
const _array2 = Symbol("array2");
exports.objectOfArrays = ... // same code
}(this)); // or `window` or whatever is the export object / global one.
Alternative can be temporary variable:
var temp, objectOfArrays = {
array1: temp = ["apple","orange","pear","mango"],
array2: [
"I want no fruit!",
"I will have one " + temp + "."
]
}
console.log( objectOfArrays );
or separate statements:
var objectOfArrays = { array1: temp = ["apple","orange","pear","mango"] };
objectOfArrays.array2 = [ "I want no fruit!",
"I will have one " + objectOfArrays.array1 + "." ];
console.log( objectOfArrays );
I am trying to call an async function and specify a custom type (let's call it "CustomType"). I've specified a few random properties in that type, it's just to understand that it's something that comes from a database (content could vary depending on the retrieved item, I have many different "CustomType" for each kind of file stored in a NoSql database).
This is the code testable by using https://flow.org/try
/* #flow */
// Type that would be returned in the case of usage of "mySpecificCallFunc(): Promise<Array<CustomType>>" defined below
declare type CustomType = {
prop1: boolean,
prop2: string,
prop3: Date
}
// Generic function that does the hard work
async function asyncFunc<T>(dateStart: Date, otherArgument: string):Promise<Array<T>>
{
let r:Array<T> = [] // do a call that returns an array that must be of a specific type
return r
}
// High level function that would send parameters to the generic function "asyncFunc"
async function mySpecificCallFunc(): Promise<Array<CustomType>>
{
let r = await asyncFunc<CustomType>(new Date(), 'test')
return []
}
Cannot reference type CustomType [1] from a value position.
Flow does not want the custom type to be used.
In C# this kind of generic usage would be totally acceptable so I do not understand why it is complaining?
It gives the following error :
20: let r = await asyncFunc<CustomType>(new Date(), 'test')
^ Cannot compare boolean [1] to string [2].
References:
20: let r = await asyncFunc<CustomType>(new Date(), 'test')
^ [1]
20: let r = await asyncFunc<CustomType>(new Date(), 'test')
^ [2]
20: let r = await asyncFunc<CustomType>(new Date(), 'test')
^ Cannot reference type `CustomType` [1] from a value position.
References:
3: declare type CustomType = {
^ [1]
UPDATE:
There is currently no link between "CustomType" and the request arguments.
In the real world scenario, it looks like this :
call: DbRetrieve('type1', param1, param2)
return: [{ _type: 'type1', prop1: true, prop2:'b' }] // an array containing <CustomType> objects
As you can see, there's no "shape" that can be defined from the arguments of the function asyncFunc because the arguments are not always linked to the properties of the returned object.
It's an ORM-like call, I just wanted to be able to specify the type without doing some "brute cast", but I may be following the wrong path because the type cannot be inferred from the usage...
You can not specifying type directly on call.
Your CustomType is an object, but in code you expect a boolean
So, first of all you need to make a link between an incoming and outgoing data:
async function asyncFunc<T>(p: T):Promise<T[]> {
return []
}
<T> in the function declaration is just like declaring a variable, but p: T and :Promise<T[]> make a dependency
Second, you need to make the T a little bit narrow by async function asyncFunc<T: CustomType>.... And change your type CustomType = boolean;
After that, you just need to call await asyncFunc(true); without any typing.
UPD:
You trying to specify a type the function should return, just on the function call - it`s not a right way not on flow and not on JS at all. A function result type should be certainly declared on the function declaration point - it may be a single type of several types combination (type0 | type).
Generic types is used to make a relations between parameters and result. So, you can, for example, make a function that get a param and returns an array of same types values like function doSmt<T>(a: T): T[] {return [a];}
I`m not sure what exactly you trying to do, but maybe you need something like this:
type CustomType<A, B> = {
prop0: A,
prop1: B,
}
function asyncFunc<C: string, D: number>(foo: C, bar: D): CustomType<C, D> {
// some actions
}