troadecnicolas
troadecnicolas

Reputation: 23

javascript : .filter updating object in original array?

I'm creating a little game for school and I'm creating a board of 100 tiles, that are JS objects. Some of these tiles are blocked (it's one of their properties), randomly. Player can't go in it.

I need to put 4 weapons on these tiles, randomly too.

So I've created an array of tiles unblocked, and then I put my weapon one by one on these free tiles. To be sure that there is not already a weapon on one of these tiles, I use the .filter() to create a new array of tiles unblocked and empty (with no weapons).

The thing is, when I update the property of a tile in my new array (issued of .filter, to be sure that there won't be two weapons on the same spot), it updates the properties of the tile in the parent array, and I don't undestand that. I've read that .filter() method don't update parent array, but here it does ? Maybe it's because it's a JS object ?

Thanks for the clarification !

let tilesUnblocked = []
//here function creating tiles and pushing unblocked tiles in the array above
for (i = 0; i < weapons.length; i++) {
    let tilesUnblockedAndEmpty = tilesUnblocked.filter(tile => tile.weapon === false) 
    let randomInt = Math.round(tilesUnblockedAndEmpty.length * Math.random())
    let weaponElt = weapons[i].HTMLElement
    weaponElt.css({ //some css })
    tilesUnblockedAndEmpty[randomInt].HTMLElement.append(weaponElt)
    tilesUnblockedAndEmpty[randomInt].weapon = true //updating array created from filter
    tilesUnblockedAndEmpty[randomInt].weaponType = weapons[i].name //updating array created from filter
}
console.log(tilesUnblocked) //I will find in this array the objects updated at the end of the foor loop, on the array issued of .filter method !

Upvotes: 2

Views: 2374

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075209

Maybe it's because it's a JS object ?

Yes. If you have this:

const original = [
    {example: 42},
    {example: 21}
];

You have something like this in memory:

               +−−−−−−−−−+
original−−−−−−>| (array) |
               +−−−−−−−−−+          +−−−−−−−−−−−−−+
               | 0       |−−−−−−−−−>|  (object)   |
               | 1       |−−+       +−−−−−−−−−−−−−+
               +−−−−−−−−−+  |       | example: 42 |
                            |       +−−−−−−−−−−−−−+
                            |       +−−−−−−−−−−−−−+
                            +−−−−−−>|  (object)   |
                                    +−−−−−−−−−−−−−+
                                    | example: 21 |
                                    +−−−−−−−−−−−−−+

If you then do this:

const filtered = original.filter(entry => entry.example !== 42);

You have:

               +−−−−−−−−−+
original−−−−−−>| (array) |
               +−−−−−−−−−+          +−−−−−−−−−−−−−+
               | 0       |−−−−−−−−−>|  (object)   |
               | 1       |−−+       +−−−−−−−−−−−−−+
               +−−−−−−−−−+  |       | example: 42 |
                            |       +−−−−−−−−−−−−−+
                            |       +−−−−−−−−−−−−−+
                            +−−−−−−>|  (object)   |
                            |       +−−−−−−−−−−−−−+
                            |       | example: 21 |
                            |       +−−−−−−−−−−−−−+
               +−−−−−−−−−+  |
filtered−−−−−−>| (array) |  |
               +−−−−−−−−−+  |
               | 0       |−−+
               +−−−−−−−−−+

As you can see, both arrays are pointing to the same object. If you change that object's state (by adding or changing a property, for instance), that change is visible through both arrays.

If you want the filtered array to have copies, you have to do that explicitly:

const filtered = original
    .filter(entry => entry.example !== 42)
    .map(entry => ({...entry});

That's just one way to copy an object, there are several more covered in the answers to this question.

Upvotes: 6

Mitch Lillie
Mitch Lillie

Reputation: 2407

Try making a copy of your objects before filtering:

let tilesUnblockedAndEmpty = tilesUnblocked.map(e => Object.assign({}, e)).filter(tile => tile.weapon === false)

That should work, depending on what the objects look like. JSON.stringify followed by JSON.parse is a hackier method that will handle more use cases.

Upvotes: 0

Related Questions