1

My exercise is creating a function that takes two parameters: a multi-dimensional array, and a value. I have to return a new array in which all the sub-array that contain the value is removed. My idea is using nesting for loop to search for the value in every sub-array. If the matching is found, remove the sub-array and end the inner loop. But the code was not runing well and I found a problem as below when debugging:

//Assingment is from FreeCodeCamp
function filteredArray(arr, elem) {
  let newArr = [];
  //my solution start here
  newArr = arr;
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr[i].length; j++) {
      //debugging
      console.log( "i", i);
      console.log( 'j', j);
      console.log(arr);
      console.log(arr[i]);
      console.log(arr[i][j]);
      //end debugging
      if (arr[i][j] == elem) {
        newArr.shift();   //remove the sub-array
       j = arr[i].length; //end the inner loop
      } 
      console.log('newArr',newArr);
    }
  }
  //my solution end here
  return newArr;
}

// change code here to test different cases:
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));

The result is:

"i" 0
"j" 0
[[3,2,3],[1,6,3],[3,13,26],[19,3,9]]
[3,2,3]
3
"newArr" [[1,6,3],[3,13,26],[19,3,9]]
"i" 1
"j" 0
[[1,6,3],[3,13,26],[19,3,9]]
[3,13,26]
3
"newArr" [[3,13,26],[19,3,9]]
[[3,13,26],[19,3,9]]

The code was run well in the first i = 0 loop. But in the next loop, i = 1, the parameter array is changed. I supposed it to be the input array of the function but turn out, the first sub-array is removed even though I didn't make any change in that array. Can anyone help me explain this?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • When you assign `arr` to `newArr`, you make both variables reference the exact same array; `newArr = arr` does **not** make a copy. – Pointy Nov 25 '19 at 16:58
  • `newArr` and `arr` are references to the same array. Make a copy if you don't want to modify the original array. – Barmar Nov 25 '19 at 16:58
  • `newArr.shift();` this always removes the first element. Also, changing an iterable mid-iteration results in undesired behavior, unless you manually adjust for it. Better just build the result up, instead of removing, that saves you trouble adjusting the indices. – ASDFGerte Nov 25 '19 at 17:00
  • 1
    what should happen with empty arrays after filtering? – Nina Scholz Nov 25 '19 at 17:51

4 Answers4

0

To make an array copy, do

newArr = arr.slice(0);
TKoL
  • 13,158
  • 3
  • 39
  • 73
0

As an alternative solution to your problem (TKol answered it), consider not using for loops if you don't need the indices

function filteredArray(arr, elem) {
  return arr.filter(row => {
    return !row.includes(elem)
  })
}
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
console.log(filteredArray([[1, 2, 4], [1, 6, 2], [3, 13, 26], [19, 3, 9]], 3));
grodzi
  • 5,633
  • 1
  • 15
  • 15
0

By saying newArr = arr;, you are keeping the same reference to original array so any change on one will affect the other. There are ways to copy arrays, one is the spreed operator([...arr]) to make a shallow copy, which is fine for your specific example. Another thing is the shift() method will always return or remove from the array the first element. You can use the array splice() method to do index based operations on array, like adding, removing or replacing elements.

function filteredArray([...arr], elem) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr[i].length; j++) {
      if (arr[i][j] == elem) {
        arr[i].splice(j, 1);
      } 
    }
  }
  return arr;
}

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));

You may also shorten the step with built in map() and filter() methods:

filteredArray = (arr, elem) => arr.map( a => a.filter(value => value != elem));

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));

EDIT :

The above answer assumed you wanted to remove the element only. Provided you want to remove the sub array containing the element:

function filteredArray([...arr], elem) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = 0; j < arr[i].length; j++) {
      if (arr[i][j] == elem) {
        arr.splice(i, 1);
        i--;    
        break;
      } 
    }
  }
  return arr;
}

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [13, 26], [19, 3, 9]], 3));

Or in short:

filteredArray = (arr, elem) => arr.filter( a => !a.includes(elem));

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
console.log(filteredArray([[3, 2, 3], [1, 6, 3], [13, 26], [19, 3, 9]], 3));
Addis
  • 2,480
  • 2
  • 13
  • 21
0

You could take a recursive approach and filter nested arrays by callign the function again.

function filteredArray(array, element) {
    var result = [],
        i, l, item;

    for (i = 0, l = array.length; i < l; i++) {
        item = array[i];
        if (item === element) continue;
        result.push(Array.isArray(item) ? filteredArray(item, element) : item);
    }
    return result;
}

console.log(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392