Is it possible to "overwrite a pointer" in JavaScript? - javascript

Is it possible to emulate the code below, in JavaScript?
var ref = new Reference();
var arr = [1, 2, ref];
ref.overwrite(3);
console.log(arr); // output: [1,2,3]
This code places a ref inside an array, then overwrites it, completely replacing its occurrence by 3. We could do something similar by storing the parent object and the index:
var arr = [1, 2, null];
var ref = {object: arr, index: 2};
ref.object[ref.index] = 3;
console.log(arr); // output: [1, 2, 3]
But this is not the same thing, since ref must keep track of all the parent objects where it is used. I'm interested in using ref as a placeholder, storing it in multiple places and then replacing all occurrences by something else, without keeping track of where it was used. We could also "almost" do this as such:
var ref = {value: null};
var arr = [1, 2, ref];
ref.value = 3;
console.log(arr); // output: [1, 2, {value: 3}]
But this doesn't completely get rid of the wrapping object.

No, JavaScript does not have references or pointers as values. You always need some wrapper object (which you can mutate however you want, but not replace), it's not possible to create a custom primitive value.

There is no way to really do what you want to do, but depending on how you are using it, you can fake it. You can use valueOf to return what you want. Example here shows using it to sum up an array of numbers and than altering it and running it again.
const sum = arr => arr.reduce((t, i) => t + i, 0)
function MyRef(n) {
this.number = n;
this.overWrite = n => this.number = n
}
MyRef.prototype.valueOf = function() {
return this.number;
};
const ref = new MyRef(100);
const myArray = [1, 2, ref]
console.log(sum(myArray))
ref.overWrite(3)
console.log(sum(myArray))

Related

How to change position of items within an array?

I am trying to create a function that changes positions of the items inside an array.
For example, I have this array:
[1, 2, 3] - if I call the function I get this => [2, 3, 1];
If I call the function again I should have this => [3, 1, 2];
The next time it should move again and so on.
But instead, if I call the function a second time I get this again => [2, 3, 1];
How could I make this work properly?
I know why this is happening, every time I call the function it receives the same values in the same order, I get that, that's why I built another array (move) to receive the values in the current order, it was an attempt to use this new array the second time I call the function, I tried it using an if/else statemente, but it didn't work; I also built another function passing 'move' as a parameter, and it worked, but just doesn't make any sense I have to have a different function for every move.
p.s. I am receiving the values from a HTML input.
<input id="numero1" value="">
<button onclick="moveLeft()">Move</button>
var n1 = document.getElementById("numero1");
function moveLeft() {
var str = n1.value;
var arr = str.split('');
var move = [];
console.log(arr);
arr.splice(0, 3, arr[1], arr[2], arr[0]);
console.log(arr);
for (var i = 0; i < arr.length; i++) {
move.push(arr[i]);
}
console.log(move);
teste(move);
}
function teste(move) {
console.log(move);
move.splice(0, 3, move[1], move[2], move[0]);
console.log(move);
}
If I understood properly, I think you can simplify in one line function:
const moveLeft = (arr) => arr.push(a.shift());
Notice that this function would mutate the content of the array but I guess that what you want:
const array = [1, 2, 3];
moveLeft(array);
console.log(array)// [2, 3, 1]
moveLeft(array);
console.log(array)// [3, 1, 2]
moveLeft(array);
console.log(array)// [1, 2, 3]
Basically with push we add a new element at the end of the array, and the new element we add, is the return value of the method shift, that removes the first element from an array and return it.
Edit
If you don't want to mutate the original array, the fastest way is create a shallow copy and pass that to the function:
const newArray = array.slice();
moveLeft(newArray);
console.log(newArray, array) // [2, 1, 3] [1, 2, 3]
However, a possible implementation that always return a new array, could be:
const moveLeft = (arr) => [...arr.slice(1), arr[0]]
Just for future reference. But keep in mind that here you're create a shallow copy, (so one new array) and returns a new array from the spread operator, so it's less ideal. It could be useful only if you're writing an API that never allow mutability.
Edit 2
As per comment, the input's value should reflect the new order, so, given the moveLeft function written before (the first one, that mutates the content):
<input id="numero1" value="">
<button onclick="inputMoveLeft('numero1')">Move</button>
And the JS:
function inputMoveLeft(id) {
const input = document.getElementById(id);
input.value = moveLeft(input.value.split("")).join("");
}
It should give you the result you were looking for.
pop and shift sound like the thing you need:
function rotateLeft(array){
let first = array.shift()
array.push(first); // add to end
return array;
}
function rotateRight(array){
let last = array.pop()
array.unshift(last); // add to front
return array;
}

