1

I am trying to create an array which has a defined length and fill it with empty arrays. I've tried using all possibilities given by @stpoa's answer here but my array does not behave correctly.

Given the code (I simplified it for the sake of example):

const tasksArray = Array(3).fill([])
const tasksArray2 = [[], [], []]
const tasks = ['task1', 'task2']

const fillWithData = (array) => {
  tasks.forEach(task => {
    array[0].push(task)
  })
}

Gives me an incorrect output for tasksArray and a obviously a correct one for tasksArray2 which is hardcoded

fillWithData(tasksArray) // [['task1', 'task2'], ['task1', 'task2'], ['task1', 'task2']] => not OK, duplicates values!
fillWithData(tasksArray2) // [['task1', 'task2'], [], []] => that's OK
Emma
  • 27,428
  • 11
  • 44
  • 69
Dandy
  • 929
  • 2
  • 14
  • 23
  • 1
    what is the expected output? – Mulan May 11 '19 at 20:12
  • a second one, maybe I'll mark it more visibly ;) – Dandy May 11 '19 at 20:13
  • 1
    Possible duplicate of [Strange behavior of an array filled by Array.prototype.fill()](https://stackoverflow.com/questions/41121982/strange-behavior-of-an-array-filled-by-array-prototype-fill) and [Unexpected behavior using Array Map on an Array Initialized with Array Fill](https://stackoverflow.com/questions/27613126) and [Create 2D array using new Array().fill(0) bug?](https://stackoverflow.com/questions/38760956) – adiga May 11 '19 at 20:18

3 Answers3

5

In taskArray, the [] you are using is passed as a reference, and the elements in taskArray all reference the same array.

In taskArray2, you have three separate empty arrays, [], each with their own reference. Therefore you do not get duplicated values.

If you wish to create an array of empty arrays programmatically, use Array.from -

const fillEmptyArrays = (count) =>
  Array.from(Array(count), _ => [])

const tasks =
  fillEmptyArrays(3)

console.log(tasks)
// [ [], [], [] ]

And please don't include type names like Array in your variable names tasksArray; just name it tasks. JavaScript is a dynamically-typed language and this kind of thinking hurts you in the long run.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • cool, I really love that. Could you only explain how `_` can be understood? – Dandy May 11 '19 at 20:23
  • Dandy, in this case `_` is a convention that signals the function's parameter is not used in the function body and therefore doesn't need a name; the function always returns `[]`. You could replace `_` with any valid variable name and the program will still work. See [Array.from](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from)'s second argument for more details. – Mulan May 11 '19 at 20:25
  • _don't include type names like Array in your variable names ... this kind of thinking hurts you in the long run_ - What makes you think that? –  May 11 '19 at 20:44
  • @bob JavaScript bindings can have a value of any type, and in the case of `let` the type could change over time. Polymorphic functions... Dynamic function calls... there's nothing enforcing that `someArray` is an array, and in the dynamic cases when it's not an array, it's really hard on your brain to see "Array" in the name and then mentally treat it as something else. – Mulan May 16 '19 at 03:12
4

You need to get independent object references inside of the array, instead of having literally the constant value.

By taking Array.from with an object with a length property and the build in mapping function, you could get an array of independent arrays.

const tasksArray = Array.from({ length: 3 }, _ => [])
const tasks = ['task1', 'task2']

const fillWithData = (array) => {
  tasks.forEach(task => {
    array[0].push(task)
  })
};

fillWithData(tasksArray);
console.log(tasksArray);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • 1
    just use `Array(3)` - `{ length: 3 }` is a voodoo and using this behavior directly keeps the programmer's mind at a level that is less effective. – Mulan May 11 '19 at 20:23
  • @user633183, i like voodoo. – Nina Scholz May 11 '19 at 20:27
  • @user633183 I would disagree. Passing a super basic array-like should be not more voodoo than using a unary `+` or `!!` or other small tricks. I am fairly confident that at this point it's gaining traction to be an idiom like those, if it's not reached that point already. – VLAZ May 11 '19 at 20:28
  • Right, but as professional developers we also appreciate code that is straightforward, maintainable, and easy on our minds. – Mulan May 11 '19 at 20:31
  • VLAZ, I strongly discourage `+` and `!!`. ["Tricks are for kids!"](https://en.wikipedia.org/wiki/Trix_(cereal)). Real programs demonstrate clear intentions. Use `Number.parseInt` and `Boolean` even if it's a few more keystrokes. – Mulan May 11 '19 at 20:32
2

fill puts the value you pass it at each index of the array.

So tasksArray has three references to the same array, while tasksArray2 has a reference to each of three different arrays.

If you want to put three different arrays in there, then you need to explicitly create three arrays.

You could approach it with a counter:

const tasksArray2 = [];
let count = 3;
while (count--) {
    tasksArray2.push([]);
}
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Thanks, now I understand that it was due to `fill()`. However, the `Array.from()` solution looks much cleaner. – Dandy May 11 '19 at 20:24