Restricting the scope of an assignment in conditional in JavaScript - javascript

The following code pushes all the points below the limit into data, until getPoint returns null.
while ((x = getPoint()) && x < limit) {
data.push(x)
}
console.log(x, 'x should be undefined here')
In this particular case I use an assignment in a conditional, I know it looks ugly, but the question focuses on x which is not local to the block. I tried to place a let there, but it doesn't work.
Is it possible to restrict the scope of x inside the while statement?
Another working implementation would be this one, but in this case I double the test on x:
do {
let x = getPoint()
if (x && x < limit) {
data.push(x)
}
} while(x && x < limit)
or
while (true) {
let x = getPoint()
if (!x || x >= limit) {
break;
}
data.push(x)
}
or
function* getPointIterator(limit) {
let x = getPoint()
while(x && x < limit) {
yield x;
}
}
data.push(...getPointIterator(limit))

You may consider to change the while loop with a for loop:
var limit = 3;
var r = 2;
var data = [];
function getPoint() {
return r++;
}
for (let x=0; (x = getPoint()) && x < limit;) {
data.push(x)
}
console.log(typeof(x) === 'undefined' ? 'undefined' : x, 'x should be undefined here')

1. Code block {...}
You can use a “bare” code block {…} to isolate variables into a “local scope”.
{
// do some job with local variables that should not be seen outside
let message = "Hello";
alert(message); // Hello
}
alert(message); // Error: message is not defined
For your case:
const limit = 3;
let y = 0;
const getPoint = () => y++;
{
let x = 0;
while ((x = getPoint()) && x < limit) {
console.log(x);
}
}
console.log(x, 'x should be undefined here');
The code outside of the block (or inside another script) doesn’t see variables inside the block, because the block has its own Lexical Environment.
2. IIFE (function {...})
You can use so-called “immediately-invoked function expressions” (abbreviated as IIFE) used for this purpose.
They look like this:
(function() {
let message = "Hello";
alert(message); // Hello
})();
For your case:
const limit = 3;
let y = 0;
const getPoint = () => y++;
(function () {
let x = 0;
while ((x = getPoint()) && x < limit) {
console.log(x);
}
})();
console.log(x, 'x should be undefined here');
Here a Function Expression is created and immediately called. So the code executes right away and has its own private variables.
The Function Expression is wrapped with brackets (function {...}), because when JavaScript meets "function" in the main code flow, it understands it as the start of a Function Declaration.

Related

Caesar Cipher technique and reverse case in javascript

