Understand the Hazards of Using Imperative Code (I don't understand how this works) [SOLVED]

What’s happening:

I don’t have to do anything in this challenge, but obviously, the problem is that the splice function changes the original array. Which is what happens here:

videoWindow.tabClose(2)

…and hence half the array is missing. But, here:

workWindow.tabClose(1).tabOpen()

…it works perfectly fine, I want to understand why and how?

Please someone help me understand this, it has been bugging for the past 3hrs.

The complete code:


// tabs is an array of titles of each site open within the window
var Window = function(tabs) {
  this.tabs = tabs; // we keep a record of the array inside the object
};

// When you join two windows into one window
Window.prototype.join = function (otherWindow) {
  this.tabs = this.tabs.concat(otherWindow.tabs);
  return this;
};

// When you open a new tab at the end
Window.prototype.tabOpen = function (tab) {
  this.tabs.push('new tab'); // let's open a new tab for now
  return this;
};

// When you close a tab
Window.prototype.tabClose = function (index) {
  var tabsBeforeIndex = this.tabs.splice(0, index); // get the tabs before the tab
  var tabsAfterIndex = this.tabs.splice(index); // get the tabs after the tab

  this.tabs = tabsBeforeIndex.concat(tabsAfterIndex); // join them together 
  return this;
 };

// Let's create three browser windows
var workWindow = new Window(['GMail', 'Inbox', 'Work mail', 'Docs', 'freeCodeCamp']); // Your mailbox, drive, and other work sites
var socialWindow = new Window(['FB', 'Gitter', 'Reddit', 'Twitter', 'Medium']); // Social sites
var videoWindow = new Window(['Netflix', 'YouTube', 'Vimeo', 'Vine']); //  Entertainment sites

// Now perform the tab opening, closing, and other operations
var finalTabs = socialWindow
                    .tabOpen() // Open a new tab for cat memes
                    .join(videoWindow.tabClose(2)) // Close third tab in video window, and join
                    .join(workWindow.tabClose(1).tabOpen());

alert(finalTabs.tabs);

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/functional-programming/understand-the-hazards-of-using-imperative-code/

try to identify the problem after analyzing this code snippet :

var months = [0, 1, 2, 3];

var index = 2; // for example
var removed = months.splice(index);

console.log(removed);
// > Array [2, 3]
console.log(months);
// > Array [0, 1]
console.log(months.concat(removed));
// > Array [0, 1, 2, 3]
1 Like

yes, but in the actual code they did arr.splice(0,index); first.
and also the element is successfully removed

nothing is removed in the closeTab function :
that’s what it should have been :

Window.prototype.tabClose = function (index) {
  var tabsBeforeIndex = this.tabs.splice(0, index);
  var tabsAfterIndex = this.tabs.splice(index+1); // get the tabs after the tab

  this.tabs = tabsBeforeIndex.concat(tabsAfterIndex); 
  return this;
 };

splice includes the element in the starting index , but excludes the one in the end index . read the document tation for splice

2 things:

  1. Well, thats not what my question was, but it’s fine I figured it out. (I will probably soon write what I figured out in my original question).
  2. Your code will not work as splice modifies the original tabs array, here is what appens.

original tabs array : ["tab1","tab2", "tab3", "tab4"]
when
var tabsBeforeIndex = tabs.splice(0,2);
executes,
splice removes ["tab1","tab2"] from tabs, hence
tabs is now => ["tab3","tab4"]
and splice returns the array of elements it removed so,
tabsBeforeIndex is now => ["tab1","tab2"]
and then when
var tabsAfterIndex = tabs.splice(2 + 1);
executes,
splice tries to look for index 3 element in tabs now, and fails to find it. and hence nothing is removed from tabs, so
tabs is still => ["tab3","tab4"]
and
tabsAfterIndex is now => [] (nothing, as splice didn't remove anything)
and now when
tabs = tabsBeforeIndex.concat(tabsAfterIndex);
executes,
what happens is
tabs = ["tab1","tab2"] + []
so
tabs is => ["tab1","tab2"]

3 Likes

it me 3 hrs to understand this one … and there are parts of it I am still iffy on…

I am a few hours into this exercise and still do not fully understand the solution, I will jump and try again in a week.

1 Like

Thank you very much for this, helped me solve this problem after an hour of picking my brain on it.

1 Like

This is the right answer that belongs to me;

// When you close a tab
Window.prototype.tabClose = function (index) {

  // Only change code below this line

  console.log("all: " + this.tabs);

  var tabsBeforeIndex = this.tabs.splice(0, index); // Get the tabs before the tab
  console.log("tabsBeforeIndex: " + tabsBeforeIndex);
  var tabsAfterIndex = this.tabs.splice(1); // Get the tabs after the tab
  console.log("tabsAfterIndex: " + tabsAfterIndex);
  this.tabs = tabsBeforeIndex.concat(tabsAfterIndex); // Join them together

  // Only change code above this line
  return this;
 };

I wrote with console.log(); code because you can see how I debug and find the answer. Hope it will be more helpful this way.

I’m a little confused on why the solutions are so complicated.
All I had to do was this and it passed the tests. Any reason why this wouldn’t work?

// When you close a tab
Window.prototype.tabClose = function (index) {

  // Only change code below this line
  this.tabs.splice(index, 1);
  // Only change code above this line

  return this;
 };

If the problem is with making the code “safer” would it be better to make a copy of the array with slice or spread operator, then modify the copy, then reassign this.tabs to the modified copy?

Like this:

// When you close a tab
Window.prototype.tabClose = function (index) {

  // Only change code below this line
  let copy = this.tabs.slice();
  copy.splice(index, 1);
  this.tabs = copy;
  // Only change code above this line

  return this;
 };

I am replying from a new account as I lost my old account

This exercise is not to make the code work, or to make it cleaner. It is only to understand the problems that may occur by using imperative code.

Your solution works, and is the right way of doing it. But that is not the point.

The point is that while making changes to variables, you as a programmer may make mistakes that leads to unexpected results in the code. Which is why it is advisable to almost always never change global variables directly. My comment explains shows an in-depth explanation of how variables are behaving in this example, and you can realize how this can lead to further confusion due to changing global variables directly.

The best practice is therefore not to do this. Instead (as much as possible, although there are exceptions) use functional code.

2 Likes

Thank you! This helped a lot :slight_smile: