Cannot update local variable - javascript

Have been trying to update a local variable (which is an array) via add/remove functions. I was able to add items using add function which also updated my local array but when I tried to remove the same using my code, it still returns my old array without modifications.
However, if I try to use pop() for removing an element, everything seems to work fine.
I know that filter from my remove function is returning the modified array but it's not getting updated in my array named mainArr.
Why is the array getting updated when I replace remove functionality with mainArr.pop() but not with my code.
The code also seems to work if I replace the assignment operator from my remove function to this.mainArr = //return value from the filter.
My remove function seems to be creating a new local variable with the same name mainArr due to which it is not updating my actual mainArr. Why is it doing so? Isn't it a concept of closures that my inner function can access global variables? What am I missing here?
function test() {
let mainArr = [];
function add(func) {
mainArr.push(func);
}
function remove(num) {
mainArr = mainArr.filter(item => item !== num)
}
return {
mainArr,
add,
remove
}
}
let val = test()
val.mainArr // returns []
val.add(3)
val.add(5)
val.mainArr //returns [3, 5]
val.remove(3)
console.log(val.mainArr) // still returns [3, 5]. Why?

mainArr.push(func); mutates the array.
mainArr.filter(item => item !== num) creates a new array.
let mainArr = []; is a variable which holds your original array. Later on, you assign the filtered version to the variable.
return { mainArr, add, remove } returns the value of mainArr which (at the time) is the original array. When you later change the value of the mainArr variable, the previously returned value is still the original array (no time travel is performed!).
Create the object upfront, and then always modify properties of that object. Don't create it from variables which later have their values changed.
function test() {
function add(func) {
data.mainArr.push(func);
}
function remove(num) {
data.mainArr = data.mainArr.filter(item => item !== num)
}
const data = {
mainArr: [],
add,
remove
};
return data;
}
let val = test()
console.log(val.mainArr);
val.add(3)
val.add(5)
console.log(val.mainArr)
val.remove(3)
console.log(val.mainArr)
In modern JS, this sort of thing is generally done with a class rather than a factory though.
class Test {
constructor() {
this.mainArr = [];
}
add(value) {
this.mainArr.push(value);
}
remove(value) {
this.mainArr = this.mainArr.filter(item => item !== value)
}
}
let val = new Test()
console.log(val.mainArr);
val.add(3)
val.add(5)
console.log(val.mainArr)
val.remove(3)
console.log(val.mainArr)

Related

How do higher-order functions like `map()` and `reduce()` receive their data?

I'm trying to write my own higher order function right now and I want to know how functions like map() and reduce() access the array they are being applied to. And not just for arrays either, but with any higher order function like toString() or toLowerCase().
array.map()
^^^ // How do I get this data when I am writing my own higher order function?
array.myOwnFunction(/* data??? */)
I hope this makes sense. I'm sure the answer is out there already, but I'm struggling to know what to search for to find the information.
You can add it to the Array prototype like :
Array.prototype.myOwnFunction = function() {
for (var i = 0; i < this.length; i++) {
this[i] += 1;
}
return this;
};
const array = [1, 2, 3];
const result = array.myOwnFunction();
console.log(result);
Check the polyfill for Array.prototype.map(), this line in particular:
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
var O = Object(this);
Simplifying, this is where the values are received.
The way this works is related to the prototype and to the "thisBinding".
When a function is instantiated using new, the properties of the prototype are attached to the new Function object. As well, an "execution context" is created to maintain the environment. The properties of the prototype are then accessible in the environment through the current instance's this binding using this.
So if you have a function you want to use the data from in an instance, you can use a prototype as well. For many reasons (memory use, readability, reusability, inheritance, polymorphism, etc.) this is the best way to go about doing it. Modifying standard implementations such as Array, Math, Date, etc. are generally discouraged (although there will always be exceptions).
function LineItem(description,price,quantity){
this.description = description;
this.price = price;
this.quantity = quantity;
}
LineItem.prototype.Total = function(){
return this.price * this.quantity;
};
var avocados = new LineItem("avocado",2.99,3);
console.log(avocados.Total());
The main thing to take away here is that the thisBinding allows access to the current instance's object through this. That is where the data access comes from. For example, in an array instance, this references the current array; in a date instance, this references the current date; in the above LineItem example, this references the current LineItem.
Thanks to the responses to this question I was able to write a higher order component that accepts a callback function as an argument. Here is the code as an example:
Array.prototype.myFunction = function (callback) {
const items = this
const newArray = []
for (let item of items) {
item = callback(item)
newArray.push(item)
}
return newArray
}
const array = [1, 2, 3, 4]
const result = array.myFunction((item => {
return item + 1
}))
console.log(result)