I am beginner and want to make my own function.
I want to hash the password by shifting every character by given x
positions and reverse to lowercase/uppercase.
I think the code below should return "EFGH7654" but it return 55 with no error message.
How can I fix it? Is it because of I put a function in a function?
Or I type wrong any thing?
function hashPassword(password, x) {
// password is a string, x is a number
// return a string
// (ex. password = 'ab1By', x = 3 so it should return "DE4eB")
function shift(text, s) {
result = "";
for (let i = 0; i < text.length; i++) {
let char = text[i];
if (char.toUpperCase(text[i])) {
let ch = String.fromCharCode((char.charCodeAt(0) + s - 65) % 26 + 65);
result += ch;
} else {
let ch = String.fromCharCode((char.charCodeAt(0) + s - 97) % 26 + 97);
result += ch;
}
}
return result;
}
function reversecase(x) {
var output = '';
for (var i = 0, len = x.length; i < len; i++) {
var character = x[i];
if (character == character.toLowerCase()) {
// The character is lowercase
output = output + character.toUpperCase();
} else {
// The character is uppercase
output = output + character.toLowerCase();
}
}
return output
}
var str = "";
var result = "";
var charcode = "";
for (var i = 0; i < password.length; i++) {
if (typeof password[i] === typeof str) {
char = shift(password[i], x)
charcode = reversecase(char)
result += charcode;
} else {
num = password[i] + x
number = num % 10
result += number.toString()
}
}
return result
};
console.log(hashPassword("abcd4321", 4))
There a quite some problems in your code.
The first problem here is not only the nesting, but the fact that you're defining the result variable in the outer function scope using the var keyword. Then you use (read/write) that variable in different places.
In function shift() (also in return statement)
In the outer function (also in return statement)
The thing you have to understand is, that you're referring to the same variable result every time. To ensure that your variables are scoped, i.e. are only valid within a block (if statement, function body, etc.), you should use the let or const keywords. This makes your code a lot safer.
The second problem are some assumptions you make regarding data types. If you have a string let s = "my string 123", the expression typeof s[x] === 'string' will be true for every x in s.
Another problem is the algorithm itself. The outer function hashPassword() iterates over all characters of the input string. Within that loop you call function shift(password[i], x), passing a single character. The first parameter of shift() is called text - and there is another for loop (which is confusing and does not make sense).
To make things short, please have a look at this simplified version:
function shift(char, x) {
let result;
const code = char.charCodeAt(0);
if (code >= 65 && code < 91) {
result = String.fromCharCode((code + x - 65) % 26 + 65);
}
else if (code >= 48 && code <= 57) {
result = String.fromCharCode((code + x - 48) % 10 + 48);
}
else {
result = String.fromCharCode((code + x - 97) % 26 + 97);
}
return result;
}
function reverseCase(character) {
if (character === character.toLowerCase()) {
return character.toUpperCase();
}
else {
return character.toLowerCase();
}
}
function hashPassword(password, x) {
let result = "";
for (let i = 0; i < password.length; i++) {
const char = shift(password[i], x);
result += reverseCase(char);
}
return result;
}
console.log(hashPassword("abcd4321", 4)); // Output: EFGH8765

JS function switch between either greater than or less than

