boolean does not change in function JavaScript - javascript

I have the following code:
const ships = (length) => {
isSunk = false
console.log('BEFORE: ' + isSunk)
const shipSize = length
let hits = []
const hitLocation = location => {
hits.push(location);
isSunk = true;
console.log('INSIDE: ' + isSunk)
}
console.log('AFTER: ' + isSunk)
return {
isSunk,
hitLocation,
hits,
shipSize
}
}
const two = ships(2)
two.hitLocation(1)
two.hitLocation(2)
console.log('FINAL: ' + two.isSunk)
console.log('HITS: ' + two.hits)
Is there a reason why isSunk is not saved as true when I call it in the end?
Is it due to the nested functions?

When you set the boolean into the object, it is that value. It is not a reference to the variable. So when you change the variable, the value you saved inside of the object that you returned is not updated.
You can use a function to get the value
const ships = (length) => {
isSunk = false
console.log('BEFORE: ' + isSunk)
const shipSize = length
let hits = []
const hitLocation = location => {
hits.push(location);
isSunk = true;
console.log('INSIDE: ' + isSunk)
}
console.log('AFTER: ' + isSunk)
return {
isSunk: () => isSunk,
hitLocation,
hits,
shipSize
}
}
const two = ships(2)
two.hitLocation(1)
two.hitLocation(2)
console.log('FINAL: ' + two.isSunk())
Other option is to use a class.
class Ship {
hits = [];
constructor(length) {
this.shipSize = length;
}
get isSunk() {
return this.hits.length === this.shipSize;
}
hitLocation (location) {
this.hits.push(location);
console.log('INSIDE: ' + this.isSunk)
}
}
const two = new Ship(2)
two.hitLocation(1)
two.hitLocation(2)
console.log('FINAL: ' + two.isSunk)

The reason this happens is that isSunk is just a local variable and a value in contrast to hits which is also a local variable but an array and thus just a reference not a value.
As you return the object when calling ship() these values and the reference get returned in an object.
Now when you call hitLocation() it uses the local variable hits to add an entry to the array and as the array is just a reference the local variable hits and the hits in the two object have the same reference so they point to the same memory location, thus the update can be seen using the returned object.
On the other hand hitLocation() also modifies the local variable isSunk but as you do not return that and it is not a reference the object stored in variable two is not updated.
The best way to fix this to my mind is using a class instead of a object here. This will be much more concise and clear than returning and/or passing the object you want to perform some action on to a function all the time.

Related

Why the Code After Array.foreach doesn't execute?