Is this an incorrect usage of JS array.find or a VSCode warning to be ignored?

I need to replace a particular object in an array with another object.
the approach I'm using is to use find on the original array and then overwrite that found value. As I understand this will change the original array. The approach seems to be successfully achieving what I require but I am getting the 'variable never read' warning in VScode.
Screenshot
Code
for (let newQuestionObj of questionWIthNumericCorrectNumericValue) {
let requiredOriginalQuestion = allPotentialAnswers.find(originalQuestion => {
return originalQuestion.originalQ === newQuestionObj.originalQ;
});
//change question to new value
requiredOriginalQuestion = newQuestionObj;
}
Are there any issue with this approach or should I ignore this warning?
the issue here is your scope. lets take it step by step:
for (let newQuestionObj of questionWIthNumericCorrectNumericValue) {
// defining the variable
let requiredOriginalQuestion = allPotentialAnswers.find(originalQuestion => {
return originalQuestion.originalQ === newQuestionObj.originalQ;
});
//useless find.... because you are going to kill the result of the find.
// reassign the variable to a new thing
requiredOriginalQuestion = newQuestionObj;
} // < -- here, requiredOriginalQuestion DIES.
so basically what is happening a simplier way is something like
for (let something from someArray){
let newvar = doExpensiveCalculation();
newvar = "--";
// and we didnt do anything with it, despite of reassigning to "--"
} <-- newvar doesnt exist anymore.
or more explicity like:
for (let something from someArray){
// lets keep doing nothing :D
}
so basically your warning says
requiredOriginalQuestion is declared but its value is never read.
this is happening because at the end of each loop, you did nothing to your let requiredOriginalQuestion. also you can check that it will dissapear doing something like:
for (let newQuestionObj of questionWIthNumericCorrectNumericValue) {
let requiredOriginalQuestion = allPotentialAnswers.find(originalQuestion => {
return originalQuestion.originalQ === newQuestionObj.originalQ;
});
requiredOriginalQuestion = newQuestionObj;
const stringified = JSON.stringify(requiredOriginalQuestion);
}
but then you will get something like:
stringified is declared but its value is never read.
FINALLY
if you wanted to override the value with the response from the find, this is what you needed:
for (let newQuestionObj of questionWIthNumericCorrectNumericValue) {
let requiredOriginalQuestion = allPotentialAnswers.find(originalQuestion => {
return originalQuestion.originalQ === newQuestionObj.originalQ;
});
newQuestionObj = requiredOriginalQuestion;
}
with this you wont have any warning ;)
When you get a value from an array, you are getting a reference to that object. So the result of your .find is a variable (called requiredOriginalQuestion) that "points" to the object it finds in the array (allPotentialAnswers).
When you assign newQuestionObj to that variable (requiredOriginalQuestion) you are telling the variable called requiredOriginalQuestion to stop "pointing" at the value you found in the array and instead "point" at newQuestionObj. The object in the array is not changed in any way.
To do what you are trying to do and replace the object in the array, you will need to make a change to the array itself. You can do this using findIndex.
const arr = [{ value: "one" }, { value: "two" }];
const newItem = { value: "Three" };
console.log(arr);
// find the index of the one you want to replace:
const indexToReplace = arr.findIndex(item => item.value === "two");
// Replace the item at that index with the new object
arr[indexToReplace] = newItem;
console.log(arr);
So a simple change to your code would look like this:
for (let newQuestionObj of questionWIthNumericCorrectNumericValue) {
let requiredOriginalQuestionIndex = allPotentialAnswers.findIndex(originalQuestion => {
return originalQuestion.originalQ === newQuestionObj.originalQ;
});
//change question to new value
allPotentialAnswers[requiredOriginalQuestionIndex] = newQuestionObj;
}
In terms of the warning you are seeing in VS code. (as I mentioned in my comment) VS code has picked up the fact that you have assigned a value to requiredOriginalQuestionand don't use that value before assigning it a new value. If you add any line of code that uses requiredOriginalQuestion (other that assigning a new value to it) that warning will go away. There is nothing wrong with your use of find there.
Hope this helps
The result of .find is never used since you override requiredOriginalQuestion with newQuestionObj
If you want to modify the element "requiredOriginalQuestion" you found in the array "allPotentialAnswers", you have to access that array and replace the value inside.
replace :
requiredOriginalQuestion = newQuestionObj;
by
allPotentialAnswers[allPotentialAnswers.indexOf(requiredOriginalQuestion)] = newQuestionObj