I have a function that is quite long and at the moment I need a duplicate of the function where the only difference is it asks greater than rather than less than. So the only difference is > or <.
Is there any (non messy) way to make this kind of function just one function instead of the two here?
function(value, modifier) {
let result;
if (value > modifier) result = 10;
return result;
}
function(value, modifier) {
let result;
if (value < modifier) result = 10;
return result;
}
So basically I need a conditional greater than/less than sign.
EDIT: To be clear, in an ideal world I'd like to do this:
myFunction(10, 5, >)
EDIT: Adding one part of my actual function to make things clearer. I was hoping there might be real simple way to do it but it seems like perhaps not so maybe part of the actual function might help things:
function getEdges(colour) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
if (data[0] < colour) {
edge.push(y);
break;
}
}
}
return edge;
}
And there is another almost identical function like this where the only difference is the line if (data[0] > colour) { is greater than rather than less than.
If the only difference between the two functions is the comparison, then you can just extract that and make it a parameter
function getEdges(colour, comparator) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
if (comparator(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
//call with less than
getEdges(someColour, (a, b) => a < b)
//call with greater than
getEdges(someColour, (a, b) => a > b)
You can also keep your logic in one function and derive two more from it. This way you don't need to maintain multiple code blocks and you still get two explicit calls:
Using partial application with .bind:
function getEdges(comparator, colour) {
// ^ ^ the two parameters are swapped
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
if (comparator(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
//partial application
const getEdgesLessThan = getEdges.bind(null, (a, b) => a < b);
const getEdgesGreaterThan = getEdges.bind(null, (a, b) => a > b);
getEdgesLessThan(someColour)
getEdgesGreaterThan(someColour)
Using a curried function:
function getEdges(comparator) {
// ^---------
return function(colour) {// | taking two parameters
// ^ ----------
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
if (comparator(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
}
//currying
const getEdgesLessThan = getEdges((a, b) => a < b);
const getEdgesGreaterThan = getEdges((a, b) => a > b);
getEdgesLessThan(someColour)
getEdgesGreaterThan(someColour)
How about passing the condition function as a third parameter?
function getEdges(colour, condition) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
if (condition(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
Call the function and pass required condition.
for lesser than: getEdges(color, (data, color) => color < data);
for greater than: getEdges(color, (data, color) => color > data);
EDIT: To be clear, in an ideal world I'd like to do this:
myFunction(10, 5, >)
You can get something very similar:
const lt = (x, y) => x < y;
const gt = (x, y) => x > y;
function foo(value, modifier, compare) {
let result;
if (compare(value, modifier)) result = 10;
return result;
}
console.log(foo(2, 3, lt)); // 10
console.log(foo(3, 2, gt)); // 10
Using your second example:
const lt = (x, y) => x < y;
const gt = (x, y) => x > y;
const width = 3;
const height = 3;
const getData = (x, y) => [height * x + y];
console.log(getEdges(3, lt)); // [2]
console.log(getEdges(3, gt)); // [2,2]
function getEdges(colour, compare) {
const edge = [];
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x, y);
if (compare(data[0], colour)) {
edge.push(y);
break;
}
}
}
return edge;
}
Hope that helps.
You could keep the operator and just swap the terms according to your needs:
function getEdges(colour, operator) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
const [a, b] = operator == '<' ? [data[0], colour] : [colour, data[0]];
if (a < b) {
edge.push(y);
break;
}
}
}
return edge;
}
EDIT:
Ok, so if you want keep things simple and in separated functions, with minimal code changes you could implement it like this:
function getEdges(colour, operator = '<') {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData();
const [a, b] = operator == '<' ? [data[0], colour] : [colour, data[0]];
if (a < b) {
edge.push(y);
break;
}
}
}
return edge;
}
The signature getEdges(colour, operator = '<') makes the operator parameter optional. If you don't pass it to the function, it'll assume a default value of '<' so that you won't to have to change anything in your existing code. Then, you could make a second function that will reuse the original one, just with a different parameter:
function getEdgesGreaterThan(colour) {
return getEdges(colour, '>');
}
And there you have it! Hope it helps!
Based on the conversation in the comments, the purpose is to re-use the getEdges function for when a greater than or lower than comparison is needed. I've added a second parameter to indicate this, with it set to false as the default case. The if statement is dual-purpose in the sense that it makes a greater than comparison when the isGreater flag is set to true and a less than comparison when the isGreater flag is set to false. The rest of the logic is re-used verbatim with no duplication.
function getEdgesInner(colour, isGreater = false) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
if ((isGreater && data[0] > colour) || (!isGreater && data[0] < colour))
edge.push(y);
break;
}
}
}
return edge;
}
/* Public Api */
function getGreaterEdges(colour) { return getEdges(colour) }
function getLesserEdges(colour) { return getEdges(colour, true) }
You can try these ways. just like you wanted in your original sample! With short, stylish and beauty ideas:
mathematical trick:
function func(value, modifier, sign) {
let result, fc=sign=="<"?1:-1;
if (value*fc< modifier*fc) result = 10;
return result;
}
A useful JS feature:
function func(value, modifier, sign) {//this is slower probably
let result;
if (eval(value+sign+modifier)) result = 10;
return result;
}
Usage (both top ways):
console.log(func(1, 2, "<"));
Passing a delegate function (for compare):
function func(value, modifier, compF) {
let result;
if (compF(value, modifier)) result = 10;
return result;
}
Usage :
console.log(func(1, 2, function(v, m){return v<m;}/*or equivalent lambda function*/));
function(value, modifier, type) {
let result;
if(type == ">") {
if (value > modifier) result = 10;
} else {
if (value < modifier) result = 10;
}
return result;
}
and pass type as a string "<" or ">"
You should add 3rd argument which takes the condition, for example, string "gt" for Grater Than(>) and "lt" for Less Than(<)
function(value, modifier, cond){
if(cond === "gt") return value > modifier ? 10 : undefined;
else if(cond === "lt") return value < modifier ? 10 : undefined;
}
EDIT: I came up with a better solution, suitable for your updated question.
let check = (value, modifier, cond)=>eval(value + cond + modifier) ? 10 : undefined
here you pass "<" or ">" for cond parameter.
EDIT 2:
function getEdges(colour,cond) {
for (let x = 0; x < width; x++) {
for (let y = height - 1; y >= 0; y--) {
const data = getData(x,y);
let shoudPush = eval(data[0] + cond + colour) ? true : false
if(shouldPush) {
edge.push(y);
break;
}
}
}
return edge;
}
EDIT: To be clear, in an ideal world I'd like to do this:
myFunction(10, 5, >)
You can almost do that with using eval. Although I'm a little confused about your result since it potentially returns undefined.
var myFunction = function(value, modifier, operation) {
var evalResult = eval(value + operation + modifier)
// I like ternary expression but could also be written as an if/else statement
return evalResult ? 10 : undefined
}
myFunction(10, 5, '<') // outputs undefined
myFunction(10, 5, '>') // outputs 10
Just be aware that eval can be dangerous.
https://medium.com/#eric_lum/the-dangerous-world-of-javascripts-eval-and-encoded-strings-96fd902af2bd
EDIT
Hey I made a codepen in response to your edited answer.
https://codepen.io/springborg/pen/ydayYa
I think this is what most closely matches your proposed syntax. Although I would probably advise against using eval unless you are absolutely certain no vulnerability will be exposed.
My recommendation is using Aditya Bhave answer :)
https://stackoverflow.com/a/56649540/1303205
If reusability is your ultimate goal, here's a curried example of how one might do this (somewhat functional-programming-stylez).
This approach allows greater reuse of the code. You might want to compare things at two points in your code, and return different values according to context.
let functionalComparer = comp => modifier => result => value => {
return comp(value, modifier) ? result: undefined
}
// curry the crap out of it!
let gt = (x,y) => x > y
let gtComp = functionalComparer(gt)
let gt100 = gtComp(100)
let whenGt100ReturnString = gt100("more than 100!")
console.log(`
with value 99: ${whenGt100ReturnString(99)},
with value 101: ${whenGt100ReturnString(101)}
`)
let lt = (x,y) => x < y
let whenLt50ReturnFoo = functionalComparer(lt)(50)("FOO")
console.log(`
with value 49: ${whenLt50ReturnFoo(49)},
with value 51: ${whenLt50ReturnFoo(51)}
`)

