Using rollup, buble, flow-remove-types,
Is it possible to create an ENUM of classes instances for chess board representation, as types, like this:
// a Ref is a class or a type
class Ref { /* ... */ }
// Refs is an ENUM
Refs.forEach((ref: Ref, key: string) => {
console.log(key) // outputs: "a1", ..., "h8" successively
})
// type checking should work
typeof Refs.a1 === Ref // true
// etc...
typeof Refs.h8 === Ref // true
// move(ref) --> ref will work
Refs.a1.move(7, 7) === Refs.h8 // true
Refs.h8.move(-7, -7) === Refs.h8 // true
// with...
Refs.a1.move(0, 0) === Refs.a1 // true
// void reference
Refs.a1.move(-1, -1) === null
// or
Refs.a1.move(-1, -1) === Refs.EMPTY
A possible modular implementation would be packing the Ref class and the Refs collection in the same file, with a initialization code, like Enuify lib does... But how to make the Ref#move method working properly ??
The same as :
TicTacToe.X.us =TicTacToe.X
TicTacToe.X.them =TicTacToe.O
TicTacToe.O.us =TicTacToe.O
TicTacToe.O.them =TicTacToe.X
something like this, is perfectible, but works fine for me...
type TF = 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'
type TR = '1'|'2'|'3'|'4'|'5'|'6'|'7'|'7'
type TRefDef = {
file: TF,
fidx: number,
rank: TR,
ridx: number
}
interface IRef {
move (df: number, dr: number) : IRef
}
const FILES: Array <TF> = 'abcdefgh'.split('')
const RANKS: Array <TR> = '12345678'.split('')
const all: {
[key:string] : IRef
} = {}
const compute = function(fidx: number, ridx: number): IRef {
const file: TF = FILES[fidx]
const rank: TR = RANKS[ridx]
return all[file + rank]
}
const select = function(key: string) : IRef {
return all[key]
}
const get = function(arg1: string | number, arg2: ?number) : IRef {
if(arguments.length === 1) {
return select (arg1)
}
if(arguments.length === 2) {
return compute (arg1, arg2)
}
}
const each = function (callback) {
Object.keys(all).forEach((key, idx) => {
callback.call(this, all[key], idx)
})
}
class Ref implements IRef {
constructor (refdef: TRefDef) {
this.file = refdef.file
this.fidx = refdef.fidx
this.rank = refdef.rank
this.ridx = refdef.ridx
this.key = this.file + this.rank
}
toString() : string {
return 'Ref: ' + '(' + this.fidx + ',' + this.ridx + ')' + ' ' + this.file + this.rank
}
move (df: number, dr: number) : Ref {
let f = FILES.indexOf(fidx)
let r = RANKS.indexOf(ridx)
f += df
r += dr
return all[FILES[f] + RANKS[r]]
}
}
FILES.forEach((file, fidx) => {
RANKS.forEach( (rank, ridx) => {
const key: string = file + rank
const ref: Ref = new Ref({ file, fidx, rank, ridx })
all[key] = ref
})
})
Ref.empty = new Ref('', -1, '', -1)
const Refs = { compute, select, each, get }
// let f = { compute, each, selection }
// console.log(f)
// export { compute, each, select, Ref }
export { Refs, Ref }
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 have the following setup:
A type StringMap which I want to work like Map<> but since I'm using vue2, Map<> reactivity is not possible and I've tried to do it myself via an Array:
import Vue from "vue";
export default class StringMap {
entries: Array<[string, number]>;
constructor(data?: Record<string, number>) {
Vue.set(this, "entries", data ? Object.keys(data).map((key) => [key, data[key]]) : []);
}
get(key: string): number {
return this.entries.find((e) => e[0] === key)?.[1];
}
set(key: string, value: number): this {
const entry = this.entries.find((e) => e[0] === key);
if (entry) {
Vue.set(entry, 1, value);
}
else {
this.entries.push([key, value]);
}
return this;
}
has(key: string): boolean {
return this.entries.some((e, idx, arr) => e[0] === key);
}
delete(key: string): boolean {
const idx = this.entries.findIndex((e, idx, obj) => e[0] === key);
if (idx != -1) {
this.entries.splice(idx, 1);
}
return idx != -1;
}
clear(): void {
Vue.set(this, "entries", []);
}
}
In my Vue-Template, I have an input-field that listens to the #change method and calls calculateTotals()
<input :id="'viewModel.phasenChangeRequests[0].ppmProjektPhaseResource[' + index +'].personalInternPlanFach_PT'"
v-model="viewModel.phasenChangeRequests[0].ppmProjektPhaseResource[index].personalInternPlanFach_PT"
class="form-control"
asp-horizontal="true"
#change="calculateTotals('totalPersonalInternPlanFach_PT')"/>
And the calculateTotals is just looping over the data and summing the fields:
calculateTotals(key: string = null) {
if (this.type === "PhasenCrs") {
let totalPersonalInternPlanFachPt = 0;
for (let year = this.startyear; year < this.startyear + 5; year++) {
const phasen = this.viewModel.phasenChangeRequests;
phasen.forEach((phase) => {
var ressourcenOfYear = phase.ppmProjektPhaseResource.filter(x => x.resourceYear === year)[0];
totalPersonalInternPlanFachPt += parseFloat(ressourcenOfYear.personalInternPlanFach_PT);
});
}
if (key === null) {
console.log(`Key 'totalPersonalInternPlanFach_PT' doesn't exist yet, set it to: ${totalPersonalInternPlanFachPt}`);
this.totals2.set("totalPersonalInternPlanFach_PT", totalPersonalInternPlanFachPt);
} else {
switch (key) {
case 'totalPersonalInternPlanFach_PT':
{
console.log(`set totalPersonalInternPlanFach_PT: ${totalPersonalInternPlanFachPt}`);
this.totals2.set("totalPersonalInternPlanFach_PT", totalPersonalInternPlanFachPt);
}
}
}
}
}
totals2 is a StringMap that is initialized in the created() method:
totals2: StringMap;
async created() {
this.startyear = parseInt(moment(this.startdate, "DD.MM.YYYY").format("YYYY"));
this.projekt = JSON.parse(this.projektjson);
this.totals2 = new StringMap();
await Axios.get(this.url)
.then(res => {
this.viewModel = res.data;
})
.then(() => {
this.calculateTotals();
});
}
When I run it, it initially looks like this:
So as you can see, all fields for each year are summed correctly.
When I now change the value of 2021, the following happens:
But in the console, I can see that the total was calculated correctly:
When I now change the value back to the initial value, it shows this:
So that the previous change is now reflected in the total field but the console is showing the correct result again.
It seems that the value in the view is always one tick behind the real value...
Why is this happening?
Thanks in advance
I have a JSON file from an external source containing a bunch of conditions I'd like to test. Either in realtime, or by somehow converting everything.
Let's say I have an instance of my class Person, containing {age: 13, country: "Norway"}, and that I have an external JSON file containing the following "helpers":
{
"is_child": "age < 16",
"is_adult": "age >= 16 and age < 67",
"is_senior": "age > 67",
"is_scandinavian": "country == 'Norway' or country == 'Sweden' or country == 'Denmark'",
}
and another file containing, for example, tickets I'd like to present, for example, "NorwegianTickets.json"
{
"childTicket": "is_child and is_scandinavian",
"flexTicket": "is_scandinavian and (is_adult or is_senior)"
}
How can I apply this logic to my code? If I want to run the condition "flexTicket" on my "Person", how should I map all the logic? How do I translate the "stringed" conditions, such as "and"/"or", and "()"?
You can easily achieve this using the eval function that execute a string as javascript.
So the logic will be:
Get the different conditions as a javascript string (is_child, is_adult, ...)
This function replace all variables (written as a string) by there value.
For that you will need to create a dictionary to list them all with the corresponding value:
const varsToReplace = {
country: 'Norway',
age: 12
}
Then you replace this variable in a given condition using the replace method. The only trick here is that you need to search for country and not country (if you not add the extra space before and after, a variable like user_country could be replaced by user_Norway). Also keep in mind that if you replace by a string you should wrapp the value in '':
const getConditionString = (condition) => {
let replacedConditon = ` ${conditions[condition]} `
Object.keys(varsToReplace).forEach((variable) => {
const re = new RegExp(` ${variable} `, 'g');
let replaceValue = ` ${varsToReplace[variable]} `
// If the value is a string we should add ''
if (typeof varsToReplace[variable] === 'string') {
replaceValue = ` '${varsToReplace[variable]}' `
}
replacedConditon = replacedConditon.replace(re, replaceValue)
})
return replacedConditon
}
Get the test as a javascript string (is_child and is_scandinavian, ...)
This function getTestString will replace all conditions key by the javascript string using the previous function:
const getTestString = (test) => {
let replacedTest = ` ${tests[test]} `
Object.keys(conditions).forEach((condition) => {
const re = new RegExp(` ${condition} `, 'g');
replacedTest = replacedTest.replace(re, ` ( ${getConditionString(condition)} ) `)
})
return replacedTest
}
Replace the different operators to be 'js valid':
const replaceOperators = (string) => {
const operators = {
or: '||',
and: '&&'
}
Object.keys(operators).forEach((operator) => {
const re = new RegExp(` ${operator} `, 'g');
string = string.replace(re, ` ${operators[operator]} `)
})
return string
}
Execute the js string using eval:
const evalTest = (test) => {
let testAsString = replaceOperators(getTestString(test))
return eval(testAsString)
}
Here is the full example:
const country = 'Norway'
const age = 12
const varsToReplace = {
country,
age
}
const conditions = {
"is_child": "age < 16",
"is_adult": "age >= 16 and age < 67",
"is_senior": "age > 67",
"is_scandinavian": "country == 'Norway' or country == 'Sweden' or country == 'Denmark'"
}
const tests = {
"childTicket": "is_child and is_scandinavian",
"flexTicket": "is_scandinavian and ( is_adult or is_senior )"
}
const getConditionString = (condition) => {
let replacedConditon = ` ${conditions[condition]} `
Object.keys(varsToReplace).forEach((variable) => {
const re = new RegExp(` ${variable} `, 'g');
let replaceValue = ` ${varsToReplace[variable]} `
// If the value is a string we should add ''
if (typeof varsToReplace[variable] === 'string') {
replaceValue = ` '${varsToReplace[variable]}' `
}
replacedConditon = replacedConditon.replace(re, replaceValue)
})
return replacedConditon
}
const getTestString = (test) => {
let replacedTest = ` ${tests[test]} `
Object.keys(conditions).forEach((condition) => {
const re = new RegExp(` ${condition} `, 'g');
replacedTest = replacedTest.replace(re, ` ( ${getConditionString(condition)} ) `)
})
return replacedTest
}
const replaceOperators = (string) => {
const operators = {
or: '||',
and: '&&'
}
Object.keys(operators).forEach((operator) => {
const re = new RegExp(` ${operator} `, 'g');
string = string.replace(re, ` ${operators[operator]} `)
})
return string
}
const evalTest = (test) => {
let testAsString = replaceOperators(getTestString(test))
console.log(testAsString)
return eval(testAsString)
}
console.log(evalTest('childTicket'))
console.log(evalTest('flexTicket'))
I would go for creating a DSL for that purpose. It's fun. I've written one to just give you some idea about it. Beware, its not fully tested, lacks basic functionality such as array access. I believe you may find better examples in the internet.
class Node_ {
children: Node_[];
constructor() {
this.children = [];
}
addChild = (node: Node_) =>
this.children.push(node);
evaluate = (context: any): boolean | number | string => {
throw new Error('Missing implementation');
}
}
enum ExprType {
Eq = 'eq',
Gt = 'gt',
Lt = 'lt',
Gte = 'gte',
Lte = 'lte',
Get = 'get',
}
class ExprNode extends Node_ {
expr: string;
constructor(expr: string) {
super();
this.throwIfInvalidExpr(expr);
this.expr = expr.toLowerCase();
}
throwIfInvalidExpr(expr: string) {
switch (expr.toLowerCase()) {
case ExprType.Eq:
case ExprType.Gt:
case ExprType.Lt:
case ExprType.Gte:
case ExprType.Lte:
case ExprType.Get:
break;
default:
throw new Error(`Unexpected expression: ${this.expr}`);
}
}
evaluate = (context: any) => {
switch (this.expr) {
case ExprType.Get:
return this.evaluateAccess(context);
default:
return this.evaluateCmp(context);
}
}
evaluateAccess = (context: any) => {
this.throwIfInvalidAccessOperands();
const prop = this.children[0].evaluate(context) as string;
const newContext = context[prop];
const child = this.children[1];
if (child) {
return child.evaluate(newContext);
} else {
return newContext;
}
}
evaluateCmp = (context: any) => {
this.throwIfInvalidOperands();
const left = this.children[0].evaluate(context);
const right = this.children[1].evaluate(context);
switch(this.expr) {
case ExprType.Eq:
return left === right;
case ExprType.Gt:
return left > right;
case ExprType.Gte:
return left >= right;
case ExprType.Lt:
return left < right;
case ExprType.Lte:
return left <= right;
}
}
throwIfInvalidOperands = () => {
if (this.children.length !== 2) {
throw new Error(`Invalid operand count ${this.children.length}`);
}
}
throwIfInvalidAccessOperands = () => {
if (this.children.length === 0 ||
this.children.length > 2) {
throw new Error(`Invalid access operand count ${this.children.length}`);
}
}
}
class ValueNode extends Node_ {
value: string | number;
constructor(value: string, str?: boolean) {
super();
if (str) {
this.value = value as string;
} else {
const num = parseInt(value);
if (Number.isNaN(num)) {
throw new Error(`Invalid number: ${value}`);
}
this.value = num;
}
}
evaluate = (_: any) => {
return this.value;
}
}
function tokenize(value: string): Node_ {
let index = 0;
const nodeStack = [];
let token = '';
while (index < value.length) {
switch(value[index]) {
case '(':
{
const node = new ExprNode(token);
nodeStack.push(node);
token = '';
}
break;
case ')':
{
if (token) {
const node = new ValueNode(token);
nodeStack.push(node);
addToParent(nodeStack);
token = '';
}
addToParent(nodeStack);
}
break;
case "'":
case '"':
const str = consumeString(value, index);
index += str.length + 1;
token += str;
{
const node = new ValueNode(token, true);
nodeStack.push(node);
addToParent(nodeStack);
}
token = '';
break;
case ',':
if (token) {
const node = new ValueNode(token);
nodeStack.push(node);
addToParent(nodeStack);
token = '';
}
break;
case ' ':
break
default:
token += value[index];
}
index++;
}
return nodeStack[0];
}
function consumeString(value: string, index: number) {
const delimiter = value[index++];
let ret = '';
while (value[index] !== delimiter) {
ret += value[index];
index++;
}
return ret;
}
function addToParent(nodeStack: Node_[]) {
console.assert(nodeStack.length > 0);
const last = nodeStack.pop();
if (nodeStack.length > 0) {
const parent = nodeStack.pop();
parent.addChild(last);
nodeStack.push(parent);
} else {
nodeStack.push(last);
}
}
{
const ast = tokenize('EQ("origami", GET("name"))');
const context = { name: 'origami' };
const context2 = { };
console.assert(ast.evaluate(context) === true);
console.assert(ast.evaluate(context2) === false);
}
{
const ast = tokenize('EQ(5, 5)');
console.assert(ast.evaluate({}) === true);
const ast1 = tokenize('EQ("foo", "foo")');
console.assert(ast1.evaluate({}) === true);
const ast2 = tokenize('EQ("foo", "bar")');
console.assert(ast2.evaluate({}) === false);
const ast3 = tokenize('GTE(15, 10)');
console.assert(ast3.evaluate({}) === true);
}
{
const ast = tokenize('GET("info", GET("person", GET("age")))');
const context = { info: { person: { age: 21 } } };
console.assert(ast.evaluate(context) === 21);
}
{
const ast = tokenize('LTE(21, GET("info", GET("person", GET("age"))))');
const context = { info: { person: { age: 21 } } };
console.assert(ast.evaluate(context) === true);
const context2 = { info: { person: { age: 15 } } };
console.assert(ast.evaluate(context2) === false);
}
{
const ast = tokenize('EQ(GET("info", GET("person", GET("planet"))), "earth")');
const context = { info: { person: { planet: "mars" } } };
console.assert(ast.evaluate(context) === false);
}
{
const ast = tokenize('GT(GET("person1", GET("age")), GET("person2", GET("age")))');
const context = { person1: { age: 56 }, person2: { age: 21 } };
console.assert(ast.evaluate(context) === true);
const context2 = { person1: { age: 25 }, person2: { age: 44 } };
console.assert(ast.evaluate(context2) === false);
}
I omitted AND & OR expressions, but it should be clear how to add them.
In this scenario, the client should submit the data along with the constraints. For example:
{
"context": {
"person": {
"age": 44,
"planet": "saturn"
}
},
"constraints": {
"shouldFrom": "EQ('mars', GET('person', GET('planet')))",
"minimumAge": "GTE(40, GET('person', GET('planet')))"
}
}
And the receiver part takes the each constraints, tokenize them and evaluate them with given context.
I have a problem with some class for cart , which I must use in my work.
Here is code of this class:
class Cart {
constructor() {
this.key = "IT_SPA_CART";
if (!this.exists()) {
this.setItSpaCart([]);
}
}
get() {
const cookies = document.cookie.split(";");
return cookies.find(cookie => cookie.startsWith(this.key));
}
exists() {
return this.get() !== undefined;
}
getItSpaCart() {
const cookieValue = this.get().slice(12);
const parsedValue = JSON.parse(cookieValue);
return parsedValue;
}
setItSpaCart(value) {
const stringifiedValue = JSON.stringify(value);
document.cookie = `${this.key}=${stringifiedValue}`;
}
add(item) {
const cartValue = this.getItSpaCart();
this.setItSpaCart([...cartValue, item]);
}
remove(item) {
const cartValue = this.getItSpaCart();
const itemInCart = cartValue.findIndex(val => val.name === item.name);
if (itemInCart !== -1) {
cartValue.splice(itemInCart, 1);
this.setItSpaCart(cartValue);
}
}
}
When I try to use this class, e.g. with method add(), like this:
let cart = new Cart();
cart.add([{ num: 1, cost: 2 }, { num: 3, cost: 4 }, { num: 5, cost: 6 }]);
this error occur:
Cannot read property 'slice' of undefined at Cart.getItSpaCart
Why this is happend?
Thanks for every hint.
I had the same problem ;-) Maybe You already know how to fix it, but if not, perhaps solution is changing code in this line: const cookies = document.cookie.split(";");. I changed ("; ) into ("; ").
I have grid which I want to export:
initializeColumnDefs() {
this.columnDefs = [];
this.columnDefs.push({
headerName: 'time,
field: 'completedTimestamp',
cellRenderer: (params: any) => {
if (params.data.isMomentarily)
return '';
return DatagridComponent.DefaultDatetimeCellRenderer(params);
},
comparator: (valueA: number, valueB: number) => {
return DatagridComponent.DefaultDatetimeCellComparator(valueA, valueB);
}
},
{
headerName: 'people',
field: 'people',
cellRenderer: (params: any) => {
if (!params || !params.value || params.value.length <= 0)
return '';
let titles = '';
params.value.forEach(element => {
if (element.name) {
titles += element.name + ',';
}
});
return titles.substring(0, titles.length - 1);
}
}
);
}
Above there's example of two columns: one with timestamp, one with object.
My export() method which I use to export as csv:
export() {
let header = this.columnDefs.map(columnDef => {
let id = columnDef.field || columnDef.colId || columnDef.value;
let headerName = columnDef.headerName;
return headerName;
});
let a: any;
let params: any = {
fileName: 'export.csv',
columnSeparator: ';',
skipHeader: true,
columnKeys: this.columnDefs.map(c => c.field || c.colId).filter(c => !!c)
};
params.customHeader = header.join(params.columnSeparator) + '\n';
this.grid.api.exportDataAsCsv(params);
}
However, I have trouble finding how format values before exporting, because here I only get header and field and no value?
And when I export my grid to csv instead of datetime I get e.g.
which is timestamp and for my object I get
Instead of having Tom, Bob, Ben
Does anyone know how to format these values before exporting?
In your export() function, you will have to add a parameter processCellCallback.
Something like this:
export() {
let header = this.columnDefs.map(columnDef => {
let id = columnDef.field || columnDef.colId || columnDef.value;
let headerName = columnDef.headerName;
return headerName;
});
let a: any;
let params: any = {
fileName: 'export.csv',
columnSeparator: ';',
skipHeader: true,
columnKeys: this.columnDefs.map(c => c.field || c.colId).filter(c => !!c)
};
params.customHeader = header.join(params.columnSeparator) + '\n';
params.processCellCallback = function(cellParams) {
if(cellParams && cellParams.column.colId === 'yourTimestampfield') {
return this.formatter; //apply your timestamp formatter
} else if(cellParams && cellParams.column.colId === 'yourObjectfield') {
return this.formatter; //apply your object formatter
} else
return cellParams.value // no formatting
}
this.grid.api.exportDataAsCsv(params);
}
Read more in the example and docs here.