Variable assigned to array not updating when array does (JS)

I am assigning a variable, proxies, equal to an array. I am trying to add items to that array at a later point in time and then access that array in other files.
The problem is, the proxyHelper.proxies value is not updating to reflect the value of proxies variable.
After modifying the array, console.log(proxies) returns the modified array, but console.log(proxyHelper.proxies) returns a blank. I need to access the proxyHelper.proxies value in other files, so you can see why this is a problem.
I use similar code elsewhere and it works fine - what am I not seeing?
var proxies = [];
proxyHelper.proxies = proxies;
proxyHelper.tester = function(win) {
electron.ipcMain.on('saveProxies', function(event, data) {
// Clear the previous proxies from list
proxies = [];
// Split proxies based on line breaks
if (data != '') {
let proxiesList = data.split('\n');
// i<= because we want to run a check to see if all proxies are added
for (let i = 0; i <= proxiesList.length; i++) {
// if the for loop hasn't ran through all proxies
if (i + 1 <= proxiesList.length) {
proxies.push(proxiesList[i]);
}
// Once it's loop through all proxies
else {
//Returns nothing
console.log(proxyHelper.proxies);
//Returns array with added items
console.log(proxies);
win.webContents.send('goodProxies', 'Saved!');
}
}
} else {
win.webContents.send('emptyProxies', 'Empty.');
}
})
}
proxies = [];
You just assigned a new array to this variable.
proxyHelper.proxies still points to the previous value, and is not affected.
You should always use a single variable, or mutate it instead of re-assigning it.
Here is what is going on in your code:
var proxies = []; // This new variable ("proxies") contains a reference to an empty array.
proxyHelper.proxies = proxies; // You assign the same reference to a property
// (named "proxies") of the "proxyHelper"
// object. The "proxies" property points
// to the same empty array as the variable above.
proxyHelper.tester = function(win) {
electron.ipcMain.on('saveProxies', function(event, data) {
proxies = []; // You assign a new reference to the global variable
// "proxies", which points to a new empty array.
// At this point the reference assigned to
// "proxyHelper.proxies" is still the original
// empty array from line 1
// ...
}
// ...
}
So when you access "proxyHelper.proxies", you are always accessing the original empty array the has never been modified...
What you should do is:
proxyHelper.proxies = []; // resets the object's property to a new empty array
Don't need proxy variable, as its changes aren't reflected to proxyHelper.proxies
Just use proxyHelper.proxies itself
Also i've cleaned up your code a little bit
There are plenty of Array methods you can use, instead of for loop
proxyHelper.proxies = []
proxyHelper.tester = function(win) {
electron.ipcMain.on('saveProxies', function(event, data) {
//Split proxies based on line breaks
if (data != '') {
let proxiesList = data.split('\n');
proxiesList.forEach(function(proxy) {
proxyHelper.proxies.push(proxy)
})
win.webContents.send('goodProxies', 'Saved!');
} else {
win.webContents.send('emptyProxies', 'Empty.');
}
})
}

Is it resource-heavy to create a new array literal inside a for loop in javascript?

I have always had this question in mind. Does it use many resources if I create an array literal in a function and return it, and use this function in a for loop?
For example, I have:
function getArray(in) {
const array = [];
in.forEach(element => {
if (element.value === 'hello') {
array.push(element);
}
});
return array;
}
for (const value of outerArray) {
const array = getArray(value.nestedArray);
array.forEach(element => { console.log('world'); });
}
Here the outerArray contains a group of values. Each value has a nestedArray. In this example I am creating a new array literal inside the loop. And assume that outerArray has 9999 values and I use this loop 10 times a second. Would that use insane amount of memory? Would it be better if I create a new array in the value of outerArray and directly manipulate the array in that function? Like this:
class Value {
constructor() {
...
this.displayArray = [];
}
...
}
function getArray(value) {
value.nestedArray.forEach(element => {
if (element.value === 'hello') {
value.displayArray.push(element);
}
});
return array;
}
for (const value of outerArray) {
getArray(value);
value.displayArray.forEach(element => { console.log('world'); });
}
Does it use many resources if I create an array literal in a function and return it, and use this function in a for loop?
No, the new arrays should be garbage-collected regularly. So yes, of course it does use some memory, but not an "insane amount". However, how much "many" is and whether it is too much, you need to try out. Write a version of the code that doesn't create the arrays - if that is possible - and profile their memory usages.
function forEachHello(arr) {
for (const element of arr) {
if (element.value === 'hello') {
callback(element);
}
}
}
for (const value of outerArray) {
forEachHello(value.nestedArray, element => { console.log('world'); });
}
Would it be better if I create a new array in the value of outerArray and directly manipulate the array in that function?
That uses even more memory, given all of the arrays exist all of the time, but it puts less pressure on the garbage collector of course. Make sure to empty the arrays when you don't need their contents any more. It might be more efficient overall, but the only way to find out is a benchmark.

dynamic array slicing to limit the length, when the limit is unset

I have a class
function Thing (n) {
this.limit = n // null or int n
this.table = [1, 2, 3, 4]
}
I want to do this for aesthetics
Thing.prototype.test = function () {
let that = this;
return this.table
.slice(0, that.limit) // this line
.map() // etc
}
since the above is simplified code and the actual table is an instance of another class and has another instance method and a map() called on it, before the slice().
So I was wondering if there was a way to force slice to slice to Thing.limit if that existed, and else slice and return the whole array. (Before the last map() the array is managably small). Something like
Thing.prototype.test = function () {
let that = this;
return this.table
.slice(0, (that.limit == null) ? this.length : that.limit)
.map() // etc
}
This doesn't work, I get an error
ReferenceError: that is not defined
As a side question, what is the this context inside a slice()? Is this a dumb question to be asking?
Note this.table.slice(0, -1) => [1, 2, 3] and this.table.slice(0, 0) => [].
I'm amenable to changing how Thing.limit is saved and also this is mostly cause now I am curious, as I have already changed the function to slice only if this.plan.limit exists && > this.table.length.
The code to instantiate the class is
const gadget = new Gadget();
const query = new Thing(gadget);
const result = query.get();
Possibly relevant: in the real code, inside query.get(), there's another function called getRowIds that gets called before the slice(), and then a very basic map().
the error occurs at the slice() line
Using undefined with slice's second parameter will give you the desired behavior. If you accept 0 as a limit for some reason, if having an empty table is a thing, then you can use this.
function Thing (n) {
this.limit = n; // undefined or int
this.table = [1, 2, 3, 4]
}
Thing.prototype.test = function () {
return this.table
.slice(0, this.limit)
.map() // etc
}
Now careful with the instanciation, I see you pass an instance of Gadget and not a number to the Thing constructor, so this may have a different behavior than what you expect.

Categories

Resources