When I change values of front and back inside of frontAndBackExist function, the changes won't be reflected outside of frontAndBackExist function. Thus when I call isEmpty function after adding and removing nodes, it returns false even though there aren't any nodes left. Is this because front and back are primitive values?
type DequeNode = {
value: any;
prev?: DequeNode;
next?: DequeNode;
};
type FrontBackTypes = DequeNode | undefined;
function Deque() {
let front: FrontBackTypes = undefined;
let back: FrontBackTypes = undefined;
let cnt = 0;
function addFront(value: any) {
if (!front) {
front = back = { value };
cnt++;
return null;
}
cnt++; // a new node just pushed!
const tempFront = front;
front = { value, prev: tempFront };
tempFront.next = front;
return;
}
function removeFront() {
if (!front) {
return null;
}
const value = peekFront();
if (frontAndBackExist(front, back)) {
front = undefined;
back = undefined;
cnt--;
return value;
}
cnt--;
front = front.prev;
delete front!.next;
return value;
}
function peekFront() {
return front?.value;
}
function addBack(value: any) {
if (!front) {
const backVal = { value };
front = backVal;
back = backVal;
cnt++;
return null;
}
cnt++;
const tempBack = back;
back = { value, next: tempBack };
tempBack!.prev = back;
return;
}
function removeBack() {
if (!back) {
return null;
}
const value = peekBack();
if (frontAndBackExist(front, back)) {
front = undefined;
back = undefined;
cnt--;
return value;
}
cnt--;
back = back.next;
delete back!.prev;
return value;
}
function peekBack() {
return back?.value;
}
function size() {
return cnt;
}
function isEmpty() {
return cnt === 0;
}
function clear() {
front = undefined;
back = undefined;
cnt = 0;
}
function frontAndBackExist(front: FrontBackTypes, back: FrontBackTypes) {
return front === back;
// if (front === back) {
// // if you change front and back value here, it won't be reflected outside of this function. thus the last isEmpty call returns false even though there is no node in deque.
// front = undefined;
// back = undefined;
// return true;
// } else {
// return false;
// }
}
return {
addFront,
removeFront,
peekFront,
addBack,
removeBack,
peekBack,
size,
isEmpty,
clear,
};
}
const deque = Deque();
console.log(deque.peekFront()); // undefined
console.log(deque.isEmpty(), "return if deque is empty or not");
console.log(deque.size(), "return length of deque");
deque.addFront(1);
console.log(deque.peekBack()); // 1
deque.addFront(2);
console.log(deque.size(), "return length of deque");
deque.clear();
console.log(deque.size(), "return length of deque");
console.log(deque.removeBack()); // 1
console.log(deque.isEmpty(), "return if deque is empty or not");
deque.addFront(3);
deque.addFront(4);
console.log(deque.size(), "return length of deque");
console.log(deque.size());
console.log(deque.peekBack()); // 2
deque.addBack(5);
deque.addBack(6);
console.log(deque.peekBack()); // 6
console.log(deque.removeFront()); // 4
console.log(deque.removeFront()); // 3
console.log(deque.removeFront()); // 2
console.log(deque.size(), "return length of deque");
console.log(deque.removeFront()); // 5
console.log(deque.isEmpty(), "return if deque is empty or not");
console.log(deque.removeFront()); // 6
console.log(deque.isEmpty(), "return if deque is empty or not");
console.log(deque.removeFront()); // undefined
console.log(deque.isEmpty(), "return if deque is empty or not");
Change front and back values inside helper function but those changes won't be reflected outside of the helper function.
The problem with code like
function frontAndBackExist(front: FrontBackTypes, back: FrontBackTypes) {
if (front === back) {
front = undefined;
back = undefined;
return true;
} else {
return false;
}
}
is that front and back are parameters to the function, and are thus local variables inside the function body. So these variables only exist in the function scope.
When you assign front = undefined or back = undefined, all you are doing is assigning new values to these local variables, inside the function. It has no effect on whatever values were passed in as arguments to the function. JavaScript does not evaluate function calls by passing references to its arguments.
And the fact that you had some variables in an outer scope named front and back doesn't affect things; your function parameters are merely shadowing the same-named variables (see What is "Shadow Identifier Declaration" in JavaScript?) and therefore preventing your access of them inside the function.
JavaScript evaluates function calls by sharing, which is essentially the same as copying the value of the argument to the parameter, but with the wrinkle that objects are essentially references. When you call a function with a primitive argument, you are getting a copy of a primitive value. But when you call a function with an object as its argument, you are getting a copy of the object reference. So while reassigning the parameter's value has no external effects:
function foo(fb: FrontBackTypes) {
fb = { value: 1 };
}
const x = { value: 0 };
console.log(x.value) // 0
foo(x);
console.log(x.value) // 0, unchanged
you can have external effects if you don't reassign the parameter but instead act on its members:
function foo(fb: FrontBackTypes) {
if (fb) {
fb.value = 1;
}
}
const x = { value: 0 };
console.log(x.value) // 0
foo(x);
console.log(x.value) // 1, changed
So, one way you could fix your code is to refactor so front and back are members of an object and then pass that object as a function parameter. But this isn't how I'd recommend proceeding.
Instead, I'd suggest making frontAndBackExist() a closure with no parameters. You can access the external front and back variables inside such a closure because they were defined in an outer scope. That is, just stop shadowing front and back:
function frontAndBackExist() {
if (front === back) {
front = undefined;
back = undefined;
return true;
} else {
return false;
}
}
Now in the rest of your code, just call frontAndBackExist() with no arguments (and you don't have to write front = undefined; back = undefined; out there either):
function removeFront() {
if (!front) {
return null;
}
const value = peekFront();
if (frontAndBackExist()) {
cnt--;
return value;
}
cnt--;
front = front.prev;
delete front!.next;
return value;
}
Playground link to code
Related
I'm using straight Javascript (no JQuery or anything like that, please). I've implemented a class which wraps an array, thus:
class Ctrls
{
_items = new Array();
constructor()
{
this._items = new Array();
}
Add(oCtrl)
{
this._items.push( { key:oCtrl.Name, value:oCtrl } );
}
Clear()
{
this._items = new Array();
}
get Count()
{
return this._items.length;
}
get Item(index)
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
get Items()
{
return this._items; // in case we desperately need to
}
}
I get an error on page load, at get Item(index), which is Uncaught SyntaxError: Getter must not have any formal parameters. I come from C# world and am looking for an equivalent of:
public Ctrl Item(iIndex)
{
get
{
return _items[iIndex];
}
}
How do I index a getter in Javascript?
Edit(1): I've had suggestions to turn get Item into a function, but if I change the definition to this:
function GetItem(index) // returns Ctrl
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
I get this error on pageload: Uncaught SyntaxError: Unexpected identifier at the line function GetItem...
Edit(2): Modified the above to read:
GetItem(index) // returns Ctrl
{
// get the index'th item.
// If item is numeric, this is an index.
// If item is a string, this is a control name
if (Number.isInteger(index))
{
return this._items(index).value;
}
else
{
item = this._items.find(element => (element.value.Name == index));
return item;
}
}
as functions within classes do not use the function keyword, oddly. This now works. Thank all.
"you can't pass parameters to getters in JS". Theoretically: yes, you cannot do that. But practically: functions are first-class citizens in JS, so they can be arguments or return values of a function. You can do it like this:
class GetterWithParameter {
constructor() {
this.array = ["index 0", "index 1", "index 2"]
}
get itemAtIndex() {
return (idx) => this.array[idx]
}
}
const getterWithParameter = new GetterWithParameter()
const idx0 = getterWithParameter.itemAtIndex(0)
const idx1 = getterWithParameter.itemAtIndex(1)
const idx2 = getterWithParameter.itemAtIndex(2)
console.log("item at index 0:", idx0)
console.log("item at index 1:", idx1)
console.log("item at index 2:", idx2)
So, while the getter cannot have arguments, you can return a function that can receive an argument - and use that.
Of course, the usage seems identical to defining a function on the class that requires the same argument - but still, you are using a getter.
I'm learning JavaScript Proxy .
By using set trap i only want to add even number to Array. But every time throw
Uncaught TypeError: 'set' on proxy: trap returned falsish for property
'length'
Here is my code example.
//Only even numbers will be added here. We are using Js Proxy
let evenNumbers = [];
evenNumbers = new Proxy(evenNumbers, {
set(target, p, value, receiver) {
if ( (value % 2) === 0 ) {
target[p] = value;
return true
}else {
return false;
}
}
});
evenNumbers.push(2);
You neet to return true for both cases, because
Return value
The set() method should return a boolean value.
Return true to indicate that assignment succeeded.
If the set() method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
push calls the proxy twice, one by pushing the value and another times to change the length property. To avoid unnecessary more action whithin in this proxy, you could exit early with this unwanted property.
let evenNumbers = [];
evenNumbers = new Proxy(evenNumbers, {
set (target, p, value, receiver) {
if (p === 'length') return true; // exclude this property
if (value % 2 === 0) {
target[p] = value;
}
return true;
}
});
evenNumbers.push(2);
console.log(evenNumbers.length); // 1
evenNumbers.push(3);
console.log(evenNumbers.length); // Still 1
console.log(evenNumbers); // [ 2 ]
The issue is that the function .push() tries to update the length (evenNumbers.length = 1), and that's not being allowed by the trap.
Try this:
let evenNumbers = [];
evenNumbers = new Proxy(evenNumbers, {
set(target, p, value, receiver) {
if ( isFinite(p) ) {
// The key is a number (index), so there's code trying to add/change an element of the array.
if ( (value % 2) === 0 ) {
target[p] = value;
return true
}else {
return false;
}
} else {
return true;
}
}
});
evenNumbers.push(2); // OK
evenNumbers.push(3); // TypeError
I'm creating a function saveOutput that accepts a function, and a string. saveOutput will then return a function that behaves exactly like the passed-in function, except for when the password string is passed in as an argument. When this happens, the returned function will return an object with all previously passed-in arguments as keys, and the corresponding outputs as values.
I think my code below is correct but I run into the Range Error: Maxiumum call stack size exceeded when I run my code.
function saveOutput(inputFunc, string) {
let obj = {};
//inputFunc() accepts one args
//string like a pwd
return function inputFunc(input) {
if (input === string) {
return obj;
} else {
obj[input] = inputFunc(input);
return inputFunc(input);
}
}
//returns a fxn
return inputFunc;
}
//Test cases
const multiplyBy2 = function(num) { return num * 2; };
const multBy2AndLog = saveOutput(multiplyBy2, 'boo');
console.log(multBy2AndLog(2)); // should log: 4
console.log(multBy2AndLog(9)); // should log: 18
console.log(multBy2AndLog('boo')); // should log: { 2: 4, 9: 18 }
You're using the name inputFunc twice. The returned function is called inputFunc so it shadows the callback function passed as parameter. The returned function calls inputFunc which is itself and causes an endless recursion and eventually the "maxiumum call stack size exceeded" error is thrown.
To fix this either use a different name or make it anonymous as the name is not needed anyway, here is the working code with some improvements:
function saveOutput(inputFunc, string) {
let obj = {};
return function (input) { // make it anonymous
if (input === string) {
return obj;
}
// improvement 1: the else block can be omitted here
return obj[input] = inputFunc(input); // improvement 2: no need to call inputFunc twice, just assign and return at the same time
}
// the return statement here is never reached because there is a return right before it so just remove it
}
Read more about variable shadowing here: An example of variable shadowing in javascript
Demo:
function saveOutput(inputFunc, string) {
let obj = {};
return function(input) {
if (input === string) {
return obj;
}
return obj[input] = inputFunc(input);
}
}
const multiplyBy2 = function(num) {
return num * 2;
};
const multBy2AndLog = saveOutput(multiplyBy2, 'boo');
console.log(multBy2AndLog(2));
console.log(multBy2AndLog(9));
console.log(multBy2AndLog('boo'));
In the code below is an iterator:
const cart = ['Product 0','Product 1','Product 2','Product 3','Product 4','Product 5','Product 6','Product 7','Product 8','Product 9','Product 10']
function createIterator(cart) {
let i = 0;//(*)
return {
nextProduct: function() {
//i:0; (**)
let end = (i >= cart.length);
let value = !end ? cart[i++] : undefined;
return {
end: end,
value: value
};
}
};
}
const it = createIterator(cart);
First I know a copy of the present state of the function's variables and the parameters are parsed.(Right?)...
And when you run
const it = createIterator(cart);
Is a property below created?
//i:0 (**);
Making it.next(); equivalent to
{
i:0;//state or value of i from createIterator() definition;
next : function(cart){
let end = (this.i >= cart.length);
let value = !end ? cart[this.i++] : undefined;
return {
end: end,
value: value
};
}
Or does state of the value of i in line (*) from the first code, Is what's what is modified?
Please if this point is not clear... let me know to explain better.
Calling the iterator will create an instance of i scoped to the createIterator function. The object returned from it will only have access to that specific instance of i, but i is not a property of the object returned. It only can access it due to the function scope.
You can see a little better how this works if we break your code down a little more simply:
function createIterator(cart, display) {
let i = 0;
return {
next: function() {
i++;
console.log(display + ' next: ', i);
}
};
}
const cart1 = [];
const cart2 = [];
const it1 = createIterator(cart1, 'cart1');
it1.next();
it1.next();
const it2 = createIterator(cart2, 'cart2');
it2.next();
it2.next();
Each instance of the iterator has a different copy of i and only the object returned from the iterator function can access it.
I am trying to target a variable from a variable value that has been passed in from function arguments. I don't know how to do this. P.S. the variables at the top are also used by other functions
let cardvalue0 = false;
let cardvalue1 = false;
function myfunction (card) {
if (card === false) {
card = true;
//ether cardValue 0 or 1 should now be true
return card;
}
}
// exturnal html
<button onclick="myfunction("cardValue0")"></button>
<button onclick="myfunction("cardValue1")"></button>
new try by me
//define cards
let card0State = false;
let card1State = false;
//toggle card status (read or not)
function cardToggle(card) {
console.log(card);
card = !card
console.log(card);
return card;
}
// external html
<button onclick="myfunction(cardValue0)"></button>
<button onclick="myfunction(cardValue1)"></button>
target a variable from a variable value? If I understood you right you want to access a variable cardvalue0 inside the function myfunction.
You can access it using, eval(card) but definitely not advised to do
function myfunction (card) {
if (eval(card) === false) { //this will work
card = true; //however you cannot change the original value
return card;
}
}
You can get this by using objects
let cardDict = { "cardvalue0" : false,
"cardvalue1" : false}
function myfunction (card) {
if (cardDict[card] === false) {
cardDict[card] = true;
return cardDict[card];
}
}
Would it be possible to store card0State and card1State in an object instead? Then you could reference them as keys to the object instead.
const cardvalues = {};
cardvalues.cardvalue0 = false;
cardvalues.cardvalue1 = false;
function myfunction (cardNumber) {
if (cardvalues[cardNumber] === false) {
cardvalues[cardNumber] = true;
//ether cardValue 0 or 1 should now be true
return cardvalues[cardNumber];
}
}
// exturnal html
<button onclick="myfunction('cardvalue0')"></button>
<button onclick="myfunction('cardvalue1')"></button>
Then you aren't declaring a new variable in the function scope and you are directly altering the key values on the object. This is usually better than a variable variable pattern.
"Variable" variables in Javascript?
the simpler would be moving the original variable (cardvalue0 and cardvalue1) in a Map or an object, so you could have :
cards = {
cardvalue0 : false,
cardvalue1 : false
}
function foo(cardName) {
cards[cardName] = !cards[cardName];
return cards[cardName]
}
}
if you can not move cardvalue0 and cardvalue1, you could simply hardcode them, but it will become ugly really fast.
function foo(cardName) {
if(cardName === "cardvalue0") {
cardValue0 = !cardValue0;
return cardValue0;
} else if(cardName === "cardvalue1")
cardValue1 = !cardValue1;
return !cardValue1;
}
eval would be the only way to get a variable from a string representation, but is not a good idea.