11

I have an array of colors that I want the option to reverse. I have a toggle function that basically colors elements based on the array. If I throw a reverse variable then it reverses, but it reverses the global variable instead of the local variable.

var inc_colors = ['#000','#333','#888']; //global inc_colors

function toggleLegendColors(reverse){
  var reverse = reverse || false;
  var colors = inc_colors; //local colors
  if(reverse) colors.reverse(); //reverses inc_colors array as well as colors
  ...
}

How can I get the reversed global array without changing the global array?

bozdoz
  • 12,550
  • 7
  • 67
  • 96

5 Answers5

14

Since nobody has really explained why you were having a problem, I'll add that to the mix.

When you assign an array or an object in javascript to a variable, it assigns a reference to that array/object. It does not make a copy of the array/object. So, then you would have two variables that both point at the same array/object and modifying either one will affect the other (since they both point to the same underlying piece of data).

So, when you had this:

var inc_colors = ['#000','#333','#888']; //global inc_colors
var colors = inc_colors; //local colors

All you have now is two variables that both point to the exact same piece of data. Modify either one and the same result will show via the other variable because they point to the same underlying data.

If you want to make a copy, then have to explicitly make a copy (javascript doesn't do it for you automatically). For an array, the simplest way to make a shallow copy is like this:

var newColors = Array.prototype.slice.call(inc_colors);

So, in your exact code, you could apply that copy like this:

var inc_colors = ['#000','#333','#888']; //global inc_colors

function toggleLegendColors(reverse){
  var reverse = reverse || false;
  var colors = Array.prototype.slice.call(inc_colors);  //local copy of the colors array
  if(reverse) colors.reverse(); //reverses inc_colors array as well as colors
  ...
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Why does that happen to arrays and not other variable types? – bozdoz Jan 16 '13 at 22:52
  • 1
    @bozdoz - it happens to arrays and objects. Strings are immutable so they can never be changed (a new string object is created whenever the string is modified) so you can't have a constant reference to a string that gets modified. The other variable types (boolean, number, etc...) are just simple types and when they are assigned they are copied. As to why it's this way, that would need to be explained by the designers of the language. – jfriend00 Jan 16 '13 at 22:56
  • Why does it matter why they were having the problem? – daniella Feb 16 '17 at 17:40
  • @daniella - Understanding how assignment of objects works in Javascript is fundamental to writing good code. – jfriend00 Feb 16 '17 at 21:54
14

You can do this by using the es6 spread operator now too:

let colors = [ ...inc_colors ].reverse()
Ian
  • 163
  • 2
  • 7
11

Just make a copy of the array using Array.slice (safe way):

var colors = Array.prototype.slice.call(inc_colors);
VisioN
  • 143,310
  • 32
  • 282
  • 281
  • what I meant is: are you sure you don't mean `reverse.call()`? :) – glomad Jan 16 '13 at 19:51
  • 4
    `var colors = inc_colors.slice();` also will work, because it's an array – Gabriel Gartz Jan 16 '13 at 19:51
  • 2
    @ithcy: You want to copy the array before you reverse it, so that you are reversing the copy. – gen_Eric Jan 16 '13 at 19:52
  • @RocketHazmat You are absolutely right! I was thinking in reverse :) – glomad Jan 16 '13 at 19:55
  • I forget, is there a reason to do one or the other? (`Array.prototype.slice.call` vs. `array.call()`)? Isn't the prototype overrideable or something? – Ian Jan 16 '13 at 19:55
  • 1
    @Ian It is just a safe way. In case if `inc_colors` is not an array. – VisioN Jan 16 '13 at 19:56
  • 6
    @Ian: You normally use `Array.prototype.slice.call` when the "array" isn't really an array, like a `NodeList` or `arguments`. – gen_Eric Jan 16 '13 at 19:56
  • @VisioN That's what I thought, and when I tested, I used `null` and `undefined` for some reason, which didn't work, so I thought that wasn't the reason. But it works for a string (splits it by char), number (empty array), boolean (empty array), and object (empty array) at least. – Ian Jan 16 '13 at 20:00
  • It makes sense to me to use it on an Object, because using `.call` (or an actual Object method) attempts to call a property first (so could be bad if you ever had `{"hasOwnProperty": "asdf"}` for whatever reason) and then looks up the prototype chain. But yeah, it makes sense for "arrays" – Ian Jan 16 '13 at 20:05
11

clean simple way that you may consider , but involves creating a new instance of the array is

var arr_reverse=arr.slice(0).reverse();
yeahdixon
  • 6,647
  • 1
  • 41
  • 43
1

Simplistic solution:

var inc_colors = ['#000','#333','#888']; //global inc_colors

function toggleLegendColors(reverse) {
  var colors = (inc_colors instanceof Array) ? inc_colors : [];
  colors = (!reverse) ? inc_colors.slice() : inc_colors.slice().reverse();
  // ...
}
Gabriel Gartz
  • 2,840
  • 22
  • 24
  • Do you need the `var` for `inc_colors` inside the function? If you take it out, it will keep `inc_colors` global, in case it was never defined. Not too important, just didn't know if there was a reason for the `var` :) – Ian Jan 16 '13 at 20:17