Passing variable value as condition in if statement

I'm working on script where I wanted to pass value as condition in if statement but it is treating is as string. I am not getting why this value is recognized as string. Do I need to extract the info from the array directly?
//extracting date and preparing condition
function dateArgument () {
var start = $("#start").val();
var end = $("#end").val();
var y;
if(start && end){
y = ' && arr[i].created.substring(0,10) >= '+start+' && arr[i].created.substring(0,10) <= '+end;
} else {
y = '';
}
return y;
};
// Using Condition
function count (arr, nameVar, valueVar){
var x = 0;
// Preparing condition
var contd = '$.trim(arr[i][nameVar]) == valueVar'+dateArgument();
console.log(contd);
var start = $("#start").val();
var end = $("#end").val();
for (i=0; i < arr.length; i++){
// using prepared condition
if (contd) {
x++;
}
}
return x;
};
// using prepared condition
function mapData (){
$.ajax({
url:'http://192.168.2.20:8020'+partUrl('province')+partUrl('activity')+partUrl('priority')+partUrl('status'),
type: 'GET',
dataType: 'json',
success: function (data){
//Using code here
console.log(count(data,'priority','HighclearLayers();
createPins(data);
//summaryValues(data);
}
})
};
I am not getting why this value is recognized as string
It is indeed a String, make it a function to be invoked later
var contd = (i) => $.trim(arr[i][nameVar]) == valueVar && dateArgument(i);
and use it as
if ( contd(i) )
Also change dateArgument as
function dateArgument ()
{
var start = $("#start").val();
var end = $("#end").val();
var y = () => true;
if(start && end){
y = (i) => arr[i].created.substring(0,10) >= start && arr[i].created.substring(0,10) <= end;
}
return y;
};
It's simple enough. You cannot use a string as a condition.
If you really need to, use eval(). But remember, eval is evil!
if (eval(contd)) {
x++;
}
Better solution: use real conditions instead of string conditions
if(start && end){
//solve the first part of your condition here
//this will return a boolean
y = arr[i].created.substring(0,10) >= start && arr[i].created.substring(0,10) <= end;
} else {
y = true;
}
//later prepare the condition it like this
//this will also return a boolean
var contd = $.trim(arr[i][nameVar]) == valueVar && dateArgument();

if condition in javascript using variables

The problem with this code is when I'm executing the if condition. The condition only works if i am using if (pixel.getx() <=100) but does not work for a var x = pixel.getX() & if (x <= 100). Can someone tell me why?
var image = new SimpleImage (200,200);
print (image);
for (var pixel of image.values())
var x = pixel.getX();
var y = pixel.getY()
if (x <= 100 && y <= 100)
{
pixel.setRed(255);
pixel.setBlue(0);
pixel.setGreen(0);
}
else if (x > 100)
{
pixel.setBlue(255);
pixel.setGreen(0);
pixel.setRed(0);
}
print (image);
your for loop is missing {}.
all it does the way you have it in your example is
executing var x = pixel.getX(); as many times as there are image.values()
if you need to repeat a multi line block of code within a for loop it needs to be inside {}
if you are repeating one statement - you don't need {} - that's why it worked when you had if (pixel.getX() <= 100) {...}
Your for loop is missing the braces { } and that's why its not working.
Modified code,
var image = new SimpleImage (200,200);
print (image);
for (var pixel of image.values()) {
var x = pixel.getX();
var y = pixel.getY()
if (x <= 100 && y <= 100) {
pixel.setRed(255);
pixel.setBlue(0);
pixel.setGreen(0);
} else if (x > 100) {
pixel.setBlue(255);
pixel.setGreen(0);
pixel.setRed(0);
}
print (image);
}

How to change a variable to something if it's undefined?

I don't have my script completed yet or anything so I can't post the code. Basically I need a variable to change and keep going through a function increasing by one until it reaches its destination. Something like:
function one(a) {
var x = a;
var max = 3;
if (a < 3) {
// some code
two(x);
} else {
// function will end here quitting the whole thing and possibly other code
}
}
function two(x) {
var change = x+1;
one(change);
}
It all works how I need it but when I first enter function one how would I make it so when x = a doesn't have a value that it will by default be 0?
something like...
function one(a) {
var x = a;
var max = 3;
if (x = undefined) {
x = 0;
} else {
if (x < 3) {
// some code
two(x);
} else {
// function will end here quitting the whole thing and possibly other code
}
}
}
function two(x) {
var change = x+1;
one(change);
}
Any ideas?
You could do this:
function one(a) {
var x = a || 0;
if (x < 3) {
//debugger;
two(x);
} else {
// function will end here quitting the whole thing and possibly other code
alert('Done');
}
}
function two(x) {
x++;
one(x);
}
one();
FIDDLE
var x = a || 0 means x is a if a can be asserted as true or 0.
x++ means x = x + 1
You can check to see if the variable is defined and send it in the functions argument by using the short hand conditional.
typeof(a)=="undefined" ? 0 : a;
You can change your code to:
function one(a) {
var x = (typeof(a)=="undefined" ? 0 : a);
var max = 3;
if (x < 3) {
// some code
two(x);
} else {
// function will end here quitting the whole thing and possibly other code
return;
}
}
Fiddle: http://jsfiddle.net/gBBL2/
var x = (typeof a === 'undefined') ? 0 : a;
If a is undefined, use 0. Otherwise use a as the value of x.

Categories

Resources