Я сталкиваюсь с чем-то, что я не понимаю с массивом. Действительно, я создал массив, который я заполнил пустыми подмассивами, чтобы получить 2D матрицу. Но когда я манипулирую массивом, он не ведет себя так, как я ожидал.

var arr = new Array(5);
arr.fill([]);
arr[2].push("third rank item");
console.log(arr);

//[ [ 'third rank item' ],
//  [ 'third rank item' ],
//  [ 'third rank item' ],
//  [ 'third rank item' ],
//  [ 'third rank item' ] ]

Каждый свет по этому вопросу будет приветствоваться

5
kevin ternet 13 Дек 2016 в 16:09

7 ответов

Лучший ответ

Это та же самая старая проблема с массивами (и объектами в целом), являющимися ссылками, а не значениями.

В частности, когда вы делаете arr.fill([]), вы берете этот единственный пустой массив и используете его для заполнения родительского.

Это как сказать:

var arr = new Array(5);
arr[0] = arr[1] = arr[2] = arr[3] = arr[4] = [];

Все они ссылаются на один и тот же массив! Поэтому, когда вы затем продолжаете модифицировать один из них, похоже, что они все модифицированы (но на самом деле это все тот же)

К сожалению, нет простого способа присвоить пустой массив каждому. Вы могли бы сделать что-то вроде:

Array.apply(null, Array(5)).map(function() {return [];});

По сути, создайте (инициализированный) пустой массив длиной 5 и сопоставьте каждое (пустое) значение с новым [].

РЕДАКТИРОВАТЬ: Кажется, я застрял в старые времена. Согласно комментарию @ torazaburo, вы можете использовать Array.from вместо Array.apply(null, Array(5)).map, например так:

Array.from( new Array(5), function() { return []; } );
8
Niet the Dark Absol 13 Дек 2016 в 13:16

В ES6 я рекомендую этот метод для создания 2-х или многомерных массивов:

// create an M x N dimension grid and fill it with 0's
const myArray = [...Array(M)].map(r => [...Array(N)].map(r => 0));
0
Nino Filiu 23 Мар 2019 в 01:28

Как вы можете заметить, используя array.fill, вы заполняете массив ссылкой на тот же массив,

Если вы хотите создать экземпляр каждого индекса массива в пустом массиве, то обычный цикл while сделает:

var arr = [];
var n = 5
while(n--)
  arr[n] = []

arr[2].push("third rank item");
console.log(arr);

Вариант 2:
если у вас есть доступный пакет lodash, вы также можете использовать _.map, так как он специально предназначен для обхода разреженного массива (нативная карта пропускает неинициализированные значения)

var arr =_.map(new Array(5), (x => []))

arr[2].push("third rank item");
console.log(arr)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
1
maioman 13 Дек 2016 в 13:24

Попробуйте это, это быстрое решение для вас в одной строке.

var arr = new Array(5);
arr = Array.from(arr, x => []);
arr[2].push("third rank item");
console.log(arr);
0
vijay 31 Дек 2016 в 06:34

Это происходит из-за ссылки. Массив - это тип объекта, и объект работает со своими ссылками, когда вы заполняете свой массив [] or new Array(), заполняете только заполняемые прогоны и помещаете один и тот же массив во все индексы, поэтому при обновлении подмассива все обновляются.

Решение:

let arr = new Array(5).fill(0).map(ele => ele = []); arr[2].push("something");

Или

let arr = Array.of([], [], [], []); arr[2].push("something");

Результат: как и ожидалось, обновляется только 2 индекса из arr.

1
Umair Ahmed 20 Янв 2017 в 12:40

Вы можете попробовать это,

var arr = new Array(5);
var i = 0;
while (i < arr.length)
  arr.fill([], i++);
arr[2].push("third rank item");
console.log(arr);
0
vijay 31 Дек 2016 в 06:35

Одиннадцатая строка документа ECMA Array.prototype.fill явно дает причину для тайны.

Повторите, пока к <финал

Пусть Pk будет ToString (k).

Пусть setStatus будет Set (O, Pk, значение, true).

ReturnIfAbrupt ( setStatus ) .

Увеличьте k на 1.

Здесь «значение» - это просто полученная ссылка. И они устанавливают это как свойство для массива напрямую. Это означает, что все заполненные массивы являются просто ссылкой на один объект массива.

2
Rajaprabhu Aravindasamy 13 Дек 2016 в 13:26