Functions on copies of an array modify the original array (No Repeats Please)

Functions on copies of an array modify the original array (No Repeats Please)
0.0 0

#1

It seems that using functions such as .push, .pop, .shift, .unshift and .splice on a copy of an array will modify the original as well and I don’t understand why. This tends to thwart efforts to move data around without changing the original input around. In particular, my solution to “No repeats please” did not work until I added lines 28 and 29 to return the 2 element array to its original order.

function permAlone(str) {

if (str.length === 0) {
return 0;
} else if (str.length === 1 ) {
return 1;
} else if (str.length === 2) {
if (str[0] == str[1]) {
return 0;
} else {
return 2;
}
}

var strArr = str.split("");

function permute(stringArray) {

if (stringArray.length == 2) {
  
  
  var tempStrArr = stringArray;
  var stringPermutationArray = [tempStrArr.join("")];
  var tempChar = tempStrArr.shift();
  tempStrArr.push(tempChar);
  stringPermutationArray.push(tempStrArr.join(""));
  tempChar = tempStrArr.shift();                                  // line 28
  tempStrArr.push(tempChar);                                     // line 29
  return stringPermutationArray;
  
} else {
  
  var altTempChar = "";
  var altStringPermutationArray = [];
  var altTempStrArr = stringArray;
  for (var k=0; k < altTempStrArr.length; k++) {
    altTempChar = altTempStrArr.splice(k,1)[0];
    var tempPermArr = permute(altTempStrArr);
    for (var m = 0; m < tempPermArr.length; m++) {
      tempPermArr[m] = altTempChar + tempPermArr[m];
    }
    altStringPermutationArray = altStringPermutationArray.concat(tempPermArr);
    altTempStrArr.splice(k, 0, altTempChar);
  }
  return altStringPermutationArray;
}

}

var strPermArr = permute(strArr);

var permCount = strPermArr.length;

for (var n=0; n < strPermArr.length; n++) {
if (/([A-Za-z])\1{1,}/.test(strPermArr[n])) {
permCount–;
}
}

return permCount;

}


#2

In JavaScript (as well as Ruby, Python, and others), you need to think of the variable name as one thing existing in memroy, and the value you assign to it as another thing. Keep them separate in your mind. When you use the assignment operator, you’re creating a link between them. Anything that you do to the variable name ends up being done to the value it’s connected to. An example:

var foo = [0,1,2,3];
foo.push(4); //Exactly the same as [0,1,2,3].push(4)

But we can sever this connection, comme ça:

foo = [5,6,7];  //Same variable name, new array!

Of course, this is no surprise. We can do this all day and all night with lots of data types:

var bar = "baz";
bar = "bam";
bar = 197;
bar = 9000;

Each time we change the value, we are severing the connection our variable bar had reattaching it to a completely different value. Remember: the variable and its value are two different things in memory linked by the assignment operator.

Let’s think about objects for a second.

var obj1 = {name: "Henry", hungry: true};
obj1.hungry = false;
console.log(obj1); //{name: "Henry", hungry: false}

Unlike the previous five changes and exactly like the first two, instead of severing the connection between the variable and the value, we simply changed the value. Now, for the love of Crockford, WHY DOES THIS MATTER?

It’s no mystery what happens when you pass a value into a function as a parameter.

function blah(a, b) {
   console.log(a); //10
   console.log(b); //"q"
}

blah(10, "q");

a is assigned the number 10 and b is assigned the string “q”. No surprises, no shock, no duh. But what’s happening when we pass a variable?

var string1 = "burger";
function blah_blah(a) {
  console.log(a); // "burger"
}
blah(string1);

Again, this is predictable. Boring, even. string1 is “burger”, and so is a. Except that what you can’t see is the fact that the string “burger” that is linked to a is the very same string in memory that is linked to string1! It isn’t copied, and we’re not passing a reference. a is created with a link to the same value in memory that links to string1. If we change the value of a in the function, we are breaking that link and assigning a new one.

Now, finally, on to your question. What about arrays (and objects, for that matter)?

function revenge_of_blah(obj1, obj2, arr1, arr2) {
  obj1.name = "Jane";
  obj2 = {name: "Linguo"};
  arr1.join("");
  arr2.push("-_-");
}

var obj1 = {name: "Henry", hungry: true};
var obj2 = {name: "Cleo", hungry: false};
var arr1 = [0,1,2,3];
var arr2 = ["a", "b", "c"];

revenge_of_blah(obj1, obj2, arr1, arr2);

Pause for a moment and think about this. What is going to be the value of the variables after the function call?

Before giving the answer and the explanation, I’m going for a shower because 1) I smell like a wet-dog-gym-sock burrito, and 2) I already wrote a long thing when I promised myself I wouldn’t (but dammit all, your question was a really good one).


#3

Perhaps:

obj1 = {name: “Jane”, hungry true}
obj2 = {name: “Linguo”}
arr1 = [0, 1, 2, 3]
arr2 = {“a”, “b”, “c”, “-_-”}

It is ambiguous, however, if the obj2 name outside the function is different from the obj2 name inside the function. If it is, then they no longer point at the same value after obj2 = {name: “Linguo”}; and obj2 outside the function is still {name: “Cleo”, hungry: false}.

Alright, here’s an important practical question. Suppose you want to manipulate data from a database without changing the database. How would you do that? Naive attempts to copy the array would fail. You simply end up with two variable names pointing at the same array, if I’m understanding you correctly.


#4

Yes! That is a fantastic rationale! In fact, none of the variables inside the function are the same variables that are outside the function, despite having the same name and being passed their doppleganger’s values. When the function finishes and its internal scope closes, extinguishing all the little variables within, obj2 will not have changed in value for exactly the reason you gave - both variables pointed to the same value, and by reassigning it a new value, we severed that connection for obj2 and reestablished a new one.

Notice what happens to a string, or a number, or a boolean:

var str = "chapstick";
var bool = true;
var num = 2;

function change_stuff_around(a, b, c) {
  a = "A new string";
  b = false;
  c = 8134978;
}

change_stuff_around(str, bool, num);

console.log(str); // "chapstick"
console.log(bool); // true
console.log(num); //2

Primitive types - booleans, numbers, strings - are immutable. Unlike an array or object, they cannot be changed. Any time you change the value of a variable which points to a primitive, you are severing its connection and reestablishing a new one.

The database is a completely separate program. Retrieving data from or writing data to a database is like working with a RESTful API, the type of which we use to get weather or Twitch user data in JSON format over HTTP. When we request data from a database, it has its own internal system by which it finds and presents the data. It’s a layer of much needed abstraction and at no point will you ever have direct access to the data from JavaScript any more than you’ll have access to Twitch’s user data. You will only be served a copy.

I hope this has helped. It’s a decently complicated topic that trips up a lot of JS developers (you’ll usually get an explanation referring to pass-by-value versus pass-by-reference, which is a totally forgivable misunderstanding).


#5

Now that the why has been explained, you could always use array.slice().pop() instead of array.pop().
Slice will return a new array and leave the original intact. :grinning::+1:


#6

Duh!

slaps forehead