I am trying to get value of a global variable (screenDisplay) after completing the for each portion.
But it seems whenever the 2nd Part is put before the 1st Part, the value that i am supposed to get from screenDisplay comes undefined and the console throws an error.
*error
TypeError: Cannot read properties of undefined (reading 'toFixed')
Why does the screenDisplay's value gets affected when part 2 is kept after part 1?
To find the error directly input values to the screen without using calculator key. ex. (2+3)
Live Link Here
const screenDisplay = document.getElementById("screen-input-selector");
const result = () => {
focusInitiate();
try {
**//1st Part**
// * auto multiplication feature
displaylocalStorageDATA();
const screenItems = [...screenDisplay.value];
let screenPosition = screenDisplay.selectionStart;
screenItems.forEach((item, index) => {
let indexno = index + 1;
if (
screenItems.length != indexno &&
![")", "+", "-", "*", "/"].includes(screenItems[index + 1]) &&
item == ")"
) {
let remainingDataFirstPortion = screenItems.slice(0, index + 1); //selects and stores the rest of the portion of the text after cursor
let remainingDataLastPortion = screenItems.slice(
index + 1,
screenItems.length
);
const clearedArray = remainingDataFirstPortion.concat("*");
const clearedArray1 = clearedArray.concat(remainingDataLastPortion);
screenDisplay.value = clearedArray1.join("");
displaylocalStorageSTORE();
screenDisplay.setSelectionRange(screenPosition, screenPosition);
}
});
//2nd Part
const inputData = localStorage.getItem("display");
if (inputData != null) {
//when inputdata and screendisplay value are same
if (inputData == screenDisplay.value) {
//when local storage has some value
displaylocalStorageREMOVE();
// screenDisplay.value = eval(localStorage.getItem("display"));
screenDisplay.value = Function("return " + inputData)().toFixed(4); //using function constructor instead of EVAL function cause EVAL() executes the code it's passed with the privileges of the caller
displaylocalStorageSTORE();
} else {
//when inputdata and screendisplay value are not same
displaylocalStorageREMOVE();
screenDisplay.value = Function(
"return " + screenDisplay.value
)().toFixed(4);
displaylocalStorageSTORE();
}
} else {
//when local storage is empty
screenDisplay.value = Function("return " + screenDisplay.value)().toFixed(
4
);
displaylocalStorageSTORE();
}
} catch (error) {
console.log(error);
};
Problem Resolved.
displaylocalStorageDATA() (which reassigns locally saved data to the global variable) of Part 1 was causing the problem. It was redefining the screenDisplay.value.
Removing that line has solved the problem.
Thanks, Everyone.

JavaScript - Issues recovering a map in an object after being saved in localStorage

I've been dealing with this for some time. I've a list of sections in which the user checks some checkboxes and that is sent to the server via AJAX. However, since the user can return to previous sections, I'm using some objects of mine to store some things the user has done (if he/she already finished working in that section, which checkboxes checked, etc). I'm doing this to not overload the database and only send new requests to store information if the user effectively changes a previous checkbox, not if he just starts clicking "Save" randomly. I'm using objects to see the sections of the page, and storing the previous state of the checkboxes in a Map. Here's my "supervisor":
function Supervisor(id) {
this.id = id;
this.verif = null;
this.selections = new Map();
var children = $("#ContentPlaceHolder1_checkboxes_div_" + id).children().length;
for (var i = 0; i < children; i++) {
if (i % 2 == 0) {
var checkbox = $("#ContentPlaceHolder1_checkboxes_div_" + id).children()[i];
var idCheck = checkbox.id.split("_")[2];
this.selections.set(idCheck, false);
}
}
console.log("Length " + this.selections.size);
this.change = false;
}
The console.log gives me the expected output, so I assume my Map is created and initialized correctly. Since the session of the user can expire before he finishes his work, or he can close his browser by accident, I'm storing this object using local storage, so I can change the page accordingly to what he has done should anything happen. Here are my functions:
function setObj(id, supervisor) {
localStorage.setItem(id, JSON.stringify(supervisor));
}
function getObj(key) {
var supervisor = JSON.parse(localStorage.getItem(key));
return supervisor;
}
So, I'm trying to add to the record whenever an user clicks in a checkbox. And this is where the problem happens. Here's the function:
function checkboxClicked(idCbx) {
var idSection = $("#ContentPlaceHolder1_hdnActualField").val();
var supervisor = getObj(idSection);
console.log(typeof (supervisor)); //Returns object, everythings fine
console.log(typeof (supervisor.change)); //Returns boolean
supervisor.change = true;
var idCheck = idCbx.split("_")[2]; //I just want a part of the name
console.log(typeof(supervisor.selections)); //Prints object
console.log("Length " + supervisor.selections.size); //Undefined!
supervisor.selections.set(idCheck, true); //Error! Note: The true is just for testing purposes
setObj(idSection, supervisor);
}
What am I doing wrong? Thanks!
Please look at this example, I removed the jquery id discovery for clarity. You'll need to adapt this to meet your needs but it should get you mostly there.
const mapToJSON = (map) => [...map];
const mapFromJSON = (json) => new Map(json);
function Supervisor(id) {
this.id = id;
this.verif = null;
this.selections = new Map();
this.change = false;
this.selections.set('blah', 'hello');
}
Supervisor.from = function (data) {
const id = data.id;
const supervisor = new Supervisor(id);
supervisor.verif = data.verif;
supervisor.selections = new Map(data.selections);
return supervisor;
};
Supervisor.prototype.toJSON = function() {
return {
id: this.id,
verif: this.verif,
selections: mapToJSON(this.selections)
}
}
const expected = new Supervisor(1);
console.log(expected);
const json = JSON.stringify(expected);
const actual = Supervisor.from(JSON.parse(json));
console.log(actual);
If you cant use the spread operation in 'mapToJSON' you could loop and push.
const mapToJSON = (map) => {
const result = [];
for (let entry of map.entries()) {
result.push(entry);
}
return result;
}
Really the only thing id change is have the constructor do less, just accept values, assign with minimal fiddling, and have a factory query the dom and populate the constructor with values. Maybe something like fromDOM() or something. This will make Supervisor more flexible and easier to test.
function Supervisor(options) {
this.id = options.id;
this.verif = null;
this.selections = options.selections || new Map();
this.change = false;
}
Supervisor.fromDOM = function(id) {
const selections = new Map();
const children = $("#ContentPlaceHolder1_checkboxes_div_" + id).children();
for (var i = 0; i < children.length; i++) {
if (i % 2 == 0) {
var checkbox = children[i];
var idCheck = checkbox.id.split("_")[2];
selections.set(idCheck, false);
}
}
return new Supervisor({ id: id, selections: selections });
};
console.log(Supervisor.fromDOM(2));
You can keep going and have another method that tries to parse a Supervisor from localStorageand default to the dom based factory if the localStorage one returns null.

Unhandled rejection SequelizeDatabaseError when passing dynamic query in where clause

I am trying to decompose a user's get request and put it in a variable named query. then pass the var query into the sequelize's findAll method using it's where clause, it seems like Sequelize thinks i am looking for a table CALLED query when in reality i am trying to pass the object. I'm sorry if i can not explain very well, but here is the code and the error:
var info = [];
//link example: localhost:8081/filter/?descripiton=san+francisco&houseType=house&numOfBedroom=3&numOfBathroom=2&houseSize=500&price=1200
exports.filterListings = function(req) {
//create an object literal which we will return, and has a nested object named filteredList inside.
//filteredList contains an array named listings where we will put listings that match our filter inside
let response = {
filteredList: {listings: []},
};
//now we need to see how the user wants us to filter the listings
const query = req.query;
//do some logic where we decompose query
if(query.descripiton != undefined) {
//info = info + 'descripiton: ' + query.descripiton+', ';
info.push('descripiton: ' + query.descripiton+', ');
console.log(info);
}
if(query.houseType != undefined) {
//info = info + 'houseType: ' + query.houseType+', ';
info.push('houseType: ' + query.houseType+', ');
//console.log(info);
}
if(query.numOfBedroom != undefined) {
//info = info + 'numOfBedroom: ' + query.numOfBedroom+', ';
info.push('numOfBedroom: ' + query.numOfBedroom+', ');
}
if(query.numOfBathroom != undefined) {
//info = info + 'numOfBathroom: ' + query.numOfBathroom+', ';
info.push('numOfBathroom: ' + query.numOfBathroom+', ');
}
if(query.houseSize != undefined) {
//info = info + 'houseSize: ' + query.houseSize+', ';
info.push('houseSize: ' + query.houseSize+', ');
}
if(query.price != undefined) {
//info = info + 'price: ' + query.price;
info.push('price: ' + query.price);
}
and then when i try to pass the info variable
listingModel.findAll({
//error because it wont recognize the variable search nor will it recognize info
where: {info}
}).then(listings => {
// so we loop through listings and insert what we have found into the response (which we are going to return)
for(var i = 0; i < listings.length; i++) {
response.filteredList.listings.push(listings[i]);
}; // loop where we insert data into response done
I want it to find all listings based on the dynamic query but i am getting the error:
Unhandled rejection SequelizeDatabaseError: Unknown column 'Listing.info' in 'where clause'
Thank you very much for the potential help!
Let's try to sort through your problems one by one. Sorry for the pun :p
Instead of using multiple if for creating your filtered list. Use for ... in. Then use that array of objects along with Sequelize.Op to create your query.
Example:
const Op = require('sequelize').Op;
const whereClause = [];
const query = req.query;
for(const key in query) {
if(query[key] !== '' && query[key] !== null) {
//object will be pushed to the array like "houseType:big"
whereClause.push({key:query[key]})
}
}
//you now have the where clause
//use it in your query with Op.and
listingModel.findAll({
where: {
[Op.and]: whereClause,
}
});
More info about querying with Sequelize - Operators

Variables Not Being Retained

Alrighty.
My problem is with the Javascript Objects itemList and priceList - they are not retained properly into the .then() section.
Here's the part that I'm having trouble with:
var stmt = `SELECT * FROM SRV${msg.guild.id}`;
var itemList = {};
var priceList = {};
var num = -1;
sql.each(stmt, function(err, row) {
num++
if(row.items!="ITEMS") { itemList[num] = row.items; }
if(row.prices!="PRICES") { priceList[num] = row.prices; }
console.log("---------\n" + JSON.stringify(itemList, null, 4) + "\n" + JSON.stringify(priceList, null, 4));
})
.then((itemList, priceList) => {
console.log("----E----\n" + JSON.stringify(itemList, null, 4) + "\n" + JSON.stringify(priceList, null, 4) + "\n----E----");
let cmd = require("./itemsPROCESS.js");
cmd.run(transfer, msg, args, itemList, priceList);
});
And here's the important bit that it outputs:
I've tried using strings instead of Javascript Objects, but it doesn't seem to like that either, and it outputs the same.
The 5 and undefined part should resemble the bit above it, but it doesn't.
Might it have something to do with the null parts? If so, will that mean it won't work with true - false - undefined etc?
remove the arguments itemList and priceList and your code should just be fine.
The itemList you are referring inside the .then function is actually what is resolved by the SQL promise.
.then(() => {...});
You are using itemList and priceList of global scope so don't redefine them in local scope of then. Just remove those arguments from then
.then(() => {
console.log("----E----\n" + JSON.stringify(itemList, null, 4) + "\n" + JSON.stringify(priceList, null, 4) + "\n----E----");
let cmd = require("./itemsPROCESS.js");
cmd.run(transfer, msg, args, itemList, priceList);
});

Writing/adjusting object from within callback

I'm working on a meteor react project and want to use load data from local storage which happens async. Unfortunately I wasn't able to get the data out of callback even with binds. I tried multiple ways but could not get any of them to work, surely I'm missing something.
I stripped as much away as I could but had to keep some for the context.
From my understanding it can only be related to the track object as setting those simple Integers, Booleans works fine.
render() {
const { audioCollection } = this.props; // database collection, async hence the following if check
if (audioCollection) {
this.tracks = audioCollection.audio(); // setting tracks for later use
// make sure tracks are loaded and only run once, as we do this in the react renderer
if (this.tracks && !this.state.tracksLoaded) {
var trackLoadedCount = 0;
this.tracks.forEach((track, i) => { // I tried to use forEach and map here
// now we try to load the data from local storage and if it fails fall back to the remote server
LocalForage.getItem(track.file_id).then(function(err, file) {
if (!err && file) {
console.log(track.file_id + ' from cache')
var blob = new Blob([file]);
fileURI = window.URL.createObjectURL(blob);
} else {
console.log(track.file_id + ' from database')
fileURI = audioCollection.audioLink(track.file_id);
}
track.fileURI = fileURI; // assigning the retrieved data uri to the track object, also tried to set it on the original parent object not the extracted one from the forEach/map
console.log(fileURI + ' ' + track.fileURI) // yes, both are set
trackLoadedCount++; // increasing the tracks loaded count, to calculate if all have been loaded and to test if it can write outside of the callback, which it does
// if all files have been processed, set state loaded, this works too.
if (trackLoadedCount == this.tracks.length) {
this.setState({
tracksLoaded: true,
})
}
}.bind(track, this))
});
}
}
// once all has been processed we try to retrieve the fileURI, but it isn't set :(
if (audioCollection && this.tracks && this.state.tracksLoaded) {
console.log('all loaded ' + this.tracks.length)
this.tracks.map(track => {
console.log('track: ' + track.file_id + ' ' + track.fileURI) // file_id is set, fileURI is not
})
}
// we only log
return (
<Content {...this.props}>
<div>just console output</div>
</Content>
);
}
I tried more approaches:
Writing the tracks as an array to state like tracksLoaded (didn't work work)
Defining a new var before the async call and setting its values from within the callback, like trackLoadedCount (with and without bind) (doesn't work)
Why isn't this working while its working for tracksLoaded and trackLoadedCount?
Update regarding Firice Nguyen Answer
render() {
const { audioCollection } = this.props;
if (audioCollection) {
this.tracks = audioCollection.audio();
if (this.tracks && !this.state.tracksLoaded) {
var trackLoadedCount = 0;
this.tracks.forEach((track, i, trackArray) => {
LocalForage.getItem(track.file_id).then(function(err, file) {
if (!err && file) {
console.log(track.file_id + ' from cache')
var blob = new Blob([file]);
fileURI = window.URL.createObjectURL(blob);
} else {
console.log(track.file_id + ' from database')
fileURI = audioCollection.audioLink(track.file_id);
}
track.fileURI = fileURI;
console.log('1. ' + track.file_id + ' ' + track.fileURI);
trackArray[i] = track;
console.log('2. ' + track.file_id + ' ' + trackArray[i].fileURI);
trackLoadedCount++;
if (trackLoadedCount == this.tracks.length) {
this.setState({
tracksLoaded: true,
})
}
}.bind(track, this))
});
}
}
if (audioCollection && this.tracks && this.state.tracksLoaded) {
console.log('all loaded ' + this.tracks.length)
this.tracks.map(track => {
console.log('3. ' + track.file_id + ' ' + track.fileURI) // file_id is set, fileURI is not
})
}
return (
<Content {...this.props}>
<div>just console output</div>
</Content>
);
}
returns
MXqniBNnq4zCfZz5Q from database
1. http://localhost:3000/cdn/storage/files/MXqniBNnq4zCfZz5Q/original/MXqniBNnq4zCfZz5Q.m4a
2. http://localhost:3000/cdn/storage/files/MXqniBNnq4zCfZz5Q/original/MXqniBNnq4zCfZz5Q.m4a
keBWP6xb9PyEJhEzo from database
1. http://localhost:3000/cdn/storage/files/keBWP6xb9PyEJhEzo/original/keBWP6xb9PyEJhEzo.m4a
2. http://localhost:3000/cdn/storage/files/keBWP6xb9PyEJhEzo/original/keBWP6xb9PyEJhEzo.m4a
K2J2W9W26DDBNoCcg from database
1. http://localhost:3000/cdn/storage/files/K2J2W9W26DDBNoCcg/original/K2J2W9W26DDBNoCcg.m4a
2. http://localhost:3000/cdn/storage/files/K2J2W9W26DDBNoCcg/original/K2J2W9W26DDBNoCcg.m4a
all loaded 3
3. MXqniBNnq4zCfZz5Q undefined
3. keBWP6xb9PyEJhEzo undefined
3. K2J2W9W26DDBNoCcg undefined
hence the issue persists.
The forEach give out a copy of the element. The track is just a copy, not the original one. It does not point to the element in your array. You can try this:
this.tracks.forEach(track, i, trackArray) => {
// change `track` value
...
trackArray[i] = track;
});
Or try map method

Categories

Resources