I'm trying to implement a primitive type of Polish Notation Calculator and I'm using an object to define each operation.
Such that calling
new RPNCalculator().calculate([4, 5, '+']);
will produce an answer of 9 or
new RPNCalculator().calculate([4, 5, '+', 3, 5, '+', '*']);
will produce an answer of 72.
The code is here:
function RPNCalculator(arr) {
this.calculate = function(arr) {
var resultArr =[];
for(var i=0; i < arr.length; i++) {
if(typeof(arr[i]) == 'number') {
resultArr.push(arr[i]);
}
else {
var a = resultArr.pop();
var b = resultArr.pop();
var c = opers[arr[i]].apply(this, [a, b]);
resultArr.push(c);
}
}
return resultArr.pop();
}
var opers = {
"+": function(a, b) { return a + b; },
"-": function(a, b) { return a - b; },
"*": function(a, b) { return a * b; },
"/": function(a, b) { return a / b; }
}
}
The calculations work correctly, but what I would like to know is whether the following line
var c = opers[arr[i]].apply(this, [a, b]);
is the best way to invoke the required function contained inside the opers object based on the symbol at the current index in the array, or is there a better way to do it?
You don't really need a reference to this in your code, because the functions aren't working on any class members.
In this case, you can simply do:
var c = opers[arr[i]](a, b);
Which is a little cleaner. To be the most readable, however, I recommend this:
var operator = opers[arr[i]];
var c = operator(a, b);
Related
I'm using the js-yaml package. It has a function named dump() that will write out a JavaScript data structure in YAML format. I want to specify the order of the keys when it dumps out an object. Here is what their docs say:
sortKeys (default: false) - if true, sort keys when dumping YAML. If a function, use the function to sort the keys.
But there's absolutely no mention of how to use a function to sort the keys. What parameters does it accept? What return values does it expect? It's exactly what I need, but there's no documentation on how to use a function with the sortKeys option.
FYI, I don't want to sort the keys alphabetically. I don't want them to appear in a random order. I don't want them to appear in the order the keys were added. I want total control over which order they appear.
It appears that when you supply a function as the sortKeys option, that function behaves the same way as the function that you can use with the Perl sort() function. I.e. the function receives 2 arguments, call them a and b. If you want key a to appear before key b, return -1 from the function. If you want key a to appear after key b, return 1 from the function. If keys a and b should be considered equal, return 0 from the function. Using this method, I developed a function named toTAML that accepts a javascript data structure and an array of key names. When displaying an object, it will display the keys in the same order as in the supplied array. Keys that don't appear in the array will be put at the end, ordered alphabetically. I will post that function in CoffeeScript, then in the equivalent JavaScript.
import yaml from 'js-yaml'
compareFunc = (a, b) =>
if (a < b)
return -1
else if (a > b)
return 1
else
return 0
toTAML = (obj, lKeys) ->
h = {}
for key,i in lKeys
h[key] = i+1
sortKeys = (a, b) =>
if h.hasOwnProperty(a)
if h.hasOwnProperty(b)
return compareFunc(h[a], h[b])
else
return -1
else
if h.hasOwnProperty(b)
return 1
else
# --- compare keys alphabetically
return compareFunc(a, b)
return yaml.dump(obj, {
skipInvalid: true
indent: 3
sortKeys
lineWidth: -1
})
console.log toTAML({e:5,d:4,c:3,b:2,a:1}, ['c','b','a'])
Output:
c: 3
b: 2
a: 1
d: 4
e: 5
// Generated by CoffeeScript 2.7.0
var compareFunc, toTAML;
import yaml from 'js-yaml';
compareFunc = (a, b) => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
};
toTAML = function(obj, lKeys) {
var h, i, j, key, len, sortKeys;
h = {};
for (i = j = 0, len = lKeys.length; j < len; i = ++j) {
key = lKeys[i];
h[key] = i + 1;
}
sortKeys = (a, b) => {
if (h.hasOwnProperty(a)) {
if (h.hasOwnProperty(b)) {
return compareFunc(h[a], h[b]);
} else {
return -1;
}
} else {
if (h.hasOwnProperty(b)) {
return 1;
} else {
// --- compare keys alphabetically
return compareFunc(a, b);
}
}
};
return yaml.dump(obj, {
skipInvalid: true,
indent: 3,
sortKeys,
lineWidth: -1
});
};
console.log(toTAML({
e: 5,
d: 4,
c: 3,
b: 2,
a: 1
}, ['c', 'b', 'a']));
The function below creates a calculator which at first supports only addition and subtraction of two numbers, but can be extended with the "this.addMethod" to accommodate other operators, like "/", "*", and "**".
However, when I add a new calculator via the constructor ("new") and add new operations to it, the power operation("**") works properly only if it is called first.
The full code below:
function Calculator() {
this.supportedOperators = ['+', '-'];
this.supportedMethods = [
(a, b) => a + b,
(a, b) => a - b
];
this.calculate = (str) => {
for (operator of this.supportedOperators) {
if (str.includes(operator)) {
let delimiter = operator.length; // for '**' operator
let firstOperand = str.slice(0, str.indexOf(operator));
let secondOperand = str.slice(str.indexOf(operator) + delimiter);
return this.supportedMethods[this.supportedOperators.findIndex(item => item === operator)]
(+firstOperand, +secondOperand);
/* check the supported operators, then use it on operands
A mess here, but i tried my best to make it more readable and understandable */
} else console.log('Unsupported operation');
}
};
this.addMethod = (operator, method) => {
this.supportedOperators.push(operator);
this.supportedMethods.push(method);
}
}
let powerCalc = new Calculator;
powerCalc.addMethod("**", (a, b) => a ** b); // works fine
powerCalc.addMethod("*", (a, b) => a * b);
powerCalc.addMethod("/", (a, b) => a / b);
let result = powerCalc.calculate("4 ** 3"); // 64, as should be(other values also supported)
console.log(result);
However, if I change the order in the last sequence of code, so that the addition of "**" operation is no longer the first, like this:
function Calculator() {
this.supportedOperators = ['+', '-'];
this.supportedMethods = [
(a, b) => a + b,
(a, b) => a - b
];
this.calculate = (str) => {
for (operator of this.supportedOperators) {
if (str.includes(operator)) {
let delimiter = operator.length; // for '**' operator
let firstOperand = str.slice(0, str.indexOf(operator));
let secondOperand = str.slice(str.indexOf(operator) + delimiter);
return this.supportedMethods[this.supportedOperators.findIndex(item => item === operator)]
(+firstOperand, +secondOperand);
/* check the supported operators, then use it on operands
A mess here, but i tried my best to make it more readable and understandable */
} else console.log('Unsupported operation');
}
};
this.addMethod = (operator, method) => {
this.supportedOperators.push(operator);
this.supportedMethods.push(method);
}
}
let powerCalc = new Calculator;
powerCalc.addMethod("*", (a, b) => a * b); //
powerCalc.addMethod("/", (a, b) => a / b); // those two work fine regardless of order
powerCalc.addMethod("**", (a, b) => a ** b); // changed the order, no works fine
let result = powerCalc.calculate("4 ** 3"); // throws NaN with any value
console.log(result);
The power operation now returns NaN.
I am at a loss trying to figure it out. Please help.
As the comments put it above, the problem was that the ** and * operators both contain the "*" symbol. One of the proposed solutions was to implement sort() so that the operators with longest length would come first.
The other was to use a different data structure, and i decided to went with it. The code is given below:
function Calculator() {
this.methods = {
"-": (a, b) => a - b,
"+": (a, b) => a + b
};
this.calculate = function(str) {
let split = str.split(' '),
a = +split[0],
operator = split[1],
b = +split[2] // split the input string into an array of two operands converted to numbers and an operator
if (!this.methods[operator] || isNaN(a) || isNaN(b)) {
return NaN;
}
return this.methods[operator](a, b);
}
this.addMethod = function(name, func) {
this.methods[name] = func;
};
}
let powerCalc = new Calculator;
powerCalc.addMethod("*", (a, b) => a * b);
powerCalc.addMethod("/", (a, b) => a / b);
powerCalc.addMethod("**", (a, b) => a ** b); // the order doesn't matter anymore
let result = powerCalc.calculate("4 ** 3");
alert( result ); // 64, as it should be
Thanks everyone for the input!
Also one of the comments pointed out that the question name was not relevant to the actual problem i encountered. I will edit it shortly, when i come up with a better idea. Sorry for the confusion, i'm just starting to learn JS.
If you have an idea on how to better name the question, please post it in the comments.
I have a function 'sometimes' and want to return a function object from it.
Below is my code:
let add = (a, b) => {
return a + b;
};
myFunc = sometimes(add);
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(myFunc(2 + i, 3 + i));
}
function sometimes (inputFunc){
return function (a, b){
return inputFunc()
}
}
outputArr
I expect my outputArr variable to equal:
[5, 7, 9]
Instead mine equals:
[ NaN, NaN, NaN ]
What am I doing wrong?
You are not passing the parameters to the wrapped function. The add function tries to sum two undefined values and the result is NaN (not a number).
You have to pass the parameters to the wrapped function:
return function(a, b) {
return inputFunc(a, b); // <<<
}
Since sometimes is a higher order function, that needs to wrap different functions, with a changing number of parameters, it can't know the implementation of the wrapped function. To ensure that, you should use rest parameters (to collect the parameters to an array) and spread (to convert the array back to parameters) to pass the arguments to the wrapped function.
let add = (a, b) => {
return a + b;
};
myFunc = sometimes(add);
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(myFunc(2 + i, 3 + i));
}
function sometimes(inputFunc) {
return function(...args) {
return inputFunc(...args)
}
}
console.log(outputArr);
you can use your code as
function sometimes(a,b){
return a + b;
}
const outputArr = [];
for (let i = 0; i < 3; i++) {
outputArr.push(sometimes(2 + i, 3 + i));
}
console.log(outputArr);
now the output is
[ 5, 7, 9 ]
I'm learning Javascript and wondering if it's possible to store a declared function in a variable to be used later?
For context,
function add(a, b) {
return a + b;
}
var addTogether = _.partial(add, 1);
doSomething() // returns a promise that resolves to a 2
.then(addTogether); // expect to return 3
Is there a way to achieve this?
var Add = function (a, b)
{
return a + b;
}
var result = Add (2, 3);
Absolutely. Functions ARE data in JavaScript.
var foo = function(a, b) {
return a + b;
}
Is perfectly legitimate.
function add(a, b) {
return a + b;
}
var foo = add;
console.log(foo(5,10));
console.log(add(10, 20));
You can also use ES6 syntax to store anonymous functions in constants, like so:
const add = (a, b) => a + b;
const addOne = a => add(a, 1);
console.log(add(5, 10)); // 15
console.log(addOne(5)); // 6
Yesterday nigth I maked this question Delete elements from array javascript
But I mistook, my explanation and my example were about an intersection between two arrays.
What I wanted to ask is about how remove elements on array that doesn´t exist on other array.
Example:
Array A=> [a, b, c, d]
Array B=> [b, d, e]
Array C= removeElementsNotIn(A, B);
Array C (after function)-> [a,c]
Thank you very much.
You can use .filter() to selectively remove items that don't pass a test.
var c = a.filter(function(item) {
return b.indexOf(item) < 0; // Returns true for items not found in b.
});
In a function:
function removeElementsNotIn(a, b) {
return a.filter(function(item) {
return b.indexOf(item) < 0; // Returns true for items not found in b.
});
}
var arrayC = removeElementsNotIn(arrayA, arrayB);
If you want to get really fancy (advanced only), you can create a function that returns the filtering function, like so:
function notIn(array) {
return function(item) {
return array.indexOf(item) < 0;
};
}
// notIn(arrayB) returns the filter function.
var c = arrayA.filter(notIn(arrayB));
Thanks Second Rikhdo
full code:
var a = [1,2,3,4,5];
var b = [4,5,6,7,8,9];
var new_array = a.filter(function(item) {
return b.indexOf(item) < 0; // Returns true for items not found in b.
});
alert(new_array);
// results: array[1,2,3]
Demo: https://jsfiddle.net/cmoa7Lw7/
a1 = ['s1', 's2', 's3', 's4', 's5'];
a2 = ['s4', 's5', 's6'];
a3 = [];
function theFunction(ar1, ar2) {
var ar3 = [];
for (var i = 0; i < a1.length; i++) {
if (ar2.indexOf(ar1[i]) != -1) {
ar3.push(ar1[i]);
}
}
return ar3;
}
a3 = theFunction(a1, a2);
document.getElementById('out').innerHTML = a3.toString();
<div id="out"></div>
2022 answer
There is one more alternative which is easier to read:
const c = a.filter(item => !b.includes(item))