Why do map, every, and other array functions skip empty values?

Background
I was writing some code to check if 2 arrays where the same but for some reason the result was true when expecting false. On closer inspection I found that where array values where undefined they were skipped.
Example
const arr1 = [, , 3, 4]
const arr2 = [1, 2, 3, 4]
const result = arr1.every((item, index) => item === arr2[index])
console.log(result) // true (HOW????)
What I've tried
So I spent some time trying to get the value in here correctly but the only thing I've come up with is a regular for loop that makes iterations based on array length not the actual items.
Question
Why does this happen and is there a way to recognise these empty/undefined values in my array?
It's an extension of the fact that forEach only visits elements that actually exist. I don't know that there's a deeper "why" for that other than that it didn't make much sense to call the callback for a missing element.
You can realize those elements (if that's the world) by using:
Spread notation, or
Array.from, or
Array.prototype.values, or
Array.prototype.entries
...or possibly some others.
const a = [, , 3];
console.log(a.hasOwnProperty(0)); // false
const b = [...a];
console.log(b.hasOwnProperty(0)); // true
const c = Array.from(a);
console.log(b.hasOwnProperty(0)); // true
Applying that to your function with Array.from:
const arr1 = [, , 3, 4]
const arr2 = [1, 2, 3, 4]
const result = Array.from(arr1).every((item, index) => item === arr2[index])
console.log(result) // false
Of course, that involves creating a new array and looping through the previous one copying over the elements. You might be better off with your own for loop.
Applying Array.prototype.entries to your function:
const arr1 = [, , 3, 4]
const arr2 = [1, 2, 3, 4]
let result = true;
for (const [index, value] of arr1.entries()) {
if (value !== arr2[index]) {
result = false;
break;
}
}
console.log(result) // false
Because the language design says so. 🤷🏻‍♂️
See the specification which says:
Repeat, while k < len
Let Pk be ToString(k).
Let kPresent be HasProperty(O, Pk).
ReturnIfAbrupt(kPresent).
If kPresent is true, then
… then do the operation.
Since a value was never assigned to the 0 and 1 properties, the HasProperty test gives false so they are skipped over by the If rule.
By docs of .every():
callback is invoked only
for indexes of the array which have assigned values; it is not invoked
for indexes which have been deleted or which have never been assigned
values.
So, you are calling .every() with just truthy values of array1:
const arr1 = [, , 3, 4]
arr1.every((x, idx) => {
console.log(`element: ${x}`, `index: ${idx}`);
return true;
})
The built in iteration functions (as described by others and defined in the specs) will skip values when HasProperty is false.
You could create your own shim for all which would check each value. This would be an expansion of the prototype. Alternatively, turning it into a function if this code were to be used in a wider scope would be a better design and require a slightly different call.
const arr1 = [, , 3, 4];
const arr2 = [1, 2, 3, 4];
Array.prototype.all = function(callback){
for(let i = 0; i < this.length; i++){
if(callback(this[i],i,this)) return false;
}
return true;
};
const result2 = arr1.all((item, index) => item === arr2[index]);
console.log(result2); // false

JavaScript array passed as ref but can't assign ref to a new array?

If I pass an array into a function and make changes to the array within the function, the array that exists outside the function reflects those effects. E.g.:
var myArr = [1, 2, 3];
function popAll(arr) {
while(arr.length) {
arr.pop();
}
}
popAll(myArr);
console.log(myArr.length); // prints "0"
However, if I try to reassign the array reference to point to another array in the function, it does not stick:
var myArr = [1, 2, 3];
function reassign(arr) {
while(arr.length) {
arr.pop();
}
var newArr = [1,2,3,4,5];
arr = newArr;
}
reassign(myArr);
console.log(myArr.length); // still prints "0"!!
What am I missing? I want reassign(...) to assign the reference to the new array.
Edit:
I do not want to return the new array. I want to reassign the incoming reference. Also, I want to learn why JS has this behavior that appears to be inconsistent (or I don't know enough about to understand how it is consistent).
All function calls in JavaScript are pass-by-value.
This may seem counter-intuitive, but it makes more sense if you consider that the arr parameter is itself a reference, so it's a reference, passed by value. Assigning newArr to arr will not modify the old reference, it will simply change what arr references within the scope of the function.
As Zeb Rawnsley pointed out in comments, you must return the new reference from the function and assign the outer variable to the return value:
var myArr = [1, 2, 3];
function reassign(arr) {
while(arr.length) {
arr.pop();
}
var newArr = [1,2,3,4,5];
return newArr;
}
myArr = reassign(myArr);
console.log(myArr.length);
Just pointing out that you don't need to pass in the old reference or empty it, it will automatically be garbage-collected once there are zero references left to the memory that myArr initially allocated:
var myArr = [1, 2, 3];
function reassign() {
return [1,2,3,4,5];
}
myArr = reassign();
console.log(myArr.length);
You can't assign the value of an array to another array, but you can add the elements from one array to another.
This should do the trick for you:
var myArr = [1, 2, 3];
function reassign(arr) {
while(arr.length) {
arr.pop();
}
var newArr = [1,2,3,4,5];
for(var num in newArr){
arr.push(num);
}
}
reassign(myArr);
console.log(myArr.length); //prints 5

JavaScript Array.concat and push workaround

It's always tricky to think Array.concat thing. Often, I just want to use mutable Array.push because I simply add extra-data on the immutable data. So, I usually do:
array[array.length] = newData;
I've asked a question related got some answers here: How to store data of a functional chain
const L = (a) => {
const m = a => (m.list ? m.list : m.list = [])
.push(a) && m;
//use `concat` and refactor needed instead of `push`
//that is not immutable
return m(a); // Object construction!
};
console.log(L);
console.log(L(2));
console.log(L(1)(2)(3))
some outputs:
{ [Function: m] list: [ 2 ] }
{ [Function: m] list: [ 1, 2, 3 ] }
I feel that push should be replaced with using concat, but still, push makes the code elegant simply because we don't want to prepare another object here.
Basically, I want to do:
arr1 = arr1.concat(arr2);
but, is there any way to write
arr1[arr1.length] = arr2;
which ends up with a nested array, and does not work.
You could assign a new array with a default array for not given m.list.
const L = (a) => {
const m = a => (m.list = (m.list || []).concat(a), m);
return m(a);
};
console.log(L.list);
console.log(L(2).list);
console.log(L(1)(2)(3).list);
You can use multiple parameters in Array.push so:
var a = [];
a.push(3, 4) // a is now [3, 4]
Combined with the ES6 spread syntax:
var a = [1, 2];
var b = [3, 4]
a.push(...b); // a is now [1, 2, 3, 4]
arr1[arr1.length] represents a single value, the value at index arr1.length.
Imagine this array
[ 1 , 2 , 3 , 4 ] // arr of length 4
^0 ^1 ^2 ^3 // indexes
If we say arr1[arr1.length] = someThing
We ask javascript to put something right here, and only here, at index 4:
[ 1 , 2 , 3 , 4 , ] // arr of length 4
^0 ^1 ^2 ^3 ^4 // add someThing in index 4
So, if we want to add something strictly with arr1[arr1.length], then we need to keep doing that for each index. for each meaning any kind of loop. E.G:
// Not recommended to use
var arr1 = [1,2,3];
var arr2 = [3,4,5];
while (arr2.length){
arr1[arr1.length] = arr2.shift();
}
console.log(arr1); // [1,2,3,3,4,5]
console.log(arr2); // []
But, as you can see, this method, or any similar one, even if optimized, is not the right approach. You need a concatenation.
Since you mention a functional one, which returns the resulting array, you can simply replace the initial array and make use of spread operator:
var arr1 = [1,2,3];
var arr2 = [3,4,5];
console.log(arr1 = [...arr1,...arr2]); // [1,2,3,3,4,5]

How do I replace an array element without modifying the original array and creating copy?

I'm trying to create a pure function that receives an array as parameter and now I want to replace an element at a given index without modifying the provided array argument.
Basically I'm looking for something like this:
export const muFunc = (arr) => {
return arr.replaceElementAt(1, 'myNewValue'); // This doesnt modify arr at all
}
How can I do that?
Simply copy the array. A simple way to do that is slice:
export const muFunc = (arr) => {
var newArray = arr.slice();
newArray[1] = 'myNewValue';
return newArray;
};
From a comment on the question:
As the topic says - I'm trying to find out if it's possible without creating a copy of the array
No it's not possible — well, not reasonably. You have to either modify the original, or make a copy.
You could create proxy object that just returns a different value for the "1" property, but that seems unnecessarily complicated.
You could take advantage of Object.assign and do something like:
const arr = [1, 2, 3, 4, 5];
const updatedArr = Object.assign([], arr, {1: 'myNewValue'});
console.log(arr); // [1, 2, 3, 4, 5]
console.log(updatedArr); // [1, "myNewValue", 3, 4, 5]
You can use map function to achieve this
var arr = [1,2,3,4,5,6,7,89];
arr.map(function (rm) {
if (rm == 2) {
return 3
} else {
return rm
}
})
Try this :
function replace(l) {
return l.splice("new value",1);
};
var x = replace(arr);

Categories

Resources