1

I'm trying to create an array of 120 objects (hex's for a map) and then add info to each one.

When I use map the id is supposed to be the index of the current object and I can console log the correct, current index as expected but for some reason EVERY object has an id of 119. I've looked at some of the other map examples on here and with Mozilla and I'm not understanding where I'm going wrong.

export class MapComponent implements OnInit {

  arrayOfObjects = Array(120).fill({})
  hexTiles = this.arrayOfObjects.map( (hex, index) => this.createHex(hex, index))

  constructor() { }

  ngOnInit() {
  }

  createHex(hex, index){
    console.log('Current Index', index)
    hex['id'] = index
    hex['x-coordinate'] = null
    hex['y-coordinate'] = null
    hex['mapped'] = false
    hex['terrain'] = ''
    hex['details'] = ''

    return hex
  }

}

UPDATE I have tried a suggested solution but am now getting an array of 120 empty objects. Here is the updated code:

HTML: This displays nothing.

<div>
  <div *ngFor="let hex of hexTiles; let i = index">
    <pre>
      {{hex}}
    </pre>
  </div>
</div>

TS: This will console log "Hexs (120) [empty × 120]"

export class MapComponent implements OnInit {

  hexTiles = [...Array(120)].map((_, i) => this.createHex({}, i));


  constructor() { }

  ngOnInit() {
    console.log('Hexs', this.hexTiles);
  }

  createHex(hex, index) {
    hex['id'] = index;
    hex['x-coordinate'] = null;
    hex['y-coordinate'] = null;
    hex['mapped'] = false;
    hex['terrain'] = '';
    hex['details'] = '';
    return hex;
  }

}
2
  • You can simplify this.arrayOfObjects.map( (hex, index) => this.createHex(hex, index)) to just this.arrayOfObjects.map(this.createHex), not sure if this will fix your plroblem. Commented Feb 8, 2019 at 19:43
  • 1
    @gv0000, I updated my answer to fix the [...Array(120)] problem, sorry for that. hexTiles = Array.from({ length: 120 }).map((_, i) => this.createHex({}, i)); should work, but your methods works too, just don't use the array content in the map Commented Feb 8, 2019 at 20:43

1 Answer 1

4

Change

arrayOfObjects = Array(120).fill({})
hexTiles = this.arrayOfObjects.map( (hex, index) => this.createHex(hex, index))

for this

hexTiles = [...Array(120)].map((_, i) => this.createHex({}, i));

Here is a working snippet:

function createHex(hex, index) {
    hex['id'] = index;
    hex['x-coordinate'] = null;
    hex['y-coordinate'] = null;
    hex['mapped'] = false;
    hex['terrain'] = '';
    hex['details'] = '';
    return hex;
}

const hexTiles = [...Array(120)].map((_, i) => createHex({}, i));

console.log(hexTiles);

Here is an explanation of your problem.

function createHex(hex, index) {
    hex['id'] = index;
    hex['x-coordinate'] = null;
    hex['y-coordinate'] = null;
    hex['mapped'] = false;
    hex['terrain'] = '';
    hex['details'] = '';
    return hex;
}
const obj = { };
const arrayOfObjects = Array(120).fill(obj);
const hexTilesBad = arrayOfObjects.map((hex, index) => createHex(hex, index));
console.log(obj)

When filling the array with {}, you are actually filling it with copies of the reference to the same object. This is because, by default, in Javascript/Typescript, when you assign an object, you assign its reference.

In the above example, instead of filling the array with {}, we fill it with obj, which is just {}. When we console.log(obj) at the end, we see that it indeed has the value of the last iteration.

Its reference has been copied 120 times in the array, and those references got passed to the map, then to your createHex function. So ultimately, you are just modifying 120 times the same object.

However, when you pass {} directly to createHex from the map arrow function, a new object is created for each iteration in the function scope, so nothing is shared.

EDIT: [...Array(120)] does not seem to work in TypeScript. As pointed out by @AJT_82 in the comments below, this is due to a TypeScript bug which causes this spread operator trick to incorrectly translate to JavaScript.

To circumvent the problem, you can try the following:

Array.from({ length: 120 })

or stick with your method

Array(120).fill({})

What is important is to not use the content of this array, since they are just references to the same object.

Sign up to request clarification or add additional context in comments.

8 Comments

Why does this work, and not what OP is doing in question? Explanations are always good :)
Yes absolutely, I am updating the answer with another example
This is weird, as your function is setting the property id and to reading it. Do you have more detail?
It should not be related to lifecycle methods as you are not depending on any data from your view or angular and everything is run at construction time. Would you mind sharing a Stackblitz?
@jo_va I got real interested why [...Array(120)] didn't work, apparently this is a TS bug... from ages ago. Weird that it hasn't been fixed?! If you want to expand your answer by saying it's a bug, here's the link as reference: github.com/Microsoft/TypeScript/issues/8856